Improvements to EntityManegedFactory JPA

1

I am working as follows, I have 2 PersistenceUnit , one from the Manager database, which is where I find the data from the clients 'databases, another from the clients' database, which I get the from the Manager data, I want to know if I'm doing it right, or if I can get better, I have multiple clients accessing the system simultaneously, so if anyone has any tips!

Connection

    package DAO;

import static DAO.DAO_001_Gestor.mitryusEM;
import Entity.Cadgru;
import Entity.UsuarioSessao;
import java.io.Serializable;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.servlet.http.HttpSession;

public class Conexao implements Serializable {

     //Cria o EMF do Gestor
    public static EntityManagerFactory GestorEMF;
    //Cria oS EMF do Banco de dados dos clientes
    private static Map<String, EntityManagerFactory> mitryusEMF = new ConcurrentHashMap<>();

    public static EntityManagerFactory usuario;

    public EntityManager getEntityManager(String PU, String Local, String endfdb) {
        // Verifico se o retorno esta vindo do GestorPU ou do MitryusPU 
        //Caso seja do Gestor o PU E igual a 0
        if (PU.equals("0")) {
            //Verifica se ja tem EMF do gestor aberto
            if (GestorEMF == null || !GestorEMF.isOpen()) {
                //Caso nao tenha Cria o EMF
                GestorEMF = Persistence.createEntityManagerFactory("GestorPU");
                return GestorEMF.createEntityManager();
            } else {
                //Caso tenha cria o EM
                return GestorEMF.createEntityManager();
            }
            //Caso seja o MitryusPU OBS(Vem da Variavel PU)
        } else if (mitryusEMF.get(Local) == null || !mitryusEMF.get(Local).isOpen()) {
            //Cria as Propriedades de Conexao
            Properties props = new Properties();

            props.setProperty("javax.persistence.jdbc.url", "jdbc:firebirdsql:" + endfdb + "/3050:" + Local);
            mitryusEMF.put(Local, Persistence.createEntityManagerFactory(PU, props));
            //Guarda o Local obs(Endereco do banco de dados ex c:\banco ) em uma sessao
            HttpSession sess = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
            sess.setAttribute("mitryusEMF", Local);

            //Retorna o EM
            return mitryusEMF.get(Local).createEntityManager();

        } else {
            HttpSession sess = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
            sess.setAttribute("mitryusEMF", Local);
            return mitryusEMF.get(Local).createEntityManager();
        }
    }

    //Pega o EM do Gestor
    public EntityManager getEntity() {
        if (GestorEMF == null || !GestorEMF.isOpen()) {
            GestorEMF = Persistence.createEntityManagerFactory("GestorPU");

            return GestorEMF.createEntityManager();

        } else {

            return GestorEMF.createEntityManager();
        }
    }

    //Pegao em do Banco de dados do cliente
    public EntityManager getEntityMitryus() {
        //String que recebe o valor da sessao "mitryusEMF"
        String valor = (String) getSession().getAttribute("mitryusEMF");
        //Retorna o EM do cliente 
        return mitryusEMF.get(valor).createEntityManager();
    }

    public HttpSession getSession() {
        return (HttpSession) getFacesContext().getExternalContext().getSession(false);
    }

    public FacesContext getFacesContext() {

        return FacesContext.getCurrentInstance();
    }

    /**
     * @return the mitryusem
     */
}

Persistence :

    <?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="GestorPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>Entity.Cadgru</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.connection.driver_class" value="org.firebirdsql.jdbc.FBDriver"/>
      <property name="hibernate.connection.url" value="jdbc:firebirdsql:10.1.1.122/3050:C:\BANCOS\GESTOR.FDB"/>
      <property name="hibernate.connection.username" value="SYSDBA"/>
      <property name="hibernate.connection.password" value="masterkey"/>
      <property name="hibernate.connection.pool_size" value="2000"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.FirebirdDialect"/>
    </properties>
  </persistence-unit>
  <persistence-unit name="MitryusPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>Entity.Cadusr</class>
    <class>Entity.Cadloj</class>
    <class>Entity.Cadfun</class>
    <class>Entity.Tipcli</class>
    <class>Entity.Tipven</class>
    <class>Entity.Vendas</class>
    <class>Entity.Venda_Sintetico</class>
    <class>Entity.CodigoPin</class>
    <class>Entity.Codloc</class>
    <class>Entity.VendaEvolucao</class>
    <class>Entity.Reljva</class>
    <class>Entity.Venda_Moeda</class>
    <class>Entity.Venda_Moeda_Loja</class>
    <class>Entity.Vendas_OP_Caixa</class>
    <class>Entity.P563_Vale</class>
    <class>Entity.P571_ValeExpirado</class>
    <class>Entity.P527_AnaliseCliente</class>
    <class>Entity.Cadpla</class>
    <class>Entity.Cadfor</class>
    <class>Entity.GraficoVendas_Funcionario</class>
    <class>Entity.P530_TicketMedio</class>
    <class>Entity.P495_OrcamentosPendentes_S</class>
    <class>Entity.P495_OrcamentosPendentes_A</class>
    <class>Entity.P535_AlteracaoVendas</class>
    <class>Entity.Detusr</class>
    <class>Entity.P468_VendaFaixaValor</class>
    <class>Entity.Tipfor</class>
    <class>Entity.P444_ContasPagar</class>
    <class>Entity.P444_ContasPagar_DATVEN</class>
    <class>Entity.P444_ContasPagar_TOTALIZADO_PCONTAS</class>
    <class>Entity.P444_ContasPagar_MesAno</class>
    <class>Entity.P444_ContasPagar_Sintetico</class>
    <class>Entity.P507_VendasAnaliseGeral_A_Mestre</class>
    <class>Entity.P507_VendasAnaliseGeral_A_Detalhe1</class>
    <class>Entity.P507_VendasAnaliseGeral_A_Detalhe2</class>
    <class>Entity.P507_VendasAnaliseGeral_S</class>
    <class>Entity.Cadpag</class>
    <class>Entity.P493_ValeFuncionarios</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.connection.driver_class" value="org.firebirdsql.jdbc.FBDriver"/>
      <property name="hibernate.connection.username" value="SYSDBA"/>
      <property name="hibernate.connection.pool_size" value="2000"/>
      <property name="hibernate.connection.password" value="masterkey"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.FirebirdDialect"/>
    </properties>
  </persistence-unit>
</persistence>
    
asked by anonymous 26.04.2016 / 15:30

1 answer

2

It's a long and complex subject, so I'll try some punctual suggestions, but it's up to you to judge whether they would be good within the context of your project.

Dependency injection

An improvement would be to use dependency injection using a standardized API rather than relying on calling a class of its own.

You have an article that explains how to use pretty much the same approach of keeping a EntityManagerFactory per bank, but allowing you to inject EntityManager transparently:

  

JPA Multitenancy

Do not use the session unnecessarily

Another thing I do not see is need to use the session if you already maintain a static list of EMFs.

Why do not you go directly through a static method or better yet, as I mentioned above, injecting with CDI or Spring?

Do not create a EntityManagerFactory per bank

I also do not like the idea of keeping many instances of EntityManagerFactory in memory.

If the number of customers grows you will have an excessive consumption of memory.

You could use EntityManagerFactory for all clients and manipulate DataSource to go to the correct database.

Note that this only works well when all clients are in the same instance of the database, but in schemas or different users, so you can reuse the same pool connections and only redirect the connection to the correct schema .

This basically works by putting a logic in the getConnection method of DataSource used in your application, so that the returned connection is pointing to the correct database.

Since all Java database access architecture, even using Hibernate, goes through getConnection , this would work transparently for your application.

An example of a% real_comment, however removed the specific parts, would be as follows:

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DelegatingDataSource;

/**
 * Set schema dynamically when a new connection is created.
 * It depends on the request.
 */
public class ContextDelegatingDataSource extends DelegatingDataSource {

    final Logger logger = LoggerFactory.getLogger(ContextDelegatingDataSource.class);

    public ContextDelegatingDataSource(DataSource dataDource) {
        super(dataDource);
    }

    @Override
    public Connection getConnection() throws SQLException {
        final Connection con = super.getConnection();
        String schemaName = currentContextSchema();
        con.createStatement().execute(MessageFormat.format("set search_path to ''{0}''", schemaName));
        return con;
    }

    protected String currentContextSchema() throws SQLException {
        Integer idCliente = ContextoDaRequisicao.getIdCliente();
        return idCliente.toString();
    }

}

This implementation works with the PostgreSQL 9.x database.

As for DataSource , this method would retrieve the client ID of a ContextoDaRequisicao.getIdCliente() variable, that is, stored in the context of the current request. The variable must be defined in a servlet filter or interceptor . In the system in question, a ThreadLocal (Spring) saves the client ID based on the URL, since each client accesses a different path , type HandlerInterceptor .

Considerations

If you are not very familiar with the concepts, this can be quite complicated and perhaps not worth the effort.

However, in an application that has some growth potential it is worth investing a bit in a better solution than keeping too much in memory. Perhaps the help of an architect or developer more experience is a differential.

    
28.04.2016 / 10:03