EntityManager with JTA multiple connections

6

I'm having a rather unusual problem, but I'm looking for a solution.

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

I have a java application with jpa , ecliselink and ejb 3 , already ready and working, today I use EntityManager to let the container manage the transactions for me (CMT) I do not need to be giving begin and commit, it resolves when I execute in the best way.

Well, with this in mind, I need to continue to use this framework and can not switch to BAN (Bean Managed Transaction), I need to continue using CMT (for larger reasons). Searching and studying a lot of the concept I was able to perform the functionality of connecting to several bases with JTA, but that way I could not get it to be managed by the container (CMT), I would have to give the begin and commit. I'm going to post the code that I was able to perform this function, if anyone knows how to leave the transaction as CMT following this basis of reasoning I would appreciate it.

The code below works but is not running as CMT, it 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.  

}  

Anyone who understands JPA well could help me?

Comment : If someone says to use multiple @PersistenceContext(unitName = "db1") , I can not use it that way, because if any IP goes out or gives a problem on some basis the application does not go up, giving a pain head, I've also tried to use Multitenancy did not fit in that case.

    
asked by anonymous 19.09.2014 / 19:33

2 answers

2

JTA, JPA, Data Source?

I think you're confusing JTA, JPA, and connection management (or EntityManager). I do not mean that you do not know, but maybe you're attacking the wrong spot in this particular situation.

JTA is for managing distributed transactions, where you change multiple databases at the same time. If this does not occur in your application, you would not need to use the API.

JPA handles persistence of entities and handles transparently with the connection.

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 you understand your problem well, you need to use a different data source depending on the client logged in. This is exactly the concept of multi-tenancy or milti tenancy .

One of the ways you can do this using the standard Java API and the data sources set up in container is to create a producer ) of EntityManager for each request.

Using the documentation , we see that it is possible to produce beans 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 method logic to produce EntityManager can use some ThreadLocal variable to define which client is logged in. Just create a ServletFilter to attach to the thread of each request some client information from which to determine data source .

In addition, you are likely to have a central database with login and client 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 the custom annotations @Cliente and @Geral with each data source . So you can inject them independently into any class like this:

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

Remembering that% client-specific% will only be available in the context of a request

Lastly, just to complement, the specific% producer method will need to create instances of EntityManager on demand. Because 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 like EntityManager , EntityManagerFactory and dataSourceName should be private.
  • CMT does not "resolve how best to execute" but places transactions in annotated methods using the container JTA implementation, so your application probably uses transactions where it is not needed. There is nothing magical that makes the best ever, you need to be aware of when, what kind and how to demarcate your transactions if you want optimized performance.
03.06.2015 / 16:00
-1

You must configure your application to use Two-Phase Commit.

Here are two cool articles:

  • link
  • link
  • I hope it helps ;-)

    P.S. Attention to setting postgresql.conf , max_prepared_transactions must be non-zero! If you need to change, you have to restart the service.

        
    25.09.2014 / 16:46