Entitymanager with JTA multiple connections

Asked

Viewed 1,478 times

6

I’m having a bit of an unusual problem, but I’m looking for a solution.

I have a scenario where I have several databases (postgresql) allocated in several clients, all databases have the same structure, but with different records.

I have a java application with jpa, ecliselink and ejb 3, ready and in operation, today in it I use the Entitymanager letting the container manage the transactions for me (CMT - Container Managed Transaction), so I do not need to keep giving Begin and commit, it solves when run in the best way.

Well, keeping this in mind, I need to continue using this structure and I cannot switch to manual mode (BTM - Bean Managed Transaction), I need to continue using CMT (for larger reasons). Researching and studying the concept a lot, I was able to realize the functionality of connecting in several bases with JTA, but that way I could not manage it by container(CMT), I would have to give Begin and commit. I will post the code that I managed to perform this function, if anyone knows how to leave the transaction as CMT following this reasoning basis I would appreciate.

The code below works, but is not running as CMT, only persists if you have Begin and commit:

@Stateless  
@TransactionManagement(TransactionManagementType.CONTAINER)  
public class TestEntDAO {  

    private EntityManager em;  
    private EntityManagerFactory emf;  

    @PostConstruct  
    public void init() {  
        em = getEntityManager();  
    }  

    public EntityManager getEntityManager() {  
        Map props = new HashMap();  
        props.put(PersistenceUnitProperties.TRANSACTION_TYPE, "JTA");  
        props.put(PersistenceUnitProperties.JTA_DATASOURCE, dataSourceName()); // <- Aqui será injetado o cliente que está logado, pegando a base dele.  
        emf = Persistence.createEntityManagerFactory("testePU", props);  
        em = emf.createEntityManager();  
        return em;  
    }  

    public String dataSourceName(){  
        if(someCondition){  
            return "db1";  
        }else{  
            return "db2";  
        }  
    }  

    public salvar(Tabela tab){  
     em.persist(tab);  
   } //<-- quando termia esse método era para dar o commit automatico (CMT), mais não esta sincronizando as transações.  

}  

Someone who understands JPA well could help me?

Observing: If someone tells you to use several @PersistenceContext(unitName = "db1"), I can not use this way, because if any IP goes outside or gives problem on some basis the application does not go up, giving a headache, also I tried to use the Multitenancy did not fit in this case.

  • The way to do this is this: set up multiple Units persistence and identify which one you want to use @PersistenceContext(unitName = "db1"). If you need to upload your application even with an unavailable database server, perhaps it would be better to search for this feature rather than another technique of using connections to multiple database servers. Which application server do you use? Perhaps the application server itself provides a setup for this.

  • I tried to do this, turned glassfish3 upside down, if any connection fails it throws a connection allocation exception. thinking that you may have more than 50 @Persistencecontext created, imagine the headache, apart from the maintenance of this, in and out customer. Unviable.

  • Hmm I remembered now that I already had this need a few years ago (connect to N Database on N servers) and I ended up with the same code you posted: without using transaction managed by the container. And yet I had to create on the arm an Entitymanagerfactory pool for performance reasons. If you find a better solution, put it there and give me a touch please. Good luck!

  • If I get put here, it’s more complicated. Thanks.

  • Luciano, in its implementation data source is defined once at the startup of bean, soon your DAO would access only one of possible data sources. Is that really how it works? Or does the application use different fonts in the same execution, depending on some criteria? What is this criterion, that is, how the application decides which is the data source?

2 answers

2

JTA, JPA, Data Source?

I think you’re confusing JTA, JPA and connection management (or Entitymanager). I don’t mean you don’t know, but maybe you’re hitting the wrong spot in this particular situation.

JTA is used to manage distributed transactions, where multiple databases are changed at the same time. If this does not occur in your application, you would not need to use the API.

JPA handles entity persistence and handles the connection transparently.

Both JTA and JPA do not care about the data source used, except of course where each API is compatible with the data source, after all some databases do not support distributed transactions or are not supported by JPA.

Multi Tenancy: different data sources

If I understand your problem, you need to use one data source different depending on the client logged in. This is exactly the concept of multi-tenure or milti tenancy.

One of the ways to do this using the standard Java API and the data sources configured in the container is creating a producer (Produce) of EntityManager for each request.

Using the documentation, we see that it is possible to produce Beans in specific scopes. If we apply this to EntityManager, we can try to produce Ems in the scope of the request:

@Produces
@Cliente
@RequestScoped
public EntityManager getClientEntityManager() {
  //TODO produces EntityManager for client
}

The logic of the method to produce the EntityManager can use some variable ThreadLocal to define which client is logged in. Simply create a ServletFilter to attach to thread of each request some customer information from which it is possible to determine the data source.

In addition, you are likely to have a central database with login and customer information. Then you can create another producer for this source:

@Produces
@Geral
public EntityManager getGeneralEntityManager() {
  //TODO produces EntityManager for general database
}

The above codes associate custom annotations @Cliente and @Geral with each data source. Therefore, you can inject them independently in any class like this:

@Inject @Cliente EntityManager emCliente;
@Inject @Geral EntityManager emGeral;

Recalling that the EntityManager customer specific will only be available in the context of a request

Finally, to complement only the method of producing EntityManagerspecific s will need to create instances of the EntityManagerFactory on demand. How to Factories should be created only once, use a global map to create them on the first call and then reuse them from the map.

Some other details

  • Methods such as dataSourceName, getEntityManager and init should be private.
  • CMT does not "solve how best to execute", but puts transactions in the annotated methods using the JTA implementation of container, so your application probably uses transactions where it’s not needed. There is nothing magical that does the best ever, you need to be aware of when, what type and how to demarcate your transactions if you want optimized performance.

0

  • 1

    The question has no relation to distributed transactions.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.