001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/datastore/sql/generic/GenericSQLTransaction.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 This file is part of deegree. 004 Copyright (C) 2001-2008 by: 005 Department of Geography, University of Bonn 006 http://www.giub.uni-bonn.de/deegree/ 007 lat/lon GmbH 008 http://www.lat-lon.de 009 010 This library is free software; you can redistribute it and/or 011 modify it under the terms of the GNU Lesser General Public 012 License as published by the Free Software Foundation; either 013 version 2.1 of the License, or (at your option) any later version. 014 This library is distributed in the hope that it will be useful, 015 but WITHOUT ANY WARRANTY; without even the implied warranty of 016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 Lesser General Public License for more details. 018 You should have received a copy of the GNU Lesser General Public 019 License along with this library; if not, write to the Free Software 020 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 021 Contact: 022 023 Andreas Poth 024 lat/lon GmbH 025 Aennchenstr. 19 026 53177 Bonn 027 Germany 028 E-Mail: poth@lat-lon.de 029 030 Prof. Dr. Klaus Greve 031 Department of Geography 032 University of Bonn 033 Meckenheimer Allee 166 034 53115 Bonn 035 Germany 036 E-Mail: greve@giub.uni-bonn.de 037 ---------------------------------------------------------------------------*/ 038 039 package org.deegree.io.datastore.sql.generic; 040 041 import java.sql.Connection; 042 import java.sql.ResultSet; 043 import java.sql.ResultSetMetaData; 044 import java.sql.SQLException; 045 import java.sql.Statement; 046 import java.util.HashMap; 047 import java.util.List; 048 import java.util.Map; 049 050 import org.deegree.framework.log.ILogger; 051 import org.deegree.framework.log.LoggerFactory; 052 import org.deegree.i18n.Messages; 053 import org.deegree.io.DBConnectionPool; 054 import org.deegree.io.DBPoolException; 055 import org.deegree.io.JDBCConnection; 056 import org.deegree.io.datastore.DatastoreException; 057 import org.deegree.io.datastore.FeatureId; 058 import org.deegree.io.datastore.schema.MappedFeatureType; 059 import org.deegree.io.datastore.sql.AbstractSQLDatastore; 060 import org.deegree.io.datastore.sql.SQLDatastoreConfiguration; 061 import org.deegree.io.datastore.sql.TableAliasGenerator; 062 import org.deegree.io.datastore.sql.transaction.SQLTransaction; 063 import org.deegree.io.quadtree.DBQuadtree; 064 import org.deegree.io.quadtree.IndexException; 065 import org.deegree.model.feature.Feature; 066 import org.deegree.model.feature.FeatureProperty; 067 import org.deegree.model.filterencoding.Filter; 068 import org.deegree.model.spatialschema.Envelope; 069 import org.deegree.model.spatialschema.GeometryException; 070 import org.deegree.ogcbase.PropertyPath; 071 072 /** 073 * Special transaction implementation for the {@link GenericSQLDatastore}. 074 * <p> 075 * Please note that the quadtree management isn't finished yet. 076 * </p><p> 077 * What should work: 078 * <ul> 079 * <li>inserting of new features</li> 080 * <li>deleting of features</li> 081 * <li>updating features (unless the geometry property is changed)</li> 082 * </ul> 083 * </p><p> 084 * What definitely won't work: 085 * <ul> 086 * <li>updating geometry properties will most probably break the index</li> 087 * </ul> 088 * </p> 089 * 090 * @see org.deegree.io.quadtree 091 * 092 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 093 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a> 094 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 095 * @author last edited by: $Author: apoth $ 096 * 097 * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $ 098 */ 099 public class GenericSQLTransaction extends SQLTransaction { 100 101 private static final ILogger LOG = LoggerFactory.getLogger( GenericSQLTransaction.class ); 102 103 private String indexName; 104 105 /** 106 * saves versioninfos about the different quadtrees. 107 */ 108 public static HashMap<String, String> quadTreeVersionInfo = new HashMap<String, String>(); 109 110 /** 111 * 112 * @param ds 113 * @param aliasGenerator 114 * @param conn 115 * @throws DatastoreException 116 */ 117 GenericSQLTransaction( AbstractSQLDatastore ds, TableAliasGenerator aliasGenerator, Connection conn ) 118 throws DatastoreException { 119 super( ds, aliasGenerator, conn ); 120 } 121 122 @Override 123 public int performDelete( MappedFeatureType mappedFeatureType, Filter filter, String lockId ) 124 throws DatastoreException { 125 assert !mappedFeatureType.isAbstract(); 126 127 if ( !mappedFeatureType.isDeletable() ) { 128 String msg = Messages.getMessage( "DATASTORE_FT_NOT_DELETABLE", mappedFeatureType.getName() ); 129 throw new DatastoreException( msg ); 130 } 131 // QualifiedName qName = mappedFeatureType.getName(); 132 // if ( qName == null ) { 133 // LOG.logDebug( "The mappedfeauterType's qname is null, this cannot be!" ); 134 // } 135 // Query q = Query.create( mappedFeatureType.getName(), filter ); 136 // if ( q == null ) { 137 // LOG.logDebug( "The query created from the qname and filter is null, this cannot be!" ); 138 // } 139 // FeatureCollection fc = null; 140 // try { 141 // fc = getDatastore().performQuery( q, new MappedFeatureType[] { mappedFeatureType } ); 142 // } catch ( UnknownCRSException e ) { 143 // throw new DatastoreException( e ); 144 // } 145 146 List<FeatureId> featureList = determineAffectedAndModifiableFIDs( mappedFeatureType, filter, lockId ); 147 148 SQLDatastoreConfiguration config = (SQLDatastoreConfiguration) getDatastore().getConfiguration(); 149 JDBCConnection jdbc = config.getJDBCConnection(); 150 String table = mappedFeatureType.getTable(); 151 String version = getQTVersion( table, jdbc ); 152 LOG.logDebug( "Found quadtree version: " + version ); 153 try { 154 for ( FeatureId fID : featureList ) { 155 int fk_index = loadIndexMetadata( jdbc, table ); 156 Object rawId = FeatureId.removeFIDPrefix( fID.getAsString(), mappedFeatureType.getGMLId() ); 157 if ( rawId instanceof Integer ) { 158 DBQuadtree<Integer> qt = null; 159 try { 160 qt = new DBQuadtree<Integer>( fk_index, indexName, jdbc, version ); 161 qt.deleteItem( (Integer) rawId ); 162 } finally { 163 if ( qt != null ) { 164 qt.releaseConnection(); 165 } 166 } 167 } else if ( rawId instanceof String ) { 168 DBQuadtree<String> qt = null; 169 try { 170 qt = new DBQuadtree<String>( fk_index, indexName, jdbc, version ); 171 qt.deleteItem( (String) rawId ); 172 } finally { 173 if ( qt != null ) { 174 qt.releaseConnection(); 175 } 176 } 177 } 178 } 179 } catch ( IndexException e ) { 180 LOG.logError( e.getMessage(), e ); 181 throw new DatastoreException( e.getMessage(), e ); 182 } 183 184 super.performDelete( mappedFeatureType, filter, lockId ); 185 186 return featureList.size(); 187 } 188 189 @Override 190 public List<FeatureId> performInsert( List<Feature> features ) 191 throws DatastoreException { 192 List<FeatureId> fids = super.performInsert( features ); 193 194 // update index 195 try { 196 SQLDatastoreConfiguration config = (SQLDatastoreConfiguration) getDatastore().getConfiguration(); 197 JDBCConnection jdbc = config.getJDBCConnection(); 198 for ( int i = 0; i < features.size(); i++ ) { 199 Envelope env = features.get( i ).getBoundedBy(); 200 if ( env != null ) { 201 MappedFeatureType mft = datastore.getFeatureType( features.get( i ).getFeatureType().getName() ); 202 203 String table = mft.getTable(); 204 String version = getQTVersion( table, jdbc ); 205 int fk_index = loadIndexMetadata( jdbc, table ); 206 Object rawId = FeatureId.removeFIDPrefix( fids.get( i ).getAsString(), mft.getGMLId() ); 207 if ( rawId instanceof String ) { 208 DBQuadtree<String> qt = null; 209 try { 210 qt = new DBQuadtree<String>( fk_index, indexName, jdbc, version ); 211 qt.insert( (String) rawId, env ); 212 } finally { 213 if ( qt != null ) { 214 qt.releaseConnection(); 215 } 216 } 217 } else if ( rawId instanceof Integer ) { 218 DBQuadtree<Integer> qt = null; 219 try { 220 qt = new DBQuadtree<Integer>( fk_index, indexName, jdbc, version ); 221 qt.insert( (Integer) rawId, env ); 222 } finally { 223 if ( qt != null ) { 224 qt.releaseConnection(); 225 } 226 } 227 228 } 229 } 230 } 231 } catch ( IndexException e ) { 232 LOG.logError( e.getMessage(), e ); 233 throw new DatastoreException( e.getMessage(), e ); 234 } catch ( GeometryException e ) { 235 LOG.logError( e.getMessage(), e ); 236 throw new DatastoreException( e.getMessage(), e ); 237 } 238 return fids; 239 } 240 241 /** 242 * @param table 243 * to open a quadtree for. 244 * @return the version of the quadtree used. 245 */ 246 private String getQTVersion( String table, JDBCConnection jdbc ) { 247 String version = "1.0.0"; 248 if ( quadTreeVersionInfo.containsKey( table ) && quadTreeVersionInfo.get( table ) != null ) { 249 LOG.logDebug( "Retrieved the quatdree version info for table: " + table + " from cache." ); 250 version = quadTreeVersionInfo.get( table ); 251 } else { 252 Connection con = null; 253 DBConnectionPool pool = null; 254 Statement stmt = null; 255 ResultSet rs = null; 256 pool = DBConnectionPool.getInstance(); 257 StringBuilder sb = new StringBuilder( 400 ); 258 sb.append( "SELECT fk_indextree FROM tab_deegree_idx WHERE " ); 259 sb.append( "column_name = 'geometry' AND " ); 260 sb.append( "table_name = '" ).append( table.toLowerCase() ).append( "'" ); 261 262 LOG.logDebug( "Get Index Metadata sql statement:\n", sb ); 263 try { 264 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 265 stmt = con.createStatement(); 266 rs = stmt.executeQuery( sb.toString() ); 267 String tableID = null; 268 if ( rs.next() ) { 269 tableID = rs.getString( 1 ); 270 } 271 if ( tableID != null ) { 272 sb = new StringBuilder( 400 ); 273 sb.append( "SELECT * FROM tab_quadtree WHERE " ); 274 sb.append( "fk_root = '" ).append( tableID.trim() ).append( "'" ); 275 if ( rs != null ) { 276 rs.close(); 277 } 278 if ( stmt != null ) { 279 stmt.close(); 280 } 281 stmt = con.createStatement(); 282 rs = stmt.executeQuery( sb.toString() ); 283 if ( rs.next() ) { 284 boolean hasVersion = false; 285 ResultSetMetaData md = rs.getMetaData(); 286 int numberOfColumns = md.getColumnCount(); 287 System.out.println( "Columnecount: " + numberOfColumns ); 288 for ( int i = 1; i <= numberOfColumns && !hasVersion; i++ ) { 289 String tmp = md.getColumnName( i ); 290 LOG.logDebug( "Found columnname: " + tmp ); 291 if ( tmp != null ) { 292 if ( "version".equalsIgnoreCase( tmp.trim() ) ) { 293 hasVersion = true; 294 version = rs.getString( i ); 295 LOG.logDebug( "Found a version column, setting version to: " + rs.getString( i ) ); 296 } 297 } 298 } 299 if ( !hasVersion ) { 300 try { 301 LOG.logInfo( "Found no Version Column in the TAB_QUADTREE table, assuming version 1.0.0, and adding the version column." ); 302 if ( rs != null ) { 303 rs.close(); 304 } 305 if ( stmt != null ) { 306 stmt.close(); 307 } 308 stmt = con.createStatement(); 309 rs = stmt.executeQuery( "ALTER TABLE TAB_QUADTREE ADD version VARCHAR(15)" ); 310 rs.close(); 311 stmt.close(); 312 } catch ( SQLException e ) { 313 if ( rs != null ) { 314 rs.close(); 315 } 316 if ( stmt != null ) { 317 stmt.close(); 318 } 319 LOG.logError( "An error occurred while trying to insert a new 'version' column in the database: " + e.getMessage(), 320 e ); 321 } 322 } 323 } 324 } else { 325 LOG.logError( "Could not find the foreign key (fk_root) of the table: '" + table 326 + "' is your database is set up correct?" ); 327 } 328 } catch ( SQLException e ) { 329 LOG.logError( "An error occurred while determening version of quadtree, therefore setting version to '1.0.0'. Errormessage: " + e.getMessage(), 330 e ); 331 } catch ( DBPoolException e ) { 332 LOG.logError( "An error occurred while acquiring connection to the database to determine version of quadtree, therefore setting version to '1.0.0'. Errormessage: " + e.getMessage(), 333 e ); 334 } finally { 335 quadTreeVersionInfo.put( table, version ); 336 try { 337 if ( rs != null ) { 338 rs.close(); 339 } 340 if ( stmt != null ) { 341 stmt.close(); 342 } 343 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 344 } catch ( SQLException e ) { 345 LOG.logError( "Could not close ResultSet or Statement because: " + e.getMessage() ); 346 } catch ( DBPoolException e ) { 347 LOG.logError( "Could not reslease connection because: " + e.getMessage() ); 348 } 349 } 350 } 351 352 // } catch ( SQLException e ) { 353 // String msg = e.getMessage(); 354 // if( msg != null && msg.contains( " ) 355 // } 356 357 return version; 358 } 359 360 @Override 361 public int performUpdate( MappedFeatureType mappedFeatureType, Feature replacementFeature, Filter filter, 362 String lockId ) 363 throws DatastoreException { 364 int cnt = super.performUpdate( mappedFeatureType, replacementFeature, filter, lockId ); 365 366 // update index 367 368 return cnt; 369 } 370 371 @Override 372 public int performUpdate( MappedFeatureType mappedFeatureType, Map<PropertyPath, FeatureProperty> replacementProps, 373 Filter filter, String lockId ) 374 throws DatastoreException { 375 376 int cnt = super.performUpdate( mappedFeatureType, replacementProps, filter, lockId ); 377 378 // update index 379 380 return cnt; 381 } 382 383 /** 384 * loads the metadata of an Index from the TAB_DEEGREE_IDX table 385 * 386 * @param jdbc 387 * database connection information 388 * @param table 389 * name of the table containing a featuretypes data 390 * 391 * @return FK to the index 392 * @throws IndexException 393 */ 394 private int loadIndexMetadata( JDBCConnection jdbc, String table ) 395 throws IndexException { 396 int fk_indexTree = -1; 397 Connection con = null; 398 DBConnectionPool pool = null; 399 Statement stmt = null; 400 ResultSet rs = null; 401 try { 402 pool = DBConnectionPool.getInstance(); 403 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 404 405 StringBuilder sb = new StringBuilder( 400 ); 406 sb.append( "Select INDEX_NAME, FK_INDEXTREE from TAB_DEEGREE_IDX where " ); 407 sb.append( "column_name = 'geometry' AND " ); 408 sb.append( "table_name = '" ).append( table.toLowerCase() ).append( "'" ); 409 410 LOG.logDebug( "Get Index Metadata sql statement:\n", sb ); 411 412 stmt = con.createStatement(); 413 rs = stmt.executeQuery( sb.toString() ); 414 415 if ( rs.next() ) { 416 indexName = rs.getString( "INDEX_NAME" ); 417 fk_indexTree = rs.getInt( "FK_INDEXTREE" ); 418 } else { 419 throw new IndexException( "could not read index metadata" ); 420 } 421 } catch ( DBPoolException e ) { 422 throw new IndexException( "could not load quadtree definition from database", e ); 423 } catch ( SQLException e ) { 424 throw new IndexException( "could not load quadtree definition from database", e ); 425 } finally { 426 try { 427 if ( rs != null ) { 428 rs.close(); 429 } 430 if ( stmt != null ) { 431 stmt.close(); 432 } 433 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() ); 434 } catch ( SQLException e ) { 435 LOG.logError( "Could not close Set or Statement because: " + e.getMessage() ); 436 } catch ( DBPoolException e ) { 437 LOG.logError( "Could not reslease connection because: " + e.getMessage() ); 438 } 439 } 440 return fk_indexTree; 441 } 442 }