001 /*
002 * 04/04/2002 - 23:12:27
003 *
004 * $RCSfile: DatabaseImpl.java,v $ - JDBF Object Relational mapping system
005 * Copyright (C) 2002 JDBF Development Team
006 
007 * http://jdbf.sourceforge.net
008 *
009 * This program is free software; you can redistribute it and/or
010 * modify it under the terms of the GNU Lesser General Public License
011 * as published by the Free Software Foundation; either version 2
012 * of the License, or (at your option) any later version.
013 *
014 * This program 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
017 * GNU Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public License
020 * along with this program; if not, write to the Free Software
021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
022 */
023 /*
024  $Id: DatabaseImpl.java,v 1.15 2004/06/28 22:11:16 gmartone Exp $
025 */
026 package org.jdbf.engine.database;
027 import java.sql.*;
028 import java.util.ArrayList;
029 import java.util.logging.Level;
030 import org.jdbf.castor.Messages;
031 import org.jdbf.engine.basic.*;
032 import org.jdbf.engine.caching.CacheException;
033 import org.jdbf.engine.caching.CacheManager;
034 import org.jdbf.engine.configuration.*;
035 import org.jdbf.engine.criteria.*;
036 import org.jdbf.engine.mapping.*;
037 import org.jdbf.engine.keygen.*;
038 import org.jdbf.engine.sql.*;
039 import org.jdbf.engine.sql.connection.*;
040 import org.jdbf.engine.repository.*;
041 import org.jdbf.engine.transaction.*;
042 /**
043  <code>DatabaseImpl</code> is the class that handles the operations against 
044  * database. DatabaseImpl handles the operations on transaction.
045  * These operations are:<br>
046  <li>begin</li>
047  <li>close</li>
048  <li>commit</li>
049  <li>rollback</li>
050  
051  @author Giovanni Martone
052  @version $Revision: 1.15 $
053  * last changed by $Author: gmartone $
054  *
055  */
056 public class DatabaseImpl extends DatabaseCore  {
057     
058     /** Transaction object */
059     private Transaction transaction;
060     
061     /** ConnectionManager object */
062     private ConnectionManager connectionManager;
063     /** List of connections affected in operations that must be commited */
064     private ArrayList dbs;
065     
066         
067     /**
068      * Creates the DatabaseImpl object, creating and loading 
069      * a ConnectionManager object
070      *
071      @param fileName name of configuration file
072      @param repFactory RepositoryFactory object
073      @param cacheMan CacheManager object
074      @throws Exception
075      *
076      */                      
077     public DatabaseImpl(String fileName,RepositoryFactory repFactory,
078                         CacheManager cacheMan)
079       throws Exception {
080     super(repFactory,cacheMan);
081   
082     dbs = new ArrayList();
083     ConfigurationImpl conf = (ConfigurationImpl)ConfigurationBuilder.build(fileName);
084     connectionManager = new ConnectionManager();
085     connectionManager.setConfiguration(conf);    
086     }
087    
088      
089     /**
090      * Add a connection that has been used to list 
091      * to be processed in commit or rollback operations
092      *
093      @param conn to add
094      *
095      */
096     private void addConnection(Connection conn){
097         
098     if(!dbs.contains(conn))
099         dbs.add(conn);
100     }
101     
102     /**
103      * Begin transaction
104      *
105      @throws TransactionException
106      *
107      */
108     public void beginTransaction() throws TransactionException{
109         
110     synchronized(this){
111         begin();
112     }
113     }
114     
115     /** 
116      * Begin transaction 
117      *
118      @throws TransactionException if transaction is already used
119      *
120      */
121     public synchronized void begin() throws TransactionException{
122       
123         logger.log(Level.INFO,Messages.message("Database.beginTx"));
124         if(transaction != null && transaction.isOpen())
125           logger.throwing(className,"begin()",
126               new TransactionException(Messages.message("transaction.txInProgress")
127             ));
128                           
129     transaction = new TransactionImpl();  
130     }
131         
132     /**
133      * Close transaction
134      *
135      */
136     public synchronized void close(){          
137     //finalize the transaction
138     transaction = null;
139     logger.log(Level.INFO,Messages.message("Database.closedTx"));
140     }
141     
142     
143     /**
144      * Commit transaction of all database affected 
145      * in sql operations
146      */
147     public void commitTransaction(){
148         
149     try{
150         synchronized(this){
151             for(int i = 0; i < dbs.size(); i++){
152             Connection conn = (Connection)dbs.get(i);
153                 commit(conn);
154         }
155         }
156     }
157     catch(Exception e){      
158         logger.log(Level.SEVERE,e.getMessage());
159     }
160     }
161     
162     
163     /** 
164      * Commit transaction.
165      *
166      <b>Not use this method, you must use commitTransaction method!</b>
167      *
168      @param connection to commit
169      @throws TransactionException
170      *
171      */
172     public synchronized void commit(Connection connection
173       throws TransactionException{
174               
175     transaction.commit(connection);
176     logger.log(Level.INFO,Messages.message("Database.committedTx"));
177     }
178   
179   
180     /**
181      * Create OID using a keyGenerator specified in typeKeyGen.
182      *
183      @param view RepositoryView object
184      @param typeKeyGen type of key generator
185      @param conn  Connection object.
186      @return Object OID created
187      @exception KeyGenerationExcpetion if error occurs
188      *
189      */
190      private Object createOID(RepositoryView view,String typeKeyGen,Connection conn,String vendor)
191         throws KeyGenerationException{
192   
193     logger.log(Level.INFO,Messages.message("Database.createOID"));  
194     synchronized(this){
195         KeyGenerator keyGen = KeyGeneratorFactory.getKeyGenerator(typeKeyGen);
196         return  keyGen.generateKey(view,conn,vendor,sqlInterface);
197     }
198      }
199      
200      /**
201       * Create cacheId composed by values of all primary key defined in obj.
202       @param pkMap
203       @param obj
204       @return String 
205       @throws CacheException
206       */
207      private String createCacheId(PrimaryKeyMap pkMap,ObjectMapped obj
208        throws CacheException {
209        return cacheManager.createId(pkMap,obj);
210      }
211   
212     /**
213      * Delete the object.<br> 
214      
215      * Delete operation is performed using primary key of object 
216      * specified in input parameter is deleted.<br> 
217      
218      * It handles the following operations:
219      <li> get connection </li>
220      <li> create statement </li>
221      <li> execute statement </li>
222      <li> release connection </li>
223      *
224      @param object object to delete
225      @return number of rows affected
226      @throws QueryException
227      @throws MappingException
228      
229      */
230      public int delete(ObjectMapped object)
231        throws QueryException,MappingException,CacheException{
232            
233         logger.log(Level.INFO,Messages.message("Database.delete"));
234         
235         long initTime = System.currentTimeMillis();        
236     String repositoryViewName = object.getRepositoryViewName();     
237     RepositoryView view = (RepositoryViewgetRepository(repositoryViewName);
238     PrimaryKeyMap pkMap = view.getBeanDescriptor().getPrimaryKeyMap();
239     String databaseName = view.getBeanDescriptor().getDatabaseName();
240     Connection connection = connectionManager.getConnection(databaseName);
241     addConnection(connection);
242         sqlInterface = getCurrentSqlInterface(databaseName);
243     int rows = 0;
244         
245     //create delete statement
246     DeleteStatement deleteStat = new DeleteStatement(view,null,sqlInterface);    
247     logger.log(Level.FINER,deleteStat.toString());
248         
249     //execute delete statement
250     rows =  deleteStat.delete(object,view,connection);
251       
252     //release connection
253     connectionManager.releaseConnection(databaseName);
254   
255     long endTime = System.currentTimeMillis();
256     long finalTime = endTime - initTime;
257   
258     logger.log(Level.INFO,Messages.format("Database.rowsAffected",
259                                         String.valueOf(rows),
260                                         String.valueOf(finalTime)
261                                        )
262                  );
263                  
264       //create cache id 
265       String cacheId = createCacheId(pkMap,object);
266       
267       //invalidate object in cache
268       cacheManager.invalidateObject(cacheId,databaseName);        
269     return rows;
270      }
271      /**
272       * Delete the object. It handles the following operations:
273       <li> get connection </li>
274       <li> create statement </li>
275       <li> execute statement </li>
276       <li> release connection </li>
277       *
278       @param criteria for delete statement
279       @return number of rows affected
280       @throws SQLException
281       @throws MappingException
282       *
283       */
284      public int deleteForCriteria(Criteria criteria
285          throws QueryException,MappingException{
286                     
287      logger.log(Level.INFO,Messages.message("Database.delete"));
288    
289      long initTime = System.currentTimeMillis();
290      String repositoryViewName = criteria.getRepositoryName();
291      RepositoryView view = (RepositoryView)getRepository(repositoryViewName);
292      String databaseName = view.getBeanDescriptor().getDatabaseName();
293      Connection connection = connectionManager.getConnection(databaseName);
294      addConnection(connection);
295      sqlInterface = getCurrentSqlInterface(databaseName);
296      int rows = 0;
297         
298      //create delete statement
299      DeleteStatement deleteStat = new DeleteStatement(view,criteria,sqlInterface);    
300      logger.log(Level.FINER,deleteStat.toString());
301         
302      //execute delete statement
303      rows =  deleteStat.delete(connection);
304         
305      //release connection
306      connectionManager.releaseConnection(databaseName);
307    
308      long endTime = System.currentTimeMillis();
309      long finalTime = endTime - initTime;
310   
311      logger.log(Level.INFO,Messages.format("Database.rowsAffected",
312                                             String.valueOf(rows),
313                                             String.valueOf(finalTime)
314                                            )
315                  );
316         
317      return rows;
318      }
319      /**
320       * Free DatabaseImpl object from memory
321       *
322       */
323      public void destroy(){
324     try{
325         synchronized(this){
326             close();
327             connectionManager.destroy();
328         }
329     }
330     catch(Exception e){
331          logger.log(Level.SEVERE,e.getMessage());
332     }
333      }
334   
335    
336      /**
337       * Finalize the object
338       
339       @throws Throwable
340       *
341       */
342      protected void finalize() throws Throwable{         
343         super.finalize();
344      transaction = null;
345      connectionManager = null;
346     dbs.clear();
347     logger.log(Level.INFO,Messages.message("Database.finalize"));
348      }
349      
350      /**
351       * Return ConnectionManager object
352       *
353       @return ConnectionManager object    
354       *
355       */
356      public ConnectionManager getConnectionManager(){
357          return connectionManager;
358      }
359    
360      /**
361       * Return Repository to RepositoryFactory specified in 
362       * repositoryViewName
363       *
364       @param repositoryViewName name repository
365       @return Repository
366       
367       */
368      protected Repository getRepository(String repositoryViewName){
369         
370          RepositoryView view = (RepositoryView)
371                    repFactory.getRepository(repositoryViewName);
372          return view;
373      }
374      /**
375       * Return the specific sqlInterface object for the 
376       * current database name
377       *
378       @param databaseName
379       @throws MappingException
380       
381       */
382      protected SqlInterface getCurrentSqlInterface(String databaseName)
383          throws MappingException{
384          
385       String vendor = connectionManager.getConnectionSource(databaseName)
386                               .getVendor();
387       return SqlInterfaceFactory.getSqlInterface(vendor);
388      }
389       
390    
391      /**
392       * Insert the object. It handles the following operations:
393       <li> get connection </li>
394       <li> key generator </li>
395       <li> create statement </li>
396       <li> execute statement </li>
397       <li> release connection </li>
398       *
399       @param object object to delete
400       @return number of rows affected
401       @throws QueryException if errors occurs
402       @throws MappingException if error occurs
403       @throws KeyGenerationException if error occurs
404       *
405       */
406       public int insert(ObjectMapped objectthrows QueryException,
407                               MappingException,
408                                 KeyGenerationException{
409                  
410       logger.log(Level.INFO,Messages.message("Database.insert"));
411      
412       long initTime = System.currentTimeMillis();
413      
414       String repositoryViewName = object.getRepositoryViewName();
415       RepositoryView view = (RepositoryView)getRepository(repositoryViewName);
416       String databaseName = view.getBeanDescriptor().getDatabaseName();
417       PrimaryKeyMap pkMap = view.getBeanDescriptor().getPrimaryKeyMap();
418       ConnectionSource connSource = connectionManager.getConnectionSource(databaseName);
419       Connection connection = connectionManager.getConnection(databaseName);
420       addConnection(connection);
421       sqlInterface = getCurrentSqlInterface(databaseName);
422       int rows = 0;
423       
424       if(!pkMap.isComposite()){      
425       
426           String typeKeyGen = view.getBeanDescriptor().getGeneratorMap().getType();
427           Object oid = createOID(view,typeKeyGen,connection,connSource.getVendor());    
428           object.setOID(oid);
429       }
430   
431       //create insert statement
432       InsertStatement insertStat = new InsertStatement(view,sqlInterface);    
433       logger.log(Level.FINER,insertStat.toString());
434             
435       //execute insert statement
436       rows = insertStat.insert(object,view,connection);
437             
438       connectionManager.releaseConnection(databaseName);
439       
440       long endTime = System.currentTimeMillis();
441       long finalTime = endTime - initTime;
442     
443       logger.log(Level.INFO,Messages.format("Database.rowsAffected",
444                                           String.valueOf(rows),
445                                           String.valueOf(finalTime)
446                                          )
447               );
448             
449       return rows;
450       }    
451     
452       /**
453        * Rollback transaction of all databases affected 
454        * in sql operations
455        */
456       public void rollbackTransaction(){
457           try{
458             synchronized(this){
459               for(int i = 0; i < dbs.size(); i++){
460                 Connection conn = (Connection)dbs.get(i);  
461                     rollback(conn);
462             }
463             }
464         }
465         catch(Exception e){
466             logger.log(Level.SEVERE,e.getMessage());
467         }        
468       }
469       
470       /**
471        * Rollback transaction.
472        *
473        *<b>Not use this method, you must use rollbackTransaction method!</b>
474        *
475        @param connection to rollback
476        @throws TransactionException
477        *
478        */
479       public void rollback(Connection connectionthrows TransactionException{
480           transaction.rollback(connection);
481           logger.log(Level.INFO,Messages.message("Database.rollbackedTx"));
482       }
483   
484   
485       /**
486        * Execute select statement.
487        *
488        @param repositoryViewName is the name of repositoryView
489        @param criteria
490        @return QueryResults a collection of object
491        @throws SQLException
492        @throws MappingException
493        *
494        */
495       public QueryResults select(String repositoryViewName,Criteria criteria)
496           throws QueryException,MappingException,CacheException{
497           
498        logger.log(Level.INFO,Messages.message("Database.select"));
499    
500         long initTime = System.currentTimeMillis();
501         
502       QueryResults results = null;
503         RepositoryView view = (RepositoryViewgetRepository(repositoryViewName);      
504         String databaseName = (view.getBeanDescriptor().getDatabaseName());
505       PrimaryKeyMap pkMap = view.getBeanDescriptor().getPrimaryKeyMap();
506         
507           sqlInterface = getCurrentSqlInterface(databaseName);
508           SelectStatement selectStat = new SelectStatement(view,null,criteria,sqlInterface)
509           logger.log(Level.FINER,selectStat.toString());     
510                              
511           Connection connection = connectionManager.getConnection(databaseName);
512           results = selectStat.select(repositoryViewName,view,connection);
513           
514       //Put in cache the objects retrieved
515       while(results.next()){
516           ObjectMapped obj = results.getObject();
517           String cacheId = cacheManager.createId(pkMap,obj);
518         logger.log(Level.INFO,Messages.message("Database.putInCache"));                       
519         cacheManager.putInCache(cacheId,obj,databaseName,
520                     repositoryViewName);
521       }
522       results.close();
523           connectionManager.releaseConnection(databaseName);
524               
525         long endTime = System.currentTimeMillis();
526         long finalTime = endTime - initTime;
527       
528         logger.log(Level.INFO,Messages.format("Database.rowsAffected",
529                                         String.valueOf(results.size()),
530                                         String.valueOf(finalTime)
531                                        )
532         );
533         
534         return results; 
535       }
536       
537       
538     /**
539      * Execute select statement with primary key in where clause.
540      
541      
542      @param repositoryViewName is the name of repositoryView
543      @param primaryKey
544      @return QueryResults a collection of object
545      @throws SQLException
546      @throws MappingException
547      @see org.jdbf.engine.basic.PrimaryKey
548      */
549