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

History | View | Annotate | Download (14.8 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.store.jdbc2.spi.JDBCSQLBuilderBase;
20
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
21
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
22
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
23
import org.gvsig.fmap.geom.Geometry;
24
import org.gvsig.fmap.geom.GeometryLocator;
25
import org.gvsig.fmap.geom.type.GeometryType;
26
import static org.gvsig.fmap.dal.store.jdbc2.spi.operations.AbstractConnectionOperation.LOGGER;
27

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

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

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

    
88
        Statement st = null;
89
        ResultSet rs = null;
90
        try {
91
            if (CollectionUtils.isEmpty(pks)) {
92
                if (table.hasSubquery()) {
93
                    LOGGER.info("Searching pk in a table with a subquery ("+table.toString()+".");
94
                }
95
                pks = this.getPrimaryKeysFromMetadata(conn, null, table.getSchema(), table.getTable());
96
                if (CollectionUtils.isEmpty(pks)) {
97
                    pks = getPrimaryKeysFromInformationSchema(conn, null, table.getSchema(), table.getTable());
98
                }
99
            }
100

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

    
110
            st = conn.createStatement();
111
            st.setFetchSize(1);
112
            rs = JDBCUtils.executeQuery(st, sqlbuilder.toString());
113
            ResultSetMetaData rsMetadata = rs.getMetaData();
114

    
115
            fetchFeatureTypeFromMetadata(conn, rsMetadata, pks);
116

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

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

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

    
152
        }
153
        if (defaultGeometryColumn == null && geometriesColumns == 1) {
154
            featureType.setDefaultGeometryAttributeName(lastGeometry);
155
        }
156

    
157
        if (crs != null && featureType.getDefaultGeometryAttribute() != null) {
158
            ((EditableFeatureAttributeDescriptor) featureType.getDefaultGeometryAttribute()).setSRS(crs);
159
        }
160
    }
161

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

    
184
        ResultSet rsPrimaryKeys = null;
185
        ResultSet rs = null;
186
        try {
187
            DatabaseMetaData metadata = conn.getMetaData();
188
            rs = metadata.getTables(catalog, schema, table, null);
189

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

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

    
215
        } catch (SQLException e) {
216
            return null;
217

    
218
        } finally {
219
            JDBCUtils.closeQuietly(rs);
220
            JDBCUtils.closeQuietly(rsPrimaryKeys);
221
        }
222

    
223
    }
224

    
225
    protected List<String> getPrimaryKeysFromInformationSchema(
226
            Connection conn,
227
            String catalog,
228
            String schema,
229
            String table) throws SQLException {
230

    
231
        String sql = getSQLToRetrievePrimaryKeysFromInformationSchema(catalog, schema, table);
232

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

    
247
        } catch (Exception ex) {
248
            return pks;
249
            
250
        } finally {
251
            JDBCUtils.closeQuietly(rs);
252
            JDBCUtils.closeQuietly(st);
253
        }
254
    }
255

    
256
    protected String getSQLToRetrievePrimaryKeysFromInformationSchema(
257
            String catalog,
258
            String schema,
259
            String table
260
        ) throws SQLException {
261
        JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
262
        ExpressionBuilder expbuilder = sqlbuilder.expression();
263

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

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

    
342
    protected int getDataTypeFromMetadata(
343
            ResultSetMetaData rsMetadata,
344
            int colIndex
345
        ) throws SQLException {
346

    
347
        switch (rsMetadata.getColumnType(colIndex)) {
348
            case java.sql.Types.INTEGER:
349
                return DataTypes.INT;
350

    
351
            case java.sql.Types.BIGINT:
352
                return DataTypes.LONG;
353

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

    
357
            case java.sql.Types.DOUBLE:
358
                return DataTypes.DOUBLE;
359

    
360
            case java.sql.Types.CHAR:
361
                return DataTypes.STRING;
362

    
363
            case java.sql.Types.VARCHAR:
364
            case java.sql.Types.LONGVARCHAR:
365
                return DataTypes.STRING;
366

    
367
            case java.sql.Types.FLOAT:
368
                return DataTypes.DOUBLE;
369

    
370
            case java.sql.Types.NUMERIC:
371
                return DataTypes.DOUBLE;
372

    
373
            case java.sql.Types.DECIMAL:
374
                return DataTypes.FLOAT;
375

    
376
            case java.sql.Types.DATE:
377
                return DataTypes.DATE;
378

    
379
            case java.sql.Types.TIME:
380
                return DataTypes.TIME;
381

    
382
            case java.sql.Types.TIMESTAMP:
383
                return DataTypes.TIMESTAMP;
384

    
385
            case java.sql.Types.BOOLEAN:
386
            case java.sql.Types.BIT:
387
                return DataTypes.BOOLEAN;
388

    
389
            case java.sql.Types.BLOB:
390
            case java.sql.Types.BINARY:
391
            case java.sql.Types.LONGVARBINARY:
392
                return DataTypes.BYTEARRAY;
393

    
394
            default:
395
                String typeName = rsMetadata.getColumnTypeName(colIndex);
396
                if( "geometry".equalsIgnoreCase(typeName) ) {
397
                    return DataTypes.GEOMETRY;
398
                }
399
                return DataTypes.OBJECT;
400
        }
401
    }
402

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