Intro
The JPA module allows to define transactional beans or methods. It's similar to the annotation you might know from the Spring framework. It's a simple alternative to EJBs (e.g. if you don't have an EJB container).
Dependencies
The page CODI Modules provides an overview about CODI modules and how to add them to your project.
For using the features described in this page, you have to add:
- CODI-Core
- CODI-JPA module
- JPA implementation of your choice
Intro page provides an overview, the setup of this module and describes the motivation for the features described below. This page explains the most important APIs and mechanisms of the JPA module provided by CODI. Please note that this page doesn't show all possibilities. If you have any question, please contact the community!
Tip |
---|
|
This module is completely independent of the rest and already moved to Apache DeltaSpike v0.3+ (including several improvements like easier handling/usage of qualifiers, optional JTA support,...) . |
Tip |
---|
|
For using @PersistenceContext you can use e.g. the resources-plugin of OpenWebBeans |
Using one (default) Entity Manager
The following example will create a CDI producer method for creating a @RequestScoped EntityManager with qualifier @Default.
Code Block |
---|
| java |
---|
| java |
---|
title | Producer for a default entity manager |
---|
|
public class DataBaseProducer
{
@Produces
@PersistenceContext(unitName="default")
private EntityManager entityManager;
@Produces
@Default
private@RequestScoped
public EntityManager createDefaultEntityManager()
{
return this.entityManager;
}
public void dispose(@Disposes @Default EntityManager entityManager)
{
if(entityManager.isOpen())
{
entityManager.close();
}
}
}
|
Tip |
---|
|
For using @PersistenceContext you can use e.g. the resources-plugin of OpenWebBeans |
Code Block |
---|
| java |
---|
| java |
---|
title | Using @Transactional |
---|
|
//...
public class CustomService1
{
@Inject
protected EntityManager entityManager;
@Transactional
public void update(CustomEntity1 entity)
{
this.entityManager.merge(entity);
}
}
|
...
Code Block |
---|
| java |
---|
| java |
---|
title | Producer for entity managers |
---|
|
public class DataBaseProducer
{
@Produces@PersistenceContext(unitName="default")
private EntityManager entityManager;
@PersistenceContext(unitName="defaultUserDB")
private EntityManager usersEntityManager;
@Produces
@Default
privatepublic EntityManager createDefaultEntityManager()
{
return this.entityManager;
}
@Produces
@Users
public EntityManager @PersistenceContextcreateUsersEntityManager(unitName="UserDB")
@Users{
private EntityManagerreturn this.usersEntityManager;
}
public void disposeDefaultDB(@Disposes @Default EntityManager entityManager)
{
if(entityManager.isOpen())
{
entityManager.close();
}
}
public void disposeUserDB(@Disposes @Users EntityManager entityManager)
{
if(entityManager.isOpen())
{
entityManager.close();
}
}
}
|
Code Block |
---|
| java |
---|
| java |
---|
title | Using @Transactional and an entity manager with a qualifier |
---|
|
//...
public class CustomService2
{
@Inject
@Users
protected EntityManager entityManager;
@Transactional(Users.class)
public void update(CustomEntity2 entity)
{
this.entityManager.merge(entity);
}
}
|
EntityManager injection without producer (CODI v1.0.3+)
Code Block |
---|
| java |
---|
| java |
---|
title | Using @Transactional and @PersistenceContext without producer |
---|
|
//...
public class CustomService3
{
@PersistenceContext(unitName = "default")
private EntityManager entityManager;
@Transactional
public void delete(Entity entity)
{
this.entityManager.remove(em.merge(entity));
}
@PreDestroy
public void cleanup()
{
if(this.entityManager.isOpen())
{
this.entityManager.close();
}
}
}
|
...
Extended Persistence Contexts
MyFaces Orchestra provides a feature which allows keeping an EntityManager
across multiple requests. That means it isn't required to call EntityManager#merge
to add detached entities to the context. However, several application architectures don't allow such an approach (due to different reasons like scalability). Seam3 still does that. In theory that sounds nice and it works pretty well for small to medium sized projects esp. if an application doesn't rely on session replication in clusters. That also means that such an approach restricts your target environment from the very beginning. One of the base problems is that an EntityManager
isn't serializable. Beans which are scoped in a normalscoped CDI context have to be serializable. So by default it isn't allowed by CDI to provide a producer-method which exposes e.g. a conversation scoped EntityManager
as it is. We don't recommend to use this approach and therefore it isn't available out-of-the-box. However, if you really need this approach to avoid calling #merge
for your detached entities, it's pretty simple to add this functionality.
Code Block |
---|
| java |
---|
| java |
---|
title | Usage of a simple ExtendedEntityManager |
---|
|
@Inject
protected EntityManager entityManager;
|
As you see the usage is the same. You don't have to use ExtendedEntityManager
at your injection point. It's just needed in the producer-method:
Code Block |
---|
| java |
---|
| java |
---|
title | Creating a simple ExtendedEntityManager |
---|
|
public class EntityManagerProducer
{
@PersistenceContext(unitName = "demoPU")
private EntityManager entityManager;
@Produces
@Default
@ConversationScoped
public ExtendedEntityManager create()
{
return new ExtendedEntityManager(this.entityManager);
}
public void dispose(@Disposes @Default ExtendedEntityManager entityManager)
{
if(entityManager.isOpen())
{
entityManager.close();
}
}
}
|
Code Block |
---|
| java |
---|
| java |
---|
title | Implementation of a simple ExtendedEntityManager |
---|
|
@Typed()
class ExtendedEntityManager implements EntityManager, Serializable
{
private static final long serialVersionUID = 3770954229283539616L;
private transient EntityManager wrapped;
protected ExtendedEntityManager()
{
}
public ExtendedEntityManager(EntityManager wrapped)
{
this.wrapped = wrapped;
}
/*
* generated
*/
//delegate all calls to this.wrapped - most IDEs allow to generate it
|
This approach just works if it doesn't come to serialization of this wrapper e.g. in case of session-replication.
If those beans get serialized, you have to overcome this restriction by storing the persistence-unit-name and recreate the EntityManager
via Persistence.createEntityManagerFactory(this.persistenceUnitName).createEntityManager();
btw. sync it with the database before closing it on serialization. Furthermore, you have to intercept some methods of the EntityManager
to merge detached entities automatically if those entities get serialized as well. However, as mentioned before we don't recommend such an approach.
JTA
CODI itself doesn't support it, but JTA support is available in Apache DeltaSpike.
Currently there is no support for it, however, you can use https://github.com/openknowledge/openknowledge-cdi-extensions in combination with MyFaces CODI for using JTA (if it works in combination with the application server of your choice).
TransactionScoped (since v1.0.2)
The @Transactional annotation of Apache MyFaces CODI will maintain an own transcaction context which automatically starts when an @Transactional method gets invoked, and ends when the outermost @Transactional method returns. This might for example be used to create @TransactionScoped EntityManagers. The effect of such an EntityManager is similar to using EJBs: when the outermost @Transactional method returns, the @TransactionScoped EntityManager will get destroyed and thus em.close() will get called effectively detaching all entities.
This scope is esp. useful if the loaded entities have to be cached.
Code Block |
---|
| java |
---|
| java |
---|
title | Producer for a @TransactionScoped entity manager |
---|
|
public class DataBaseProducer
{
@PersistenceContext(unitName="default")
private EntityManager entityManager;
@Produces
@TransactionScoped
public EntityManager createEntityManager()
{
return this.entityManager;
}
public void dispose(@Disposes EntityManager entityManager)
{
if(entityManager.isOpen())
{
entityManager.close();
}
}
}
|
ConfigurableDataSource (since v1.0.2)
Configuring the data connection properties in JPA projects in pure Java EE is always a painful task. Directly configuring the JDBC driver class and database credentials in the persistence.xml is a no-go.
Configuring a DataSource via JNDI sounds good at the first glance, but has serious flaws:
- providing JNDI for native unit tests (without starting the EE container) requires a lot of tweaking.
- JNDI locations for configured DataSources are not specified. Configuring the same myDb DataSource in n EE containers will give you n different JNDI locations - thus it's not possible to use a fixed JNDI location in your persistence.xml
A solution to this problem is provided via the Apache MyFaces CODI ConfigurableDataSource. This is a thin DataSource wrapper which will get it's configuration via CDI mechanics.
Code Block |
---|
| XML |
---|
| XML |
---|
title | persistence.xml |
---|
|
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="test" >
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>org.apache.myfaces.extensions.cdi.jpa.test.TestEntity</class>
<properties>
<property name="openjpa.jdbc.DBDictionary" value="hsql" />
<property name="openjpa.ConnectionDriverName"
value="org.apache.myfaces.extensions.cdi.jpa.impl.datasource.ConfigurableDataSource"/>
<property name="openjpa.ConnectionProperties" value="connectionId=core"/>
</properties>
</persistence-unit>
</persistence>
|
The configuration itself is provided by implementing the CODI DataSourceConfig class. The DataSourceConfig determines whether a JNDI path, a JDBC connection or another DataSource should get used, by just returning the proper values. You can use all the flexible CDI and CODI mechanisms like @ProjectStageActivated, @ExpressionActivated, @Alternative, @Specializes, etc and even if/then/else,... to find the perfect settings for your current runtime environment!
TransactionHelper
This helper allows to execute CDI-unmanaged code blocks in a @Transactional manner. E.g. with the test modules provided by MyFaces CODI you can inject CDI beans and therefore transactional services into a JUnit test. So it's possible to use existing implementations e.g. to create sample data for unit tests or to remove such entries again. If a service doesn't (or shouldn't) provide the needed functionality e.g. for deleting entities, it's possible to use TransactionHelper
for such additional logic which should be executed within a transaction.
Note |
---|
|
Please be aware that this helper only works for @Transactional with the default qualifier! If you need the functionality for another EntityManager , then you need to copy this code and adopt it. |
Code Block |
---|
| XML |
---|
| XML |
---|
title | Optional usage of TransactionHelper |
---|
|
MyReslt result = TransactionHelper.getInstance().executeTransactional(new Callable<MyReslt>() {
public MyReslt call() throws Exception
{
EntityManager entityManager = BeanManagerProvider.getInstance().getContextualReference(EntityManager.class);
entityManager.remove(...);
return entityManager.find(...);
}
});
|