Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.db / org.gvsig.fmap.dal.db.jdbc / src / main / java / org / gvsig / fmap / dal / store / jdbc2 / spi / operations / FetchFeatureTypeOperation.java @ 44951

History | View | Annotate | Download (16.7 KB)

1
package org.gvsig.fmap.dal.store.jdbc2.spi.operations;
2

    
3
import java.sql.Connection;
4
import java.sql.DatabaseMetaData;
5
import java.sql.ResultSet;
6
import java.sql.ResultSetMetaData;
7
import java.sql.SQLException;
8
import java.sql.Statement;
9
import java.util.ArrayList;
10
import java.util.List;
11
import org.apache.commons.collections.CollectionUtils;
12
import org.apache.commons.lang3.StringUtils;
13
import org.cresques.cts.IProjection;
14
import org.gvsig.expressionevaluator.ExpressionBuilder;
15
import org.gvsig.fmap.dal.DataTypes;
16
import org.gvsig.fmap.dal.exception.DataException;
17
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
18
import org.gvsig.fmap.dal.feature.EditableFeatureType;
19
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
20
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
21
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
22
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
23
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
24
import org.gvsig.fmap.geom.Geometry;
25
import org.gvsig.fmap.geom.GeometryLocator;
26
import org.gvsig.fmap.geom.type.GeometryType;
27
import static org.gvsig.fmap.dal.store.jdbc2.spi.operations.AbstractConnectionOperation.LOGGER;
28
import org.gvsig.tools.dataTypes.DataType;
29

    
30
@SuppressWarnings("UseSpecificCatch")
31
public class FetchFeatureTypeOperation extends AbstractConnectionOperation {
32
    private final EditableFeatureType featureType;
33
    protected final TableReference table;
34
    private final List<String> primaryKeys;
35
    private final String defaultGeometryColumn;
36
    private final IProjection crs;
37

    
38
    public FetchFeatureTypeOperation(
39
            JDBCHelper helper
40
        ) {
41
        this(helper, null, null, null, null, null);
42
    }
43
    
44
    public FetchFeatureTypeOperation(
45
            JDBCHelper helper,
46
            EditableFeatureType featureType,
47
            String defaultGeometryColumn,
48
            IProjection crs
49
        ) {
50
        this(helper, featureType, null, null, defaultGeometryColumn, crs);
51
    }
52

    
53
    public FetchFeatureTypeOperation(
54
            JDBCHelper helper,
55
            EditableFeatureType featureType,
56
            TableReference table,
57
            List<String> primaryKeys,
58
            String defaultGeometryColumn,
59
            IProjection crs
60
        ) {
61
        super(helper);
62
        this.featureType = featureType;
63
        this.table = table;
64
        this.primaryKeys = primaryKeys;
65
        this.defaultGeometryColumn = defaultGeometryColumn;
66
        this.crs = crs;
67
    }
68
    
69
    @Override
70
    public final Object perform(Connection conn) throws DataException {
71
        this.fetch(conn);
72
        return true;
73
    }
74
    
75
    protected TableReference getTable() {
76
        return this.table;
77
    }
78
    
79
    public void fetch(Connection conn) throws DataException {
80
        List<String> pks = this.primaryKeys;
81
        Statement st = null;
82
        ResultSet rs = null;
83
        try {
84
            if (CollectionUtils.isEmpty(pks)) {
85
                if (table.hasSubquery()) {
86
                    LOGGER.info("Searching pk in a table with a subquery ("+table.toString()+".");
87
                }
88
                pks = this.getPrimaryKeysFromMetadata(conn, null, table.getSchema(), table.getTable());
89
                if (CollectionUtils.isEmpty(pks)) {
90
                    pks = getPrimaryKeysFromInformationSchema(conn);
91
                }
92
            }
93
            st = conn.createStatement();
94
            st.setFetchSize(1);
95
            rs = JDBCUtils.executeQuery(st, this.getSQLToRetrieveFirstRowOfTable());
96
            ResultSetMetaData rsMetadata = rs.getMetaData();
97

    
98
            fetchFeatureTypeFromMetadata(conn, rsMetadata, pks);
99

    
100
        } catch (SQLException ex) {
101
            throw new RuntimeException("Can't fecth feature type.",ex);
102
        } finally {
103
            JDBCUtils.closeQuietly(rs);
104
            JDBCUtils.closeQuietly(st);
105
        }
106
    }
107
    
108
    public String getSQLToRetrieveFirstRowOfTable() {
109
      JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
110
      sqlbuilder.select().column().all();
111
      sqlbuilder.select().from().table()
112
          .database(this.table.getDatabase())
113
          .schema(this.table.getSchema())
114
          .name(this.table.getTable());
115
      sqlbuilder.select().from().subquery(this.table.getSubquery());
116
      sqlbuilder.select().limit(1);
117

    
118
      String sql = sqlbuilder.toString();
119
      return sql;
120
    }
121
    
122
    
123
    public void fetchFeatureTypeFromMetadata(Connection conn, ResultSetMetaData rsMetadata) throws SQLException {
124
        this.fetchFeatureTypeFromMetadata(conn, rsMetadata, new ArrayList<String>());
125
    }
126

    
127
    protected void fetchFeatureTypeFromMetadata(Connection conn, ResultSetMetaData rsMetadata, List<String> pks) throws SQLException {
128
        int i;
129
        int geometriesColumns = 0;
130
        String lastGeometry = null;
131

    
132
        EditableFeatureAttributeDescriptor attr;
133
        boolean firstGeometryAttrFound = false;
134
        for (i = 1; i <= rsMetadata.getColumnCount(); i++) {
135
            attr = getAttributeFromMetadata(featureType, conn, rsMetadata, i);
136
            if ( isInPrimaryKeys(pks,attr) ) {
137
                attr.setIsPrimaryKey(true);
138
            }
139
            if (attr.getType() == DataTypes.GEOMETRY) {
140
                geometriesColumns++;
141
                lastGeometry = attr.getName();
142
                // Set the default geometry attribute if it is the one
143
                // given as parameter or it is the first one, just in case.
144
                if (!firstGeometryAttrFound || StringUtils.equalsIgnoreCase(lastGeometry, defaultGeometryColumn)) {
145
                    firstGeometryAttrFound = true;
146
                    featureType.setDefaultGeometryAttributeName(lastGeometry);
147
                }
148
            }
149

    
150
        }
151
        if (StringUtils.isBlank(defaultGeometryColumn)) {
152
            if (geometriesColumns == 1) {
153
                featureType.setDefaultGeometryAttributeName(lastGeometry);
154
            }
155
        } else if (!StringUtils.equalsIgnoreCase(defaultGeometryColumn, featureType.getDefaultGeometryAttributeName())) {
156
            EditableFeatureAttributeDescriptor geomattr = featureType.getEditableAttributeDescriptor(defaultGeometryColumn);
157
            if (geomattr.getDataType().getType() != DataTypes.GEOMETRY) {
158
                geomattr.setDataType(DataTypes.GEOMETRY);
159
                geomattr.setGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
160
            }
161
            
162
        }
163
            
164
        if (crs != null && featureType.getDefaultGeometryAttribute() != null) {
165
            ((EditableFeatureAttributeDescriptor) featureType.getDefaultGeometryAttribute()).setSRS(crs);
166
        }
167
    }
168

    
169
    protected boolean isInPrimaryKeys(List<String> pks, EditableFeatureAttributeDescriptor attr) {
170
        if( pks == null || attr == null ) {
171
            return false;
172
        }
173
        // En algunos gestores de BBDD, los nombres obtenidos de las pks de los 
174
        // metadados no coinciden con los nombres de los campos ya que unos estan
175
        // en mayusculas y otros en minusculas, asi que en lugar de usar un "contains"
176
        // nos los recorremos y comparamos con IgnoreCase.
177
        for (String pk : pks) {
178
            if( StringUtils.equalsIgnoreCase(pk, attr.getName()) ) {
179
                return true;
180
            }
181
        }
182
        return false;        
183
    }
184
    
185
    protected List<String> getPrimaryKeysFromMetadata(
186
            Connection conn,
187
            String catalog,
188
            String schema,
189
            String table) throws SQLException {
190

    
191
        ResultSet rsPrimaryKeys = null;
192
        ResultSet rs = null;
193
        try {
194
            DatabaseMetaData metadata = conn.getMetaData();
195
            rs = metadata.getTables(catalog, schema, table, null);
196

    
197
            if (!rs.next()) {
198
                // No tables found with default values, ignoring catalog
199
                rs.close();
200
                catalog = null;
201
                schema = null;
202
                rs = metadata.getTables(catalog, schema, table, null);
203
                if (!rs.next()) {
204
                    // table not found
205
                    return null;
206
                } else if (rs.next()) {
207
                    // More that one, cant identify
208
                    return null;
209
                }
210

    
211
            } else if (rs.next()) {
212
                // More that one, cant identify
213
                return null;
214
            }
215
            rsPrimaryKeys = metadata.getPrimaryKeys(catalog, schema, table);
216
            List pks = new ArrayList();
217
            while (rsPrimaryKeys.next()) {
218
                pks.add(rsPrimaryKeys.getString("COLUMN_NAME"));
219
            }
220
            return pks;
221

    
222
        } catch (SQLException e) {
223
            return null;
224

    
225
        } finally {
226
            JDBCUtils.closeQuietly(rs);
227
            JDBCUtils.closeQuietly(rsPrimaryKeys);
228
        }
229

    
230
    }
231

    
232
    protected List<String> getPrimaryKeysFromInformationSchema(Connection conn) throws SQLException {
233

    
234
        String sql = getSQLToRetrievePrimaryKeysFromInformationSchema();
235

    
236
        Statement st = null;
237
        ResultSet rs = null;
238
        List<String> pks = new ArrayList();
239
        try {
240
            st = conn.createStatement();
241
            rs = JDBCUtils.executeQuery(st, sql);
242
            while (rs.next()) {
243
                pks.add(rs.getString(1));
244
            }
245
            if (pks.isEmpty()) {
246
                return null;
247
            }
248
            return pks;
249

    
250
        } catch (Exception ex) {
251
            return pks;
252
            
253
        } finally {
254
            JDBCUtils.closeQuietly(rs);
255
            JDBCUtils.closeQuietly(st);
256
        }
257
    }
258

    
259
    public String getSQLToRetrievePrimaryKeysFromInformationSchema() throws SQLException {
260
        JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
261
        ExpressionBuilder expbuilder = sqlbuilder.expression();
262

    
263
        sqlbuilder.select().column().name("COLUMN_NAME");
264
        sqlbuilder.select().column().name("CONSTRAINT_TYPE");
265
        sqlbuilder.select().from().custom(
266
                "INFORMATION_SCHEMA.table_constraints t_cons "
267
                + "inner join INFORMATION_SCHEMA.key_column_usage c on "
268
                + "c.constraint_catalog = t_cons.constraint_catalog and "
269
                + "c.table_schema = t_cons.table_schema and "
270
                + "c.table_name = t_cons.table_name and "
271
                + "c.constraint_name = t_cons.constraint_name "
272
        );
273
        sqlbuilder.select().where().set(
274
                expbuilder.like(
275
                        expbuilder.custom("c.TABLE_NAME"), 
276
                        expbuilder.constant(table.getTable())
277
                )
278
        );
279
        if( table.hasSchema() ) {
280
            sqlbuilder.select().where().and(
281
                    expbuilder.like(
282
                            expbuilder.custom("c.TABLE_SCHEMA"),
283
                            expbuilder.constant(table.getSchema())
284
                    )
285
            );
286
        }
287
//        if (catalog != null) {
288
//            sqlbuilder.select().where().and(
289
//                    expbuilder.like(
290
//                            expbuilder.custom("c.CONSTRAINT_CATALOG"),
291
//                            expbuilder.constant(catalog)
292
//                    )
293
//            );
294
//        }
295
        sqlbuilder.select().where().and(
296
                expbuilder.eq(
297
                        expbuilder.column("CONSTRAINT_TYPE"),
298
                        expbuilder.constant("PRIMARY KEY")
299
                )
300
        );
301
        return sqlbuilder.toString();
302
    }
303
    
304
    
305
    protected EditableFeatureAttributeDescriptor getAttributeFromMetadata(
306
            EditableFeatureType type,
307
            Connection conn,
308
            ResultSetMetaData rsMetadata,
309
            int colIndex
310
        ) throws SQLException {
311

    
312
        EditableFeatureAttributeDescriptor attr = type.add(
313
                rsMetadata.getColumnName(colIndex),
314
                this.getDataTypeFromMetadata(rsMetadata, colIndex)
315
        );
316
        attr.setAllowNull(
317
            rsMetadata.isNullable(colIndex) == ResultSetMetaData.columnNullable
318
        );
319
        attr.setIsAutomatic(rsMetadata.isAutoIncrement(colIndex));
320
        attr.setIsReadOnly(rsMetadata.isReadOnly(colIndex));
321
        switch(attr.getType()) {
322
            case DataTypes.STRING:
323
              attr.setSize(rsMetadata.getPrecision(colIndex));
324
              attr.setPrecision(DataType.PRECISION_NONE);
325
              attr.setScale(DataType.SCALE_NONE);
326
              break;
327
            case DataTypes.BYTE:
328
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
329
              attr.setPrecision(DataType.BYTE_DEFAULT_PRECISION);
330
              attr.setScale(DataType.SCALE_NONE);
331
              break;
332
            case DataTypes.INT:
333
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
334
              attr.setPrecision(DataType.INT_DEFAULT_PRECISION);
335
              attr.setScale(DataType.SCALE_NONE);
336
              break;
337
            case DataTypes.LONG:
338
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
339
              attr.setPrecision(DataType.LONG_DEFAULT_PRECISION);
340
              attr.setScale(DataType.SCALE_NONE);
341
              break;
342
            case DataTypes.FLOAT:
343
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
344
              attr.setPrecision(DataType.FLOAT_DEFAULT_PRECISION);
345
              attr.setScale(DataType.SCALE_NONE);
346
              break;
347
            case DataTypes.DOUBLE:
348
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
349
              attr.setPrecision(DataType.DOUBLE_DEFAULT_PRECISION);
350
              attr.setScale(DataType.SCALE_NONE);
351
              break;
352
            case DataTypes.DECIMAL:
353
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
354
              attr.setScale(rsMetadata.getScale(colIndex));
355
              attr.setPrecision(rsMetadata.getPrecision(colIndex));
356
              break;
357
            case DataTypes.OBJECT:
358
                attr.setAdditionalInfo(
359
                        "SQLType",
360
                        rsMetadata.getColumnType(colIndex)
361
                );
362
                attr.setAdditionalInfo(
363
                        "SQLTypeName",
364
                        rsMetadata.getColumnTypeName(colIndex)
365
                );
366
                break;
367
            case DataTypes.GEOMETRY:
368
                this.fetchGeometryTypeAndSRS(attr, rsMetadata, colIndex);
369
                break;
370
        }
371
        return attr;
372
    }
373

    
374
    protected int getDataTypeFromMetadata(
375
            ResultSetMetaData rsMetadata,
376
            int colIndex
377
        ) throws SQLException {
378

    
379
        switch (rsMetadata.getColumnType(colIndex)) {
380
            case java.sql.Types.TINYINT:
381
                return DataTypes.BYTE;
382

    
383
            case java.sql.Types.SMALLINT:
384
            case java.sql.Types.INTEGER:
385
                return DataTypes.INT;
386

    
387
            case java.sql.Types.BIGINT:
388
                return DataTypes.LONG;
389

    
390
            case java.sql.Types.REAL:
391
            case java.sql.Types.FLOAT:
392
                return DataTypes.FLOAT;
393

    
394
            case java.sql.Types.DOUBLE:
395
                return DataTypes.DOUBLE;
396

    
397
            case java.sql.Types.NUMERIC:
398
            case java.sql.Types.DECIMAL:
399
                return DataTypes.DECIMAL;
400

    
401
            case java.sql.Types.CHAR:
402
            case java.sql.Types.VARCHAR:
403
            case java.sql.Types.LONGVARCHAR:
404
                return DataTypes.STRING;
405

    
406
            case java.sql.Types.DATE:
407
                return DataTypes.DATE;
408

    
409
            case java.sql.Types.TIME:
410
                return DataTypes.TIME;
411

    
412
            case java.sql.Types.TIMESTAMP:
413
                return DataTypes.TIMESTAMP;
414

    
415
            case java.sql.Types.BOOLEAN:
416
            case java.sql.Types.BIT:
417
                return DataTypes.BOOLEAN;
418

    
419
            case java.sql.Types.BLOB:
420
            case java.sql.Types.BINARY:
421
            case java.sql.Types.LONGVARBINARY:
422
                return DataTypes.BYTEARRAY;
423

    
424
            default:
425
                String typeName = rsMetadata.getColumnTypeName(colIndex);
426
                if( "geometry".equalsIgnoreCase(typeName) ) {
427
                    return DataTypes.GEOMETRY;
428
                }
429
                return DataTypes.OBJECT;
430
        }
431
    }
432

    
433
    /**
434
     * Inicializa el tipo, subtipo y SRS del attributo de tipo geometria.
435
     * 
436
     * @param attr
437
     * @param rsMetadata
438
     * @param colIndex 
439
     */
440
    protected void fetchGeometryTypeAndSRS(
441
            EditableFeatureAttributeDescriptor attr,
442
            ResultSetMetaData rsMetadata,
443
            int colIndex
444
        ) {
445
        if( attr.getType()!=DataTypes.GEOMETRY ) {
446
            return;
447
        }
448
        try {
449
            GeometryType geomType = GeometryLocator.getGeometryManager().getGeometryType(
450
                    Geometry.TYPES.GEOMETRY,
451
                    Geometry.SUBTYPES.GEOM2D
452
            );
453
            attr.setGeometryType(geomType);
454
            attr.setSRS((IProjection)null);
455
        } catch (Exception ex) {
456
            LOGGER.warn("Can't get default geometry type.",ex);
457
        }
458
    }
459
    
460
}