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

History | View | Annotate | Download (17.7 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
    private final EditableFeatureType featureType;
56
    protected final TableReference table;
57
    private final List<String> primaryKeys;
58
    private final String defaultGeometryColumn;
59
    private final IProjection crs;
60

    
61
    public FetchFeatureTypeOperation(
62
            JDBCHelper helper
63
        ) {
64
        this(helper, null, null, null, null, null);
65
    }
66
    
67
    public FetchFeatureTypeOperation(
68
            JDBCHelper helper,
69
            EditableFeatureType featureType,
70
            String defaultGeometryColumn,
71
            IProjection crs
72
        ) {
73
        this(helper, featureType, null, null, defaultGeometryColumn, crs);
74
    }
75

    
76
    public FetchFeatureTypeOperation(
77
            JDBCHelper helper,
78
            EditableFeatureType featureType,
79
            TableReference table,
80
            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
        this.fetch(conn);
95
        return true;
96
    }
97
    
98
    protected TableReference getTable() {
99
        return this.table;
100
    }
101
    
102
    public void fetch(Connection conn) throws DataException {
103
        List<String> pks = this.primaryKeys;
104
        Statement st = null;
105
        ResultSet rs = null;
106
        try {
107
            if (CollectionUtils.isEmpty(pks)) {
108
                if (table.hasSubquery()) {
109
                    LOGGER.info("Searching pk in a table with a subquery ("+table.toString()+".");
110
                }
111
                pks = this.getPrimaryKeysFromMetadata(conn, null, table.getSchema(), table.getTable());
112
                if (CollectionUtils.isEmpty(pks)) {
113
                    pks = getPrimaryKeysFromInformationSchema(conn);
114
                }
115
            }
116
            st = conn.createStatement();
117
            st.setFetchSize(1);
118
            rs = JDBCUtils.executeQuery(st, this.getSQLToRetrieveFirstRowOfTable());
119
            ResultSetMetaData rsMetadata = rs.getMetaData();
120

    
121
            fetchFeatureTypeFromMetadata(conn, rsMetadata, pks);
122

    
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
    }
130
    
131
    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
      sqlbuilder.select().from().subquery(this.table.getSubquery());
139
      sqlbuilder.select().limit(1);
140

    
141
      String sql = sqlbuilder.toString();
142
      return sql;
143
    }
144
    
145
    
146
    public void fetchFeatureTypeFromMetadata(Connection conn, ResultSetMetaData rsMetadata) throws SQLException {
147
        this.fetchFeatureTypeFromMetadata(conn, rsMetadata, new ArrayList<String>());
148
    }
149

    
150
    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
                if (!firstGeometryAttrFound || StringUtils.equalsIgnoreCase(lastGeometry, defaultGeometryColumn)) {
168
                    firstGeometryAttrFound = true;
169
                    featureType.setDefaultGeometryAttributeName(lastGeometry);
170
                }
171
            }
172

    
173
        }
174
        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
        }
186
            
187
        if (crs != null && featureType.getDefaultGeometryAttribute() != null) {
188
            ((EditableFeatureAttributeDescriptor) featureType.getDefaultGeometryAttribute()).setSRS(crs);
189
        }
190
    }
191

    
192
    protected boolean isInPrimaryKeys(List<String> pks, EditableFeatureAttributeDescriptor attr) {
193
        if( pks == null || attr == null ) {
194
            return false;
195
        }
196
        // 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
    }
207
    
208
    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
    protected List<String> getPrimaryKeysFromInformationSchema(Connection conn) throws SQLException {
256

    
257
        String sql = getSQLToRetrievePrimaryKeysFromInformationSchema();
258

    
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
    public String getSQLToRetrievePrimaryKeysFromInformationSchema() throws SQLException {
283
        JDBCSQLBuilderBase sqlbuilder = this.createSQLBuilder();
284
        ExpressionBuilder expbuilder = sqlbuilder.expression();
285

    
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
                expbuilder.like(
298
                        expbuilder.custom("c.TABLE_NAME"), 
299
                        expbuilder.constant(table.getTable())
300
                )
301
        );
302
        if( table.hasSchema() ) {
303
            sqlbuilder.select().where().and(
304
                    expbuilder.like(
305
                            expbuilder.custom("c.TABLE_SCHEMA"),
306
                            expbuilder.constant(table.getSchema())
307
                    )
308
            );
309
        }
310
//        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
        sqlbuilder.select().where().and(
319
                expbuilder.eq(
320
                        expbuilder.column("CONSTRAINT_TYPE"),
321
                        expbuilder.constant("PRIMARY KEY")
322
                )
323
        );
324
        return sqlbuilder.toString();
325
    }
326
    
327
    
328
    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
            case DataTypes.STRING:
346
              attr.setSize(rsMetadata.getPrecision(colIndex));
347
              attr.setPrecision(DataType.PRECISION_NONE);
348
              attr.setScale(DataType.SCALE_NONE);
349
              break;
350
            case DataTypes.BYTE:
351
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
352
              attr.setPrecision(DataType.BYTE_DEFAULT_PRECISION);
353
              attr.setScale(DataType.SCALE_NONE);
354
              break;
355
            case DataTypes.INT:
356
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
357
              attr.setPrecision(DataType.INT_DEFAULT_PRECISION);
358
              attr.setScale(DataType.SCALE_NONE);
359
              break;
360
            case DataTypes.LONG:
361
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
362
              attr.setPrecision(DataType.LONG_DEFAULT_PRECISION);
363
              attr.setScale(DataType.SCALE_NONE);
364
              break;
365
            case DataTypes.FLOAT:
366
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
367
              attr.setPrecision(DataType.FLOAT_DEFAULT_PRECISION);
368
              attr.setScale(DataType.SCALE_NONE);
369
              break;
370
            case DataTypes.DOUBLE:
371
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
372
              attr.setPrecision(DataType.DOUBLE_DEFAULT_PRECISION);
373
              attr.setScale(DataType.SCALE_NONE);
374
              break;
375
            case DataTypes.DECIMAL:
376
              attr.setDisplaySize(rsMetadata.getColumnDisplaySize(colIndex));
377
              attr.setScale(rsMetadata.getScale(colIndex));
378
              attr.setPrecision(rsMetadata.getPrecision(colIndex));
379
              break;
380
            case DataTypes.OBJECT:
381
                attr.setAdditionalInfo(
382
                        "SQLType",
383
                        String.valueOf(rsMetadata.getColumnType(colIndex))
384
                );
385
                attr.setAdditionalInfo(
386
                        "SQLTypeName",
387
                        rsMetadata.getColumnTypeName(colIndex)
388
                );
389
                break;
390
            case DataTypes.GEOMETRY:
391
                this.fetchGeometryTypeAndSRS(attr, rsMetadata, colIndex);
392
                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
            case java.sql.Types.TINYINT:
404
                return DataTypes.BYTE;
405

    
406
            case java.sql.Types.SMALLINT:
407
            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
            case java.sql.Types.FLOAT:
415
                return DataTypes.FLOAT;
416

    
417
            case java.sql.Types.DOUBLE:
418
                return DataTypes.DOUBLE;
419

    
420
            case java.sql.Types.NUMERIC:
421
            case java.sql.Types.DECIMAL:
422
                return DataTypes.DECIMAL;
423

    
424
            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
    /**
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
            ResultSetMetaData rsMetadata,
466
            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
            attr.setSRS((IProjection)null);
478
        } catch (Exception ex) {
479
            LOGGER.warn("Can't get default geometry type.",ex);
480
        }
481
    }
482
    
483
}