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 = (RepositoryView) getRepository(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 object) throws 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 connection) throws 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 = (RepositoryView) getRepository(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 |