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

History | View | Annotate | Download (18.6 KB)

1
/**
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
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
import org.gvsig.expressionevaluator.ExpressionBuilder;
38
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
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
43
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
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
47
import org.gvsig.fmap.geom.Geometry;
48
import org.gvsig.fmap.geom.GeometryLocator;
49
import org.gvsig.fmap.geom.type.GeometryType;
50
import static org.gvsig.fmap.dal.store.jdbc2.spi.operations.AbstractConnectionOperation.LOGGER;
51
import org.gvsig.tools.dataTypes.DataType;
52

    
53
@SuppressWarnings("UseSpecificCatch")
54
public class FetchFeatureTypeOperation extends AbstractConnectionOperation {
55
    protected final EditableFeatureType featureType;
56
    protected final TableReference table;
57
    protected final List<String> primaryKeys;
58
    protected final String defaultGeometryColumn;
59
    protected final IProjection crs;
60
    protected final int geometryType;
61
    protected final int geometrySubtype;
62

    
63
    public FetchFeatureTypeOperation(
64
            JDBCHelper helper
65
        ) {
66
        this(helper, null, null, null, null, null, Geometry.TYPES.UNKNOWN, Geometry.SUBTYPES.UNKNOWN);
67
    }
68
    
69
    public FetchFeatureTypeOperation(
70
            JDBCHelper helper,
71
            EditableFeatureType featureType,
72
            String defaultGeometryColumn,
73
            IProjection crs
74
        ) {
75
        this(helper, featureType, null, null, defaultGeometryColumn, crs, Geometry.TYPES.UNKNOWN, Geometry.SUBTYPES.UNKNOWN);
76
    }
77

    
78
    public FetchFeatureTypeOperation(
79
            JDBCHelper helper,
80
            EditableFeatureType featureType,
81
            TableReference table,
82
            List<String> primaryKeys,
83
            String defaultGeometryColumn,
84
            IProjection crs,
85
            int geometryType,
86
            int geometrySubtype
87
        ) {
88
        super(helper);
89
        this.featureType = featureType;
90
        this.table = table;
91
        this.primaryKeys = primaryKeys;
92
        this.defaultGeometryColumn = defaultGeometryColumn;
93
        this.crs = crs;
94
        this.geometryType = geometryType;
95
        this.geometrySubtype = geometrySubtype;
96
    }
97
    
98
    @Override
99
    public final Object perform(Connection conn) throws DataException {
100
        this.fetch(conn);
101
        return true;
102
    }
103
    
104
    protected TableReference getTable() {
105
        return this.table;
106
    }
107
    
108
    public void fetch(Connection conn) throws DataException {
109
        List<String> pks = this.primaryKeys;
110
        Statement st = null;
111
        ResultSet rs = null;
112
        try {
113
            if (CollectionUtils.isEmpty(pks)) {
114
                if (table.hasSubquery()) {
115
                    LOGGER.debug("Searching pk in a table with a subquery ("+table.toString()+".");
116
                }
117
                pks = this.getPrimaryKeysFromMetadata(conn, null, table.getSchema(), table.getTable());
118
                if (CollectionUtils.isEmpty(pks)) {
119
                    pks = getPrimaryKeysFromInformationSchema(conn);
120
                }
121
            }
122
            st = conn.createStatement();
123
            st.setFetchSize(1);
124
            rs = JDBCUtils.executeQuery(st, this.getSQLToRetrieveFirstRowOfTable());
125
            ResultSetMetaData rsMetadata = rs.getMetaData();
126

    
127
            fetchFeatureTypeFromMetadata(conn, rsMetadata, pks);
128

    
129
        } catch (SQLException ex) {
130
            throw new RuntimeException("Can't fecth feature type.",ex);
131
        } finally {
132
            JDBCUtils.closeQuietly(rs);
133
            JDBCUtils.closeQuietly(st);
134
        }
135
    }
136
    
137
    public String getSQLToRetrieveFirstRowOfTable() {
138
      JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
139
      sqlbuilder.select().column().all();
140
      sqlbuilder.select().from().table()
141
          .database(this.table.getDatabase())
142
          .schema(this.table.getSchema())
143
          .name(this.table.getTable());
144
//      sqlbuilder.select().from().subquery(this.table.getSubquery());
145
      sqlbuilder.select().limit(1);
146

    
147
      String sql = sqlbuilder.toString();
148
      return sql;
149
    }
150
    
151
    
152
    public void fetchFeatureTypeFromMetadata(Connection conn, ResultSetMetaData rsMetadata) throws SQLException {
153
        this.fetchFeatureTypeFromMetadata(conn, rsMetadata, new ArrayList<String>());
154
    }
155

    
156
    protected void fetchFeatureTypeFromMetadata(Connection conn, ResultSetMetaData rsMetadata, List<String> pks) throws SQLException {
157
        int i;
158
        int geometriesColumns = 0;
159
        String lastGeometry = null;
160

    
161
        EditableFeatureAttributeDescriptor attr;
162
        boolean firstGeometryAttrFound = false;
163
        for (i = 1; i <= rsMetadata.getColumnCount(); i++) {
164
            attr = getAttributeFromMetadata(featureType, conn, rsMetadata, i);
165
            if ( isInPrimaryKeys(pks,attr) ) {
166
                attr.setIsPrimaryKey(true);
167
            }
168
            if (attr.getType() == DataTypes.GEOMETRY) {
169
                geometriesColumns++;
170
                lastGeometry = attr.getName();
171
                // Set the default geometry attribute if it is the one
172
                // given as parameter or it is the first one, just in case.
173
                if (!firstGeometryAttrFound || StringUtils.equalsIgnoreCase(lastGeometry, defaultGeometryColumn)) {
174
                    firstGeometryAttrFound = true;
175
                    featureType.setDefaultGeometryAttributeName(lastGeometry);
176
                }
177
            }
178

    
179
        }
180
        if (StringUtils.isBlank(defaultGeometryColumn)) {
181
            if (geometriesColumns == 1) {
182
                featureType.setDefaultGeometryAttributeName(lastGeometry);
183
            }
184
        } else if (!StringUtils.equalsIgnoreCase(defaultGeometryColumn, featureType.getDefaultGeometryAttributeName())) {
185
            EditableFeatureAttributeDescriptor geomattr = featureType.getEditableAttributeDescriptor(defaultGeometryColumn);
186
            if (geomattr.getDataType().getType() != DataTypes.GEOMETRY) {
187
                geomattr.setDataType(DataTypes.GEOMETRY);
188
                geomattr.setGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
189
            }
190
            
191
        }
192
        
193
        if (featureType.getDefaultGeometryAttribute() != null) {
194
            EditableFeatureAttributeDescriptor attrGeom = (EditableFeatureAttributeDescriptor) featureType.getDefaultGeometryAttribute();
195
            if (crs != null) {
196
                attrGeom.setSRS(crs);
197
            }
198
            if (geometryType != Geometry.TYPES.UNKNOWN && geometrySubtype != Geometry.SUBTYPES.UNKNOWN) {
199
                attrGeom.setGeometryType(geometryType, geometrySubtype);
200
            } else if (geometryType != Geometry.TYPES.UNKNOWN) {
201
                attrGeom.setGeometryType(geometryType, attrGeom.getGeomType().getSubType());
202
            } else if (geometrySubtype != Geometry.SUBTYPES.UNKNOWN) {
203
                attrGeom.setGeometryType(attrGeom.getGeomType().getType(), geometrySubtype);
204
            }
205
        }
206
    }
207

    
208
    protected boolean isInPrimaryKeys(List<String> pks, EditableFeatureAttributeDescriptor attr) {
209
        if( pks == null || attr == null ) {
210
            return false;
211
        }
212
        // En algunos gestores de BBDD, los nombres obtenidos de las pks de los 
213
        // metadados no coinciden con los nombres de los campos ya que unos estan
214
        // en mayusculas y otros en minusculas, asi que en lugar de usar un "contains"
215
        // nos los recorremos y comparamos con IgnoreCase.
216
        for (String pk : pks) {
217
            if( StringUtils.equalsIgnoreCase(pk, attr.getName()) ) {
218
                return true;
219
            }
220
        }
221
        return false;        
222
    }
223
    
224
    protected List<String> getPrimaryKeysFromMetadata(
225
            Connection conn,
226
            String catalog,
227
            String schema,
228
            String table) throws SQLException {
229

    
230
        ResultSet rsPrimaryKeys = null;
231
        ResultSet rs = null;
232
        try {
233
            DatabaseMetaData metadata = conn.getMetaData();
234
            rs = metadata.getTables(catalog, schema, table, null);
235

    
236
            if (!rs.next()) {
237
                // No tables found with default values, ignoring catalog
238
                rs.close();
239
                catalog = null;
240
                schema = null;
241
                rs = metadata.getTables(catalog, schema, table, null);
242
                if (!rs.next()) {
243
                    // table not found
244
                    return null;
245
                } else if (rs.next()) {
246
                    // More that one, cant identify
247
                    return null;
248
                }
249

    
250
            } else if (rs.next()) {
251
                // More that one, cant identify
252
                return null;
253
            }
254
            rsPrimaryKeys = metadata.getPrimaryKeys(catalog, schema, table);
255
            List pks = new ArrayList();
256
            while (rsPrimaryKeys.next()) {
257
                pks.add(rsPrimaryKeys.getString("COLUMN_NAME"));
258
            }
259
            return pks;
260

    
261
        } catch (SQLException e) {
262
            return null;
263

    
264
        } finally {
265
            JDBCUtils.closeQuietly(rs);
266
            JDBCUtils.closeQuietly(rsPrimaryKeys);
267
        }
268

    
269
    }
270

    
271
    protected List<String> getPrimaryKeysFromInformationSchema(Connection conn) throws SQLException {
272

    
273
        String sql = getSQLToRetrievePrimaryKeysFromInformationSchema();
274

    
275
        Statement st = null;
276
        ResultSet rs = null;
277
        List<String> pks = new ArrayList();
278
        try {
279
            st = conn.createStatement();
280
            rs = JDBCUtils.executeQuery(st, sql);
281
            while (rs.next()) {
282
                pks.add(rs.getString(1));
283
            }
284
            if (pks.isEmpty()) {
285
                return null;
286
            }
287
            return pks;
288

    
289
        } catch (Exception ex) {
290
            return pks;
291
            
292
        } finally {
293
            JDBCUtils.closeQuietly(rs);
294
            JDBCUtils.closeQuietly(st);
295
        }
296
    }
297

    
298
    public String getSQLToRetrievePrimaryKeysFromInformationSchema() throws SQLException {
299
        JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
300
        ExpressionBuilder expbuilder = sqlbuilder.expression();
301

    
302
        sqlbuilder.select().column().name("COLUMN_NAME");
303
        sqlbuilder.select().column().name("CONSTRAINT_TYPE");
304
        sqlbuilder.select().from().custom(
305
                "INFORMATION_SCHEMA.table_constraints t_cons "
306
                + "inner join INFORMATION_SCHEMA.key_column_usage c on "
307
                + "c.constraint_catalog = t_cons.constraint_catalog and "
308
                + "c.table_schema = t_cons.table_schema and "
309
                + "c.table_name = t_cons.table_name and "
310
                + "c.constraint_name = t_cons.constraint_name "
311
        );
312
        sqlbuilder.select().where().set(
313
                expbuilder.like(
314
                        expbuilder.custom("c.TABLE_NAME"), 
315
                        expbuilder.constant(table.getTable())
316
                )
317
        );
318
        if( table.hasSchema() ) {
319
            sqlbuilder.select().where().and(
320
                    expbuilder.like(
321
                            expbuilder.custom("c.TABLE_SCHEMA"),
322
                            expbuilder.constant(table.getSchema())
323
                    )
324
            );
325
        }
326
//        if (catalog != null) {
327
//            sqlbuilder.select().where().and(
328
//                    expbuilder.like(
329
//                            expbuilder.custom("c.CONSTRAINT_CATALOG"),
330
//                            expbuilder.constant(catalog)
331
//                    )
332
//            );
333
//        }
334
        sqlbuilder.select().where().and(
335
                expbuilder.eq(
336
                        expbuilder.column("CONSTRAINT_TYPE"),
337
                        expbuilder.constant("PRIMARY KEY")
338
                )
339
        );
340
        return sqlbuilder.toString();
341
    }
342
    
343
    
344
    protected EditableFeatureAttributeDescriptor getAttributeFromMetadata(
345
            EditableFeatureType type,
346
            Connection conn,
347
            ResultSetMetaData rsMetadata,
348
            int colIndex
349
        ) throws SQLException {
350

    
351
        EditableFeatureAttributeDescriptor attr = type.add(
352
                rsMetadata.getColumnName(colIndex),
353
                this.getDataTypeFromMetadata(rsMetadata, colIndex)
354
        );
355
        attr.setAllowNull(
356
            rsMetadata.isNullable(colIndex) == ResultSetMetaData.columnNullable
357
        );
358
        attr.setIsAutomatic(rsMetadata.isAutoIncrement(colIndex));
359
        attr.setIsReadOnly(rsMetadata.isReadOnly(colIndex));
360
        switch(attr.getType()) {
361
            case DataTypes.STRING:
362
              attr.setSize(rsMetadata.getPrecision(colIndex));
363
              attr.setPrecision(DataType.PRECISION_NONE);
364
              attr.setScale(DataType.SCALE_NONE);
365
              break;
366
            case DataTypes.BYTE:
367
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
368
              attr.setPrecision(DataType.BYTE_DEFAULT_PRECISION);
369
              attr.setScale(DataType.SCALE_NONE);
370
              break;
371
            case DataTypes.INT:
372
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
373
              attr.setPrecision(DataType.INT_DEFAULT_PRECISION);
374
              attr.setScale(DataType.SCALE_NONE);
375
              break;
376
            case DataTypes.LONG:
377
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
378
              attr.setPrecision(DataType.LONG_DEFAULT_PRECISION);
379
              attr.setScale(DataType.SCALE_NONE);
380
              break;
381
            case DataTypes.FLOAT:
382
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
383
              attr.setPrecision(DataType.FLOAT_DEFAULT_PRECISION);
384
              attr.setScale(DataType.SCALE_NONE);
385
              break;
386
            case DataTypes.DOUBLE:
387
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
388
              attr.setPrecision(DataType.DOUBLE_DEFAULT_PRECISION);
389
              attr.setScale(DataType.SCALE_NONE);
390
              break;
391
            case DataTypes.DECIMAL:
392
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
393
              attr.setScale(rsMetadata.getScale(colIndex));
394
              attr.setPrecision(rsMetadata.getPrecision(colIndex));
395
              break;
396
            case DataTypes.OBJECT:
397
                attr.setAdditionalInfo(
398
                        "SQLType",
399
                        String.valueOf(rsMetadata.getColumnType(colIndex))
400
                );
401
                attr.setAdditionalInfo(
402
                        "SQLTypeName",
403
                        rsMetadata.getColumnTypeName(colIndex)
404
                );
405
                break;
406
            case DataTypes.GEOMETRY:
407
                this.fetchGeometryTypeAndSRS(attr, rsMetadata, colIndex);
408
                break;
409
        }
410
        return attr;
411
    }
412

    
413
    protected int getDataTypeFromMetadata(
414
            ResultSetMetaData rsMetadata,
415
            int colIndex
416
        ) throws SQLException {
417

    
418
        switch (rsMetadata.getColumnType(colIndex)) {
419
            case java.sql.Types.TINYINT:
420
                return DataTypes.BYTE;
421

    
422
            case java.sql.Types.SMALLINT:
423
            case java.sql.Types.INTEGER:
424
                return DataTypes.INT;
425

    
426
            case java.sql.Types.BIGINT:
427
                return DataTypes.LONG;
428

    
429
            case java.sql.Types.REAL:
430
            case java.sql.Types.FLOAT:
431
                return DataTypes.FLOAT;
432

    
433
            case java.sql.Types.DOUBLE:
434
                return DataTypes.DOUBLE;
435

    
436
            case java.sql.Types.NUMERIC:
437
            case java.sql.Types.DECIMAL:
438
                return DataTypes.DECIMAL;
439

    
440
            case java.sql.Types.CHAR:
441
            case java.sql.Types.VARCHAR:
442
            case java.sql.Types.LONGVARCHAR:
443
            case java.sql.Types.CLOB:
444
                return DataTypes.STRING;
445

    
446
            case java.sql.Types.DATE:
447
                return DataTypes.DATE;
448

    
449
            case java.sql.Types.TIME:
450
                return DataTypes.TIME;
451

    
452
            case java.sql.Types.TIMESTAMP:
453
                return DataTypes.TIMESTAMP;
454

    
455
            case java.sql.Types.BOOLEAN:
456
            case java.sql.Types.BIT:
457
                return DataTypes.BOOLEAN;
458

    
459
            case java.sql.Types.BLOB:
460
            case java.sql.Types.BINARY:
461
            case java.sql.Types.LONGVARBINARY:
462
                return DataTypes.BYTEARRAY;
463

    
464
            default:
465
                String typeName = rsMetadata.getColumnTypeName(colIndex);
466
                if( "geometry".equalsIgnoreCase(typeName) ) {
467
                    return DataTypes.GEOMETRY;
468
                }
469
                return DataTypes.OBJECT;
470
        }
471
    }
472

    
473
    /**
474
     * Inicializa el tipo, subtipo y SRS del attributo de tipo geometria.
475
     * 
476
     * @param attr
477
     * @param rsMetadata
478
     * @param colIndex 
479
     */
480
    protected void fetchGeometryTypeAndSRS(
481
            EditableFeatureAttributeDescriptor attr,
482
            ResultSetMetaData rsMetadata,
483
            int colIndex
484
        ) {
485
        if( attr.getType()!=DataTypes.GEOMETRY ) {
486
            return;
487
        }
488
        try {
489
            GeometryType geomType = GeometryLocator.getGeometryManager().getGeometryType(
490
                    Geometry.TYPES.GEOMETRY,
491
                    Geometry.SUBTYPES.GEOM2D
492
            );
493
            attr.setGeometryType(geomType);
494
            attr.setSRS((IProjection)null);
495
        } catch (Exception ex) {
496
            LOGGER.warn("Can't get default geometry type.",ex);
497
        }
498
    }
499
    
500
}