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

History | View | Annotate | Download (17.7 KB)

1 45065 jjdelcerro
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2020 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24 43020 jjdelcerro
package org.gvsig.fmap.dal.store.jdbc2.spi.operations;
25
26
import java.sql.Connection;
27
import java.sql.DatabaseMetaData;
28
import java.sql.ResultSet;
29
import java.sql.ResultSetMetaData;
30
import java.sql.SQLException;
31
import java.sql.Statement;
32
import java.util.ArrayList;
33
import java.util.List;
34
import org.apache.commons.collections.CollectionUtils;
35
import org.apache.commons.lang3.StringUtils;
36
import org.cresques.cts.IProjection;
37 44198 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionBuilder;
38 43020 jjdelcerro
import org.gvsig.fmap.dal.DataTypes;
39
import org.gvsig.fmap.dal.exception.DataException;
40
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
41
import org.gvsig.fmap.dal.feature.EditableFeatureType;
42 44951 omartinez
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
43 43020 jjdelcerro
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
44
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
45
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
46 44058 jjdelcerro
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
47 43020 jjdelcerro
import org.gvsig.fmap.geom.Geometry;
48
import org.gvsig.fmap.geom.GeometryLocator;
49
import org.gvsig.fmap.geom.type.GeometryType;
50 44058 jjdelcerro
import static org.gvsig.fmap.dal.store.jdbc2.spi.operations.AbstractConnectionOperation.LOGGER;
51 44678 jjdelcerro
import org.gvsig.tools.dataTypes.DataType;
52 43020 jjdelcerro
53 44253 jjdelcerro
@SuppressWarnings("UseSpecificCatch")
54 43020 jjdelcerro
public class FetchFeatureTypeOperation extends AbstractConnectionOperation {
55
    private final EditableFeatureType featureType;
56 44376 jjdelcerro
    protected final TableReference table;
57 43020 jjdelcerro
    private final List<String> primaryKeys;
58
    private final String defaultGeometryColumn;
59
    private final IProjection crs;
60
61
    public FetchFeatureTypeOperation(
62
            JDBCHelper helper
63
        ) {
64 44058 jjdelcerro
        this(helper, null, null, null, null, null);
65 43020 jjdelcerro
    }
66
67
    public FetchFeatureTypeOperation(
68
            JDBCHelper helper,
69
            EditableFeatureType featureType,
70 43377 jjdelcerro
            String defaultGeometryColumn,
71
            IProjection crs
72
        ) {
73 44058 jjdelcerro
        this(helper, featureType, null, null, defaultGeometryColumn, crs);
74 43377 jjdelcerro
    }
75
76
    public FetchFeatureTypeOperation(
77
            JDBCHelper helper,
78
            EditableFeatureType featureType,
79 44058 jjdelcerro
            TableReference table,
80 43020 jjdelcerro
            List<String> primaryKeys,
81
            String defaultGeometryColumn,
82
            IProjection crs
83
        ) {
84
        super(helper);
85
        this.featureType = featureType;
86
        this.table = table;
87
        this.primaryKeys = primaryKeys;
88
        this.defaultGeometryColumn = defaultGeometryColumn;
89
        this.crs = crs;
90
    }
91
92
    @Override
93
    public final Object perform(Connection conn) throws DataException {
94 44678 jjdelcerro
        this.fetch(conn);
95 43020 jjdelcerro
        return true;
96
    }
97
98 44058 jjdelcerro
    protected TableReference getTable() {
99 43114 jjdelcerro
        return this.table;
100
    }
101
102 44678 jjdelcerro
    public void fetch(Connection conn) throws DataException {
103
        List<String> pks = this.primaryKeys;
104 43020 jjdelcerro
        Statement st = null;
105
        ResultSet rs = null;
106
        try {
107
            if (CollectionUtils.isEmpty(pks)) {
108 44331 jjdelcerro
                if (table.hasSubquery()) {
109 45433 fdiaz
                    LOGGER.debug("Searching pk in a table with a subquery ("+table.toString()+".");
110 43020 jjdelcerro
                }
111 44331 jjdelcerro
                pks = this.getPrimaryKeysFromMetadata(conn, null, table.getSchema(), table.getTable());
112
                if (CollectionUtils.isEmpty(pks)) {
113 44678 jjdelcerro
                    pks = getPrimaryKeysFromInformationSchema(conn);
114 44331 jjdelcerro
                }
115 43020 jjdelcerro
            }
116
            st = conn.createStatement();
117
            st.setFetchSize(1);
118 44678 jjdelcerro
            rs = JDBCUtils.executeQuery(st, this.getSQLToRetrieveFirstRowOfTable());
119 43020 jjdelcerro
            ResultSetMetaData rsMetadata = rs.getMetaData();
120
121 43377 jjdelcerro
            fetchFeatureTypeFromMetadata(conn, rsMetadata, pks);
122 43020 jjdelcerro
123
        } catch (SQLException ex) {
124
            throw new RuntimeException("Can't fecth feature type.",ex);
125
        } finally {
126
            JDBCUtils.closeQuietly(rs);
127
            JDBCUtils.closeQuietly(st);
128
        }
129 43377 jjdelcerro
    }
130
131 44678 jjdelcerro
    public String getSQLToRetrieveFirstRowOfTable() {
132
      JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
133
      sqlbuilder.select().column().all();
134
      sqlbuilder.select().from().table()
135
          .database(this.table.getDatabase())
136
          .schema(this.table.getSchema())
137
          .name(this.table.getTable());
138 45452 fdiaz
//      sqlbuilder.select().from().subquery(this.table.getSubquery());
139 44678 jjdelcerro
      sqlbuilder.select().limit(1);
140
141
      String sql = sqlbuilder.toString();
142
      return sql;
143
    }
144
145
146 43377 jjdelcerro
    public void fetchFeatureTypeFromMetadata(Connection conn, ResultSetMetaData rsMetadata) throws SQLException {
147
        this.fetchFeatureTypeFromMetadata(conn, rsMetadata, new ArrayList<String>());
148
    }
149 43020 jjdelcerro
150 43377 jjdelcerro
    protected void fetchFeatureTypeFromMetadata(Connection conn, ResultSetMetaData rsMetadata, List<String> pks) throws SQLException {
151
        int i;
152
        int geometriesColumns = 0;
153
        String lastGeometry = null;
154
155
        EditableFeatureAttributeDescriptor attr;
156
        boolean firstGeometryAttrFound = false;
157
        for (i = 1; i <= rsMetadata.getColumnCount(); i++) {
158
            attr = getAttributeFromMetadata(featureType, conn, rsMetadata, i);
159
            if ( isInPrimaryKeys(pks,attr) ) {
160
                attr.setIsPrimaryKey(true);
161
            }
162
            if (attr.getType() == DataTypes.GEOMETRY) {
163
                geometriesColumns++;
164
                lastGeometry = attr.getName();
165
                // Set the default geometry attribute if it is the one
166
                // given as parameter or it is the first one, just in case.
167 44951 omartinez
                if (!firstGeometryAttrFound || StringUtils.equalsIgnoreCase(lastGeometry, defaultGeometryColumn)) {
168 43377 jjdelcerro
                    firstGeometryAttrFound = true;
169
                    featureType.setDefaultGeometryAttributeName(lastGeometry);
170
                }
171
            }
172
173
        }
174 44951 omartinez
        if (StringUtils.isBlank(defaultGeometryColumn)) {
175
            if (geometriesColumns == 1) {
176
                featureType.setDefaultGeometryAttributeName(lastGeometry);
177
            }
178
        } else if (!StringUtils.equalsIgnoreCase(defaultGeometryColumn, featureType.getDefaultGeometryAttributeName())) {
179
            EditableFeatureAttributeDescriptor geomattr = featureType.getEditableAttributeDescriptor(defaultGeometryColumn);
180
            if (geomattr.getDataType().getType() != DataTypes.GEOMETRY) {
181
                geomattr.setDataType(DataTypes.GEOMETRY);
182
                geomattr.setGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
183
            }
184
185 43377 jjdelcerro
        }
186 44951 omartinez
187 43020 jjdelcerro
        if (crs != null && featureType.getDefaultGeometryAttribute() != null) {
188
            ((EditableFeatureAttributeDescriptor) featureType.getDefaultGeometryAttribute()).setSRS(crs);
189
        }
190
    }
191
192 43114 jjdelcerro
    protected boolean isInPrimaryKeys(List<String> pks, EditableFeatureAttributeDescriptor attr) {
193 43786 jjdelcerro
        if( pks == null || attr == null ) {
194
            return false;
195
        }
196 43355 jjdelcerro
        // En algunos gestores de BBDD, los nombres obtenidos de las pks de los
197
        // metadados no coinciden con los nombres de los campos ya que unos estan
198
        // en mayusculas y otros en minusculas, asi que en lugar de usar un "contains"
199
        // nos los recorremos y comparamos con IgnoreCase.
200
        for (String pk : pks) {
201
            if( StringUtils.equalsIgnoreCase(pk, attr.getName()) ) {
202
                return true;
203
            }
204
        }
205
        return false;
206 43114 jjdelcerro
    }
207
208 43020 jjdelcerro
    protected List<String> getPrimaryKeysFromMetadata(
209
            Connection conn,
210
            String catalog,
211
            String schema,
212
            String table) throws SQLException {
213
214
        ResultSet rsPrimaryKeys = null;
215
        ResultSet rs = null;
216
        try {
217
            DatabaseMetaData metadata = conn.getMetaData();
218
            rs = metadata.getTables(catalog, schema, table, null);
219
220
            if (!rs.next()) {
221
                // No tables found with default values, ignoring catalog
222
                rs.close();
223
                catalog = null;
224
                schema = null;
225
                rs = metadata.getTables(catalog, schema, table, null);
226
                if (!rs.next()) {
227
                    // table not found
228
                    return null;
229
                } else if (rs.next()) {
230
                    // More that one, cant identify
231
                    return null;
232
                }
233
234
            } else if (rs.next()) {
235
                // More that one, cant identify
236
                return null;
237
            }
238
            rsPrimaryKeys = metadata.getPrimaryKeys(catalog, schema, table);
239
            List pks = new ArrayList();
240
            while (rsPrimaryKeys.next()) {
241
                pks.add(rsPrimaryKeys.getString("COLUMN_NAME"));
242
            }
243
            return pks;
244
245
        } catch (SQLException e) {
246
            return null;
247
248
        } finally {
249
            JDBCUtils.closeQuietly(rs);
250
            JDBCUtils.closeQuietly(rsPrimaryKeys);
251
        }
252
253
    }
254
255 44678 jjdelcerro
    protected List<String> getPrimaryKeysFromInformationSchema(Connection conn) throws SQLException {
256 43020 jjdelcerro
257 44678 jjdelcerro
        String sql = getSQLToRetrievePrimaryKeysFromInformationSchema();
258 44058 jjdelcerro
259
        Statement st = null;
260
        ResultSet rs = null;
261
        List<String> pks = new ArrayList();
262
        try {
263
            st = conn.createStatement();
264
            rs = JDBCUtils.executeQuery(st, sql);
265
            while (rs.next()) {
266
                pks.add(rs.getString(1));
267
            }
268
            if (pks.isEmpty()) {
269
                return null;
270
            }
271
            return pks;
272
273
        } catch (Exception ex) {
274
            return pks;
275
276
        } finally {
277
            JDBCUtils.closeQuietly(rs);
278
            JDBCUtils.closeQuietly(st);
279
        }
280
    }
281
282 44678 jjdelcerro
    public String getSQLToRetrievePrimaryKeysFromInformationSchema() throws SQLException {
283 43020 jjdelcerro
        JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
284 44198 jjdelcerro
        ExpressionBuilder expbuilder = sqlbuilder.expression();
285 43020 jjdelcerro
286
        sqlbuilder.select().column().name("COLUMN_NAME");
287
        sqlbuilder.select().column().name("CONSTRAINT_TYPE");
288
        sqlbuilder.select().from().custom(
289
                "INFORMATION_SCHEMA.table_constraints t_cons "
290
                + "inner join INFORMATION_SCHEMA.key_column_usage c on "
291
                + "c.constraint_catalog = t_cons.constraint_catalog and "
292
                + "c.table_schema = t_cons.table_schema and "
293
                + "c.table_name = t_cons.table_name and "
294
                + "c.constraint_name = t_cons.constraint_name "
295
        );
296
        sqlbuilder.select().where().set(
297 44198 jjdelcerro
                expbuilder.like(
298
                        expbuilder.custom("c.TABLE_NAME"),
299 44678 jjdelcerro
                        expbuilder.constant(table.getTable())
300 43020 jjdelcerro
                )
301
        );
302 44678 jjdelcerro
        if( table.hasSchema() ) {
303 43020 jjdelcerro
            sqlbuilder.select().where().and(
304 44198 jjdelcerro
                    expbuilder.like(
305
                            expbuilder.custom("c.TABLE_SCHEMA"),
306 44678 jjdelcerro
                            expbuilder.constant(table.getSchema())
307 43020 jjdelcerro
                    )
308
            );
309
        }
310 44678 jjdelcerro
//        if (catalog != null) {
311
//            sqlbuilder.select().where().and(
312
//                    expbuilder.like(
313
//                            expbuilder.custom("c.CONSTRAINT_CATALOG"),
314
//                            expbuilder.constant(catalog)
315
//                    )
316
//            );
317
//        }
318 43020 jjdelcerro
        sqlbuilder.select().where().and(
319 44198 jjdelcerro
                expbuilder.eq(
320
                        expbuilder.column("CONSTRAINT_TYPE"),
321
                        expbuilder.constant("PRIMARY KEY")
322 43020 jjdelcerro
                )
323
        );
324 44058 jjdelcerro
        return sqlbuilder.toString();
325 43020 jjdelcerro
    }
326 44058 jjdelcerro
327
328 43020 jjdelcerro
    protected EditableFeatureAttributeDescriptor getAttributeFromMetadata(
329
            EditableFeatureType type,
330
            Connection conn,
331
            ResultSetMetaData rsMetadata,
332
            int colIndex
333
        ) throws SQLException {
334
335
        EditableFeatureAttributeDescriptor attr = type.add(
336
                rsMetadata.getColumnName(colIndex),
337
                this.getDataTypeFromMetadata(rsMetadata, colIndex)
338
        );
339
        attr.setAllowNull(
340
            rsMetadata.isNullable(colIndex) == ResultSetMetaData.columnNullable
341
        );
342
        attr.setIsAutomatic(rsMetadata.isAutoIncrement(colIndex));
343
        attr.setIsReadOnly(rsMetadata.isReadOnly(colIndex));
344
        switch(attr.getType()) {
345 44926 jjdelcerro
            case DataTypes.STRING:
346
              attr.setSize(rsMetadata.getPrecision(colIndex));
347
              attr.setPrecision(DataType.PRECISION_NONE);
348
              attr.setScale(DataType.SCALE_NONE);
349
              break;
350 44678 jjdelcerro
            case DataTypes.BYTE:
351 44947 jjdelcerro
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
352 44678 jjdelcerro
              attr.setPrecision(DataType.BYTE_DEFAULT_PRECISION);
353
              attr.setScale(DataType.SCALE_NONE);
354
              break;
355
            case DataTypes.INT:
356 44947 jjdelcerro
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
357 44678 jjdelcerro
              attr.setPrecision(DataType.INT_DEFAULT_PRECISION);
358
              attr.setScale(DataType.SCALE_NONE);
359
              break;
360
            case DataTypes.LONG:
361 44947 jjdelcerro
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
362 44678 jjdelcerro
              attr.setPrecision(DataType.LONG_DEFAULT_PRECISION);
363
              attr.setScale(DataType.SCALE_NONE);
364
              break;
365
            case DataTypes.FLOAT:
366 44947 jjdelcerro
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
367 44678 jjdelcerro
              attr.setPrecision(DataType.FLOAT_DEFAULT_PRECISION);
368 44926 jjdelcerro
              attr.setScale(DataType.SCALE_NONE);
369 44678 jjdelcerro
              break;
370
            case DataTypes.DOUBLE:
371 44947 jjdelcerro
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
372 44678 jjdelcerro
              attr.setPrecision(DataType.DOUBLE_DEFAULT_PRECISION);
373 44926 jjdelcerro
              attr.setScale(DataType.SCALE_NONE);
374 44678 jjdelcerro
              break;
375 44926 jjdelcerro
            case DataTypes.DECIMAL:
376 44947 jjdelcerro
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
377 44926 jjdelcerro
              attr.setScale(rsMetadata.getScale(colIndex));
378
              attr.setPrecision(rsMetadata.getPrecision(colIndex));
379
              break;
380 43020 jjdelcerro
            case DataTypes.OBJECT:
381
                attr.setAdditionalInfo(
382
                        "SQLType",
383 45154 jjdelcerro
                        String.valueOf(rsMetadata.getColumnType(colIndex))
384 43020 jjdelcerro
                );
385
                attr.setAdditionalInfo(
386
                        "SQLTypeName",
387
                        rsMetadata.getColumnTypeName(colIndex)
388
                );
389
                break;
390
            case DataTypes.GEOMETRY:
391 43114 jjdelcerro
                this.fetchGeometryTypeAndSRS(attr, rsMetadata, colIndex);
392 43020 jjdelcerro
                break;
393
        }
394
        return attr;
395
    }
396
397
    protected int getDataTypeFromMetadata(
398
            ResultSetMetaData rsMetadata,
399
            int colIndex
400
        ) throws SQLException {
401
402
        switch (rsMetadata.getColumnType(colIndex)) {
403 44678 jjdelcerro
            case java.sql.Types.TINYINT:
404
                return DataTypes.BYTE;
405
406 44945 omartinez
            case java.sql.Types.SMALLINT:
407 43020 jjdelcerro
            case java.sql.Types.INTEGER:
408
                return DataTypes.INT;
409
410
            case java.sql.Types.BIGINT:
411
                return DataTypes.LONG;
412
413
            case java.sql.Types.REAL:
414 44678 jjdelcerro
            case java.sql.Types.FLOAT:
415
                return DataTypes.FLOAT;
416 43020 jjdelcerro
417
            case java.sql.Types.DOUBLE:
418
                return DataTypes.DOUBLE;
419
420 44678 jjdelcerro
            case java.sql.Types.NUMERIC:
421
            case java.sql.Types.DECIMAL:
422
                return DataTypes.DECIMAL;
423
424 43020 jjdelcerro
            case java.sql.Types.CHAR:
425
            case java.sql.Types.VARCHAR:
426
            case java.sql.Types.LONGVARCHAR:
427
                return DataTypes.STRING;
428
429
            case java.sql.Types.DATE:
430
                return DataTypes.DATE;
431
432
            case java.sql.Types.TIME:
433
                return DataTypes.TIME;
434
435
            case java.sql.Types.TIMESTAMP:
436
                return DataTypes.TIMESTAMP;
437
438
            case java.sql.Types.BOOLEAN:
439
            case java.sql.Types.BIT:
440
                return DataTypes.BOOLEAN;
441
442
            case java.sql.Types.BLOB:
443
            case java.sql.Types.BINARY:
444
            case java.sql.Types.LONGVARBINARY:
445
                return DataTypes.BYTEARRAY;
446
447
            default:
448
                String typeName = rsMetadata.getColumnTypeName(colIndex);
449
                if( "geometry".equalsIgnoreCase(typeName) ) {
450
                    return DataTypes.GEOMETRY;
451
                }
452
                return DataTypes.OBJECT;
453
        }
454
    }
455
456 43114 jjdelcerro
    /**
457
     * Inicializa el tipo, subtipo y SRS del attributo de tipo geometria.
458
     *
459
     * @param attr
460
     * @param rsMetadata
461
     * @param colIndex
462
     */
463
    protected void fetchGeometryTypeAndSRS(
464
            EditableFeatureAttributeDescriptor attr,
465 43020 jjdelcerro
            ResultSetMetaData rsMetadata,
466 43114 jjdelcerro
            int colIndex
467
        ) {
468
        if( attr.getType()!=DataTypes.GEOMETRY ) {
469
            return;
470
        }
471
        try {
472
            GeometryType geomType = GeometryLocator.getGeometryManager().getGeometryType(
473
                    Geometry.TYPES.GEOMETRY,
474
                    Geometry.SUBTYPES.GEOM2D
475
            );
476
            attr.setGeometryType(geomType);
477 44253 jjdelcerro
            attr.setSRS((IProjection)null);
478 43114 jjdelcerro
        } catch (Exception ex) {
479 44058 jjdelcerro
            LOGGER.warn("Can't get default geometry type.",ex);
480 43114 jjdelcerro
        }
481 43020 jjdelcerro
    }
482 43114 jjdelcerro
483 43020 jjdelcerro
}