/* * 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: *
*UserTransaction
* * See also the design overview. *
* * ** 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(); ** * *
* 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
*
* 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. * * *
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
* 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; * } * }); *
* Note that in addition to this method it is also possible to directly control transactions * through {@link JpaBuilder#begin()}, {@link JpaBuilder#commit(javax.persistence.EntityManager)}, * and {@link JpaBuilder#rollback(javax.persistence.EntityManager)}. *
* * *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()); ** * *
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.
*
UserTransaction
* It is also possible to manage JPA transaction through a UserTransaction
.
* This is mostly useful if you are test software that uses UserTransaction
for
* managing transactions, or if you want to manage multiple transactional resources.
* See the explanation at {@link org.wamblee.test.transactions.SimpleTransactionManager} for more details.
*
* {@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.
*
JPATester
is responsible for: