Search code examples
jpaeclipselinkjtaglassfish-4

How to use JPA to persist same object to two different db's without distributed transaction?


I want to persist the same entity to a MySQL database and Postgres database (essentially one is a real-time clone of the other). Conceptually, I want to do this in a single method:

EntityManager mysql = ...;
EntityManager postgres = ...;
MyEntity e = new MyEntity();
e.setStuff();
mysql.persist(e);
postgres.persist(e);

The MyEntity class specifies a @GeneratedValue strategy of IDENTITY for its @Id field, and the two data sources are non-XA data sources.

JPA/JTA seems to want to do this in a distributed transaction, which I believe is due to how the transaction boundaries are determined for container-managed transactions, and I get an error because the data sources are non-XA. I can define the data sources as XA sources so the above works as a distributed transaction, but for the immediate need, that's really unnecessary. I don't really care that the two persists are in the same transaction -- In fact, if one fails and the other succeeds, that's fine too (at least for now).

Is there a way to persist the same object to multiple databases with non-XA data sources and still use container-managed transactions? Related, if I want to do a series of persists with multiple objects and multiple data sources in a single method, is there a way to do that with non-XA data sources? I'm using EclipseLink with GlassFish 4.0.


Solution

  • Well, didn't discover how to do it with container-managed transactions, but did it with bean-managed transactions. Injected a UserTransaction resource, and wrapped each persist between a begin/commit pair:

    @Stateless
    @TransactionManagement(TransactionManagementType.BEAN)
    public class MyClass 
    {
      @Resource private UserTransaction utx;
    
      public void doStuff() 
      {
        EntityManager mysql = ...;
        EntityManager postgres = ...;
        MyEntity e = new MyEntity();
        e.setStuff();
        try {
          utx.begin();
          mysql.persist(e);
          utx.commit();
          utx.begin();
          postgres.persist(e);
          utx.end();
        } catch (...) {
        ...
        }
      }
    }
    

    I've never tried using bean-managed transactions before, and even though this isn't for production use and isn't especially elegant, if there's something fundamentally wrong with this, I'd appreciate somebody pointing out the proper way.