/* * Copyright 2005-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** *

* This package provides test library for database testing in general and JPA testing * specifically. As part of this it provides a means to transparently start an inmemory * database from a junit test or connect to an external database. Also, given a persistence * unit it is easy to start testing it in a junit test with only a few lines of code. *

* *

* The main use cases are explained below: *

* * *

* See also the design overview. *

* * *

Basic database testing, transparently connecting to a database

*
* Starting the database: *
 *      Database db = DatabaseBuilder.getDatabase();
 *      DataSource dataSource = db.start();
 * 
*

* If nothing is specified in the user's environment, an inmemory database is started (derby). * Using the datasource is just standard JDBC now. *

*

* After a test it is good practice to stop the database: *

*
 *      db.stop();
 * 
* * *

Connecting to an external database

*
* * Connecting to an external database can be done by requiring the 'external' capability on * the database provider. *
 *      Database db = DatabaseBuilder.getDatabase(DatabaseProvider.CAPABILITY_EXTERNAL); 
 * 
* This also requires a number of environment variables or system properties to be set, * see {@link ExternalDatabase}. * *

* However, the most convenient way to set the capabilities is usually to set a system property or environment * variable see the javadocs of {@link DatabaseBuilder}. and * specifically DatabaseBuilder.DB_CAPABILITIES_PROP *

* * * *

Executing code within a JDBC transaction

*
*

* To execute code withing a JDBC transaction, use the {@link DatabaseUtils} and * use the {@link DatabaseUtils#executeInTransaction(org.wamblee.support.persistence.DatabaseUtils.JdbcUnitOfWork)} * method. *

*
 *       DatabaseUtils dbutils = new DatabaseUtils(dataSource);
 *       boolean result = dbutils.executeInTransaction(
 *           new JdbcUnitOfWork<Boolean>() {
 *               @Override
 *               public Boolean execute(Connection aConnection) throws Exception {
 *                   ResultSet res = jpaTester.getDbUtils().executeQuery(
 *                       aConnection, GROUP_QUERY, aGroup);
 *                   return res.next();
 *               }
 *           });
 * 
* {@link DatabaseUtils} also provides various other utility methods to work with JDBC queries. * * *

Using DB Unit in your tests

*
* *

To work with DBUnit, * DatabaseUtils#createDbTester(org.dbunit.dataset.filter.ITableFilterSimple) * must be used passing it in the tables to use in the form of a {@link org.dbunit.dataset.filter.ITableFilterSimple} * object.

* *
 *      IDatabaseTester dbtester = dbutils.createDbTester(new ITableFilterSimple() {
 *          public boolean accept(String aTableName) throws DataSetException {
 *              return aTableName.startsWith("XYZ_");
 *          }
 *      });
 * 
* *

The reason for using a DatabaseUtils instead of DBUnit directly is that * DatabseUtils will keep track of connections and close them when DatabaseUtils * is closed

* * *

Basic JPA testing

*
*

* First step is to create a {@link PersistenceUnitDescription} that matches the persistence unit you * want to test. *

*

Second step is to make sure that all entities are listed explicitly in your * persistence.xml. Currently, class path scanning appears to fail when * run from junit. * Specifying all entities explicitly is not necessarily a bad thing as it is also more efficient. *

* *

* Now create a JpaTester in your test code: *

*
 *      @Before
 *      public void setUp() throws Exception {
 * 
 *          // First we create the JpaTester by telling us which persistence unit we
 *          // are going to test
 *          jpaTester = new JpaTester(new MyPersistenceUnit());
 *          jpaTester.start();     
 *      }
 * 
* *

* Then in test code execute some JPA code within a unit of work: *

*
 *      jpaTester.getJpaBuilder().execute(new JpaUnitOfWork() {
 *          public Void execute(EntityManager aEm) {
 *              MyEntity entity = new MyEntity("a", "b");
 *              aEm.persist(entity);
 *              return null;
 *          }
 *      });
 * 
* * *

JPA testing combined with JDBC and DBUnit

*
*

The JPATester provides access to all required object. It is usually convenient to * get them directly from the JPATester after initializing it: *

 *      builder = jpaTester.getJpaBuilder();
 *      dbutils = jpaTester.getDbUtils();
 *      dbtester = dbutils.createDbTester(new MyTables());
 * 
* * *

Testing a service that requires a transaction

*
* *

Using {@link TransactionProxyFactory} it is possible to create a proxy for a given * service interface to provide the semantics of 'requires new' transaction semantics. *

 *      TransactionProxyFactory factory = new TransactionProxyFactory(
 *           jpaTester.getJpaBuilder(), Service.class);
 *      Service service = new ServiceImpl(factory.getTransactionScopedEntityManager()); 
 *      Service proxy = factory.getProxy(service);
 *      proxy.execute(...);
 * 
* *

* In the above example, the Service POJO requires an {@link EntityManager} in its * constructor and it is passed a transaction scoped entitymanager from the factory. This entitymanager * is in fact a so-called contextual reference. * Next, the proxy is obtained from the factory. Invoking any method on it will make sure a new * transaction is started and a new entity manager is created for the scope of that transaction. *

* * *

Design overview

*
* *

Database transparency

* *
* database *
*

* {@link DatabaseProvider} uses java.util.ServiceLoader to find all implementations * of {@link DatabaseProvider} on the classpath. It then asks the database providers whether * they support the required capabilities (by default inmemory), and the first provider that * supports the capabilities is used to create the database. *

*

* Note that the Database interface is not intended to always create a database. * It will do so for {@link DerbyDatabase} (inmemory), but with {@link ExternalDatabase} * it simply connects to an external database based on system properties or environment * variables. *

* *

JPA tester overview

* *
* database *
* *

JPATester is responsible for:

* * * */ package org.wamblee.support.persistence;