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 @ 44058

History | View | Annotate | Download (14.6 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.fmap.dal.DataTypes;
15
import org.gvsig.fmap.dal.exception.DataException;
16
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
17
import org.gvsig.fmap.dal.feature.EditableFeatureType;
18
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
19
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
20
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
21
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
22
import org.gvsig.fmap.geom.Geometry;
23
import org.gvsig.fmap.geom.GeometryLocator;
24
import org.gvsig.fmap.geom.type.GeometryType;
25
import static org.gvsig.fmap.dal.store.jdbc2.spi.operations.AbstractConnectionOperation.LOGGER;
26

    
27
public class FetchFeatureTypeOperation extends AbstractConnectionOperation {
28
    private final EditableFeatureType featureType;
29
    private final TableReference table;
30
    private final List<String> primaryKeys;
31
    private final String defaultGeometryColumn;
32
    private final IProjection crs;
33

    
34
    public FetchFeatureTypeOperation(
35
            JDBCHelper helper
36
        ) {
37
        this(helper, null, null, null, null, null);
38
    }
39
    
40
    public FetchFeatureTypeOperation(
41
            JDBCHelper helper,
42
            EditableFeatureType featureType,
43
            String defaultGeometryColumn,
44
            IProjection crs
45
        ) {
46
        this(helper, featureType, null, null, defaultGeometryColumn, crs);
47
    }
48

    
49
    public FetchFeatureTypeOperation(
50
            JDBCHelper helper,
51
            EditableFeatureType featureType,
52
            TableReference table,
53
            List<String> primaryKeys,
54
            String defaultGeometryColumn,
55
            IProjection crs
56
        ) {
57
        super(helper);
58
        this.featureType = featureType;
59
        this.table = table;
60
        this.primaryKeys = primaryKeys;
61
        this.defaultGeometryColumn = defaultGeometryColumn;
62
        this.crs = crs;
63
    }
64
    
65
    @Override
66
    public final Object perform(Connection conn) throws DataException {
67
        this.fetch(featureType, conn, table, 
68
                primaryKeys, defaultGeometryColumn, crs
69
        );
70
        return true;
71
    }
72
    
73
    protected TableReference getTable() {
74
        return this.table;
75
    }
76
    
77
    public void fetch(
78
            EditableFeatureType featureType,
79
            Connection conn,
80
            TableReference table,
81
            List<String> pks,
82
            String defaultGeometryColumn,
83
            IProjection crs
84
    ) throws DataException {
85

    
86
        Statement st = null;
87
        ResultSet rs = null;
88
        try {
89
            if (CollectionUtils.isEmpty(pks)) {
90
                if (!table.hasSubquery()) {
91
                    pks = this.getPrimaryKeysFromMetadata(conn, null, table.getSchema(), table.getTable());
92
                    if (CollectionUtils.isEmpty(pks)) {
93
                        pks = getPrimaryKeysFromInformationSchema(conn, null, table.getSchema(), table.getTable());
94
                    }
95
                }
96
            }
97

    
98
            JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
99
            sqlbuilder.select().column().all();
100
            sqlbuilder.select().from().table()
101
                .database(this.table.getDatabase())
102
                .schema(this.table.getSchema())
103
                .name(this.table.getTable());
104
            sqlbuilder.select().from().subquery(this.table.getSubquery());
105
            sqlbuilder.select().limit(1);
106

    
107
            st = conn.createStatement();
108
            st.setFetchSize(1);
109
            rs = JDBCUtils.executeQuery(st, sqlbuilder.toString());
110
            ResultSetMetaData rsMetadata = rs.getMetaData();
111

    
112
            fetchFeatureTypeFromMetadata(conn, rsMetadata, pks);
113

    
114
        } catch (SQLException ex) {
115
            throw new RuntimeException("Can't fecth feature type.",ex);
116
        } finally {
117
            JDBCUtils.closeQuietly(rs);
118
            JDBCUtils.closeQuietly(st);
119
        }
120
    }
121
    
122
    public void fetchFeatureTypeFromMetadata(Connection conn, ResultSetMetaData rsMetadata) throws SQLException {
123
        this.fetchFeatureTypeFromMetadata(conn, rsMetadata, new ArrayList<String>());
124
    }
125

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

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

    
149
        }
150
        if (defaultGeometryColumn == null && geometriesColumns == 1) {
151
            featureType.setDefaultGeometryAttributeName(lastGeometry);
152
        }
153

    
154
        if (crs != null && featureType.getDefaultGeometryAttribute() != null) {
155
            ((EditableFeatureAttributeDescriptor) featureType.getDefaultGeometryAttribute()).setSRS(crs);
156
        }
157
    }
158

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

    
181
        ResultSet rsPrimaryKeys = null;
182
        ResultSet rs = null;
183
        try {
184
            DatabaseMetaData metadata = conn.getMetaData();
185
            rs = metadata.getTables(catalog, schema, table, null);
186

    
187
            if (!rs.next()) {
188
                // No tables found with default values, ignoring catalog
189
                rs.close();
190
                catalog = null;
191
                schema = null;
192
                rs = metadata.getTables(catalog, schema, table, null);
193
                if (!rs.next()) {
194
                    // table not found
195
                    return null;
196
                } else if (rs.next()) {
197
                    // More that one, cant identify
198
                    return null;
199
                }
200

    
201
            } else if (rs.next()) {
202
                // More that one, cant identify
203
                return null;
204
            }
205
            rsPrimaryKeys = metadata.getPrimaryKeys(catalog, schema, table);
206
            List pks = new ArrayList();
207
            while (rsPrimaryKeys.next()) {
208
                pks.add(rsPrimaryKeys.getString("COLUMN_NAME"));
209
            }
210
            return pks;
211

    
212
        } catch (SQLException e) {
213
            return null;
214

    
215
        } finally {
216
            JDBCUtils.closeQuietly(rs);
217
            JDBCUtils.closeQuietly(rsPrimaryKeys);
218
        }
219

    
220
    }
221

    
222
    protected List<String> getPrimaryKeysFromInformationSchema(
223
            Connection conn,
224
            String catalog,
225
            String schema,
226
            String table) throws SQLException {
227

    
228
        String sql = getSQLToRetrievePrimaryKeysFromInformationSchema(catalog, schema, table);
229

    
230
        Statement st = null;
231
        ResultSet rs = null;
232
        List<String> pks = new ArrayList();
233
        try {
234
            st = conn.createStatement();
235
            rs = JDBCUtils.executeQuery(st, sql);
236
            while (rs.next()) {
237
                pks.add(rs.getString(1));
238
            }
239
            if (pks.isEmpty()) {
240
                return null;
241
            }
242
            return pks;
243

    
244
        } catch (Exception ex) {
245
            return pks;
246
            
247
        } finally {
248
            JDBCUtils.closeQuietly(rs);
249
            JDBCUtils.closeQuietly(st);
250
        }
251
    }
252

    
253
    protected String getSQLToRetrievePrimaryKeysFromInformationSchema(
254
            String catalog,
255
            String schema,
256
            String table
257
        ) throws SQLException {
258
        JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
259

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

    
309
        EditableFeatureAttributeDescriptor attr = type.add(
310
                rsMetadata.getColumnName(colIndex),
311
                this.getDataTypeFromMetadata(rsMetadata, colIndex)
312
        );
313
        attr.setAllowNull(
314
            rsMetadata.isNullable(colIndex) == ResultSetMetaData.columnNullable
315
        );
316
        attr.setIsAutomatic(rsMetadata.isAutoIncrement(colIndex));
317
        attr.setIsReadOnly(rsMetadata.isReadOnly(colIndex));
318
        attr.setPrecision(rsMetadata.getPrecision(colIndex));
319
        attr.setSize(rsMetadata.getColumnDisplaySize(colIndex));
320
        switch(attr.getType()) {
321
            case DataTypes.OBJECT:
322
                attr.setAdditionalInfo(
323
                        "SQLType",
324
                        rsMetadata.getColumnType(colIndex)
325
                );
326
                attr.setAdditionalInfo(
327
                        "SQLTypeName",
328
                        rsMetadata.getColumnTypeName(colIndex)
329
                );
330
                break;
331
            case DataTypes.GEOMETRY:
332
                this.fetchGeometryTypeAndSRS(attr, rsMetadata, colIndex);
333
                break;
334
        }
335
        return attr;
336
    }
337

    
338
    protected int getDataTypeFromMetadata(
339
            ResultSetMetaData rsMetadata,
340
            int colIndex
341
        ) throws SQLException {
342

    
343
        switch (rsMetadata.getColumnType(colIndex)) {
344
            case java.sql.Types.INTEGER:
345
                return DataTypes.INT;
346

    
347
            case java.sql.Types.BIGINT:
348
                return DataTypes.LONG;
349

    
350
            case java.sql.Types.REAL:
351
                return DataTypes.DOUBLE;
352

    
353
            case java.sql.Types.DOUBLE:
354
                return DataTypes.DOUBLE;
355

    
356
            case java.sql.Types.CHAR:
357
                return DataTypes.STRING;
358

    
359
            case java.sql.Types.VARCHAR:
360
            case java.sql.Types.LONGVARCHAR:
361
                return DataTypes.STRING;
362

    
363
            case java.sql.Types.FLOAT:
364
                return DataTypes.DOUBLE;
365

    
366
            case java.sql.Types.NUMERIC:
367
                return DataTypes.DOUBLE;
368

    
369
            case java.sql.Types.DECIMAL:
370
                return DataTypes.FLOAT;
371

    
372
            case java.sql.Types.DATE:
373
                return DataTypes.DATE;
374

    
375
            case java.sql.Types.TIME:
376
                return DataTypes.TIME;
377

    
378
            case java.sql.Types.TIMESTAMP:
379
                return DataTypes.TIMESTAMP;
380

    
381
            case java.sql.Types.BOOLEAN:
382
            case java.sql.Types.BIT:
383
                return DataTypes.BOOLEAN;
384

    
385
            case java.sql.Types.BLOB:
386
            case java.sql.Types.BINARY:
387
            case java.sql.Types.LONGVARBINARY:
388
                return DataTypes.BYTEARRAY;
389

    
390
            default:
391
                String typeName = rsMetadata.getColumnTypeName(colIndex);
392
                if( "geometry".equalsIgnoreCase(typeName) ) {
393
                    return DataTypes.GEOMETRY;
394
                }
395
                return DataTypes.OBJECT;
396
        }
397
    }
398

    
399
    /**
400
     * Inicializa el tipo, subtipo y SRS del attributo de tipo geometria.
401
     * 
402
     * @param attr
403
     * @param rsMetadata
404
     * @param colIndex 
405
     */
406
    protected void fetchGeometryTypeAndSRS(
407
            EditableFeatureAttributeDescriptor attr,
408
            ResultSetMetaData rsMetadata,
409
            int colIndex
410
        ) {
411
        if( attr.getType()!=DataTypes.GEOMETRY ) {
412
            return;
413
        }
414
        try {
415
            GeometryType geomType = GeometryLocator.getGeometryManager().getGeometryType(
416
                    Geometry.TYPES.GEOMETRY,
417
                    Geometry.SUBTYPES.GEOM2D
418
            );
419
            attr.setGeometryType(geomType);
420
            attr.setSRS(null);
421
        } catch (Exception ex) {
422
            LOGGER.warn("Can't get default geometry type.",ex);
423
        }
424
    }
425
    
426
}