Improvements to Entitymanegedfactory JPA

Asked

Viewed 200 times

1

Staff I am working as follows , I have 2 PersistenceUnit , one from the Manager’s database , which is where I find the data from the clients' databases ,another from the clients' database , which I take from the Manager’s data, I want to know if I’m doing it the right way, or if I can improve, I have several clients simultaneously accessing the system today, 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>
  • Felipe, I created an answer based on my comments and that also tries to answer your question.

1 answer

2


It’s an extensive and complex subject, so I’ll try some point 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.

There’s an article that explains how to use virtually the same approach to maintaining a EntityManagerFactory per bank, but allowing to inject the EntityManager transparently:

JPA Multitenancy

Do not use session unnecessarily

Another thing I see no need to use the session if you already keep a static list of Emfs.

Why not access directly by a static method or better yet, as mentioned above, by injecting with CDI or Spring?

Not create a EntityManagerFactory by bank

Nor do I like the idea of maintaining many instances of EntityManagerFactory in remembrance.

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

You could use a EntityManagerFactory for all customers and manipulate the DataSource to go in the correct bank.

Note that this only works well when all customers are in the same bank instance, however at schemas or different users, so that you can reuse the same pool connections and just redirect the connection to the schema correct.

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

Like all bank access architecture in Java, even using Hibernate, goes through getConnection, this would work transparently for your application.

An example of a DataSource real, but 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 to the ContextoDaRequisicao.getIdCliente(), this method would recover the client ID from a variable ThreadLocal, 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 HandlerInterceptor (Spring) saves the URL-based client ID, as each client accesses one path different, like sistema.com.br/id-cliente/funcao-do-sistema.

Considerations

If you don’t have much familiarity with the concepts, this can be quite complicated and maybe not worth the effort.

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

  • I really need to study about this, because there will be a lot of people accessing the app , any doubt I put here !!!

Browser other questions tagged

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