Skip to end of metadata
Go to start of metadata

What is wrong with the current design?

jpa.container

The main problem with jpa.container is that it is too complicated. Especially the Quiesce support and wrapping of DataSources make it complex and by this fragile.

jpa.container.context

This module takes care of providing a managed EM (EntityManager).

The basic problem with the EM in general is that in JEE each EJB instance runs in its own thread. So the specs defines that an EntityManager is injected into the EJB class directly. In OSGi as well as in plain Java the problem is that one instance of aclass using the EM is typically used by many threads. The EntityManager is not thread safe though. To work around this jpa.container creates an EntityManager proxy that internally makes sure each thread has its own instance.

Problems

  • In the current design is that this module also creates an EMF service that is just different in the property org.apache.aries.jpa.proxy.factory=true. So it is easy to get the wrong one injected when simply selecting the EMF by the property osgi.unit.name like defined in the spec. So you have to know how aries works to create the correct filter.
  • The created EM is proxied and a lot of methods are handled in a special way. This makes it harder than necessary to understand what is going on when using the EM.
  • A third problem is that all EMF and EMs are managed in a central class instance. So the classes for handling this internally are quite complicated.
  • The created EM requires an aries specific Synchronization interface to interact with the transactions and method boundaries
  • Even more aries proprietary APIs are necessary to communicate between the blueprint handler and container.context

jpa.blueprint

Parses the blueprint xml elements for jpa:context and jpa:unit and create corresponding EMF or EM for injection.

Problems:

  • Instead of the XML I would prefer to use the Java EE annotations @PersistenceContext and @PersistenceUnit to mark the inection as it is more natural for users

transaction.blueprint

Parse the blueprint elements for tx:transaction and create an interceptor for the blueprint beans if necessary.

Probblems:

  • Like for jpa I would prefer to mark the transactions using annotations. Ideal would be to use the JTA 1.2 annotation @Transactional. Unfortunately this spec level it is not yet supported in aries transaction.

Support for non blueprint like declarative services?

The current solution is quite specific for aries blueprint and can not be used easily in other frameworks like declarative services.

 

Aries JPA 2.0

service design

jpa.container manages EntityManagerFactory service

EntityManagerFactory creation

The jpa.container module tracks bundles and creates one EntityManagerFactory for each persistence unit it finds. It acts a bit different from the old aries jpa container though:

It uses a hierarchy of PersistenceBundleTracker -> PeristenceProviderTracker -> DataSourceTracker. The EntityManagerFactory service is only created when all are present. If one of persistence bundle, PersitenceProvider or the used DataSource goes away then the EMF service is unregistered. So it makes sure all is nicely cleaned up.

The DataSource is looked up as a service and given to the PersistenceProvider as a object. So there is no jndi involved.  Creating datasources from persistence properties is supported thorugh DataSourceFactory services. The XA wrapping is done by pax-jdbc-pool the new jpa.container does not wrap XA resources itself.

The module creates no proxies. The EntityManagerFactory is published as is. So it is much simpler.

EM supplier service

The basic idea for the new design was to have an indirection in the EM usage. So the EM is used through an interface EmSupplier like emSupplier.get().persist(myEntity). The advantage is that inside the get() you can easily hold one EM per thread without using a proxy.
To make sure the EM can be reused between user classes using the same persistence unit it makes sense to provide a central instance of this supplier. So the idea is to publish one EMSupplier per persistence unit as an OSGi service.

This can then be used in the blueprint handler as well as in other frameworks.

To avoid on demand creation of the EM which can be confusing the EMSupplier interface also has the methods preCall() and postCall() like the aries Synchronization interface. Before the first usage of get() in a method preCall() has to be called. At the end of the method postCall() has to be called.

PreCall makes sure the EntityManager is created and postCall makes sure it is destroyed. If cascaded calls happen the EM is created / destroyed only at the outermost level.

Blueprint handler

The blueprint handler reacts on the @PersistenceContext annotation and injects injects either an EmSupplier or an EntityManger depending on the field type.

  • If an EM is injected EM it is a simple proxy that delegates to the EM it gets from the EmSupplier. So while I prefer the supplier approach it is also possible to use the JavaEE style at the cost of having a proxy.
  • The nice part of the EMSupplier solution is that it completely works without proxying the EM. So you get a much nicer stack trace for debugs.

The handler also takes care of the transaction management. There I largely reused the aries transaction code but also added a RESOURCE-LOCAL support. In the later case the transaction is managed using the EM transactions.

See jpa-blueprint-example

Currently transactions are handled together with the EM management. For later the idea would be to handle transactions sepearately by reacting on the JTA 1.2 @Transactional annotation.

JPA Templates and closure support

As we can not create a transaction handling proxy for declarative services I created a JpaTemplate class to support a closure based approach for transactions.

The idea is to support this style of marking transactions:

Closure based transactions
@Component
public class TaskServiceImpl implements TaskService {

    private JpaTemplate jpa;
    
    public Task getTask(Integer id) {
        return jpa.txExpr(Required, em -> em.find(Task.class, id));
    }

    public void addTask(Task task) {
        jpa.tx(em -> {
            em.persist(task);
            em.flush();
        });
    }

    @Reference(target="(osgi.unit.name=tasklist)")
    public void setJpaTemplate(JpaTemplate jpa) {
        this.jpa = jpa;
    }
}

So per persistence unit there will be one JpaTemplate with RESOURCE_LOCAL or JTA support depending on the setting in persistence.xml. The user simply binds the template as a service and can then write his code inside closures. This way the EM is accessed local to the closure and does not need additional measures to be thread safe.

Internally the JpaTemplate uses the EmSupplier of the persistence unit and the JTA TransactonManager as services. So this allows to reuse the EnityManager even between different frameworks. For example you can start your transaction in a DS service and from there also call another service created using blueprint. Still both will transparently use the same transaction if the transaction type is e.g. REQUIRED. If the use the same persistence unit then both will also use the same EM instance and so take advantage of internal caching.

See jpa-closure-example

Code

The full code is available in a github repo. The README should allow to run it in karaf 3 within some minutes.

https://github.com/cschneider/jpa-experiments

Further enhancements, how to move on

  • The transaction handling is not fully implemented. Wherever an EM is used the method is executed in a REQUIRED transaction.
  • At one point the code should go into aries jpa.
  • The persistence.xml should be validated as required by the spec
  • I would be happy about any feedback before I put more work into the code base
  • jpa.ef does not suport transformers. For hibernate it still works though but needs some special imports in the user bundle to enable class enhancement. Maybe this would work better when using transormers.

 

 

Contact

mail: chris_at_die-schneider.net

Twitter: @schneider_chris

Github: cschneider

Xing

Linkedin

Popular Labels

My colleagues at Talend

Talend Community Coders





  • No labels