persistence.xml for development and other for production?

Asked

Viewed 1,483 times

1

I have my persistence.xml configured for a local database.

<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/meubanco"/>
<property name="hibernate.connection.username" value="usuario"/>
<property name="hibernate.connection.password" value="senha"/>

It turns out that the development and production environments should have different configurations, because in production the BD may be located on another server and have different user and password.

How I do it automatically?

Current Multi Tenancy Structure

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="onlinePU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <class>br.com.teste.model.Cliente</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3307/online"/>
      <property name="hibernate.connection.username" value="user"/>
      <property name="hibernate.connection.password" value="senha"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
      <property name="hibernate.show_sql" value="false"/>
      <property name="hibernate.format_sql" value="false"/>
      <!--Configuracao do Hibernate para MultiTenant-->
      <property name="hibernate.multiTenancy" value="SCHEMA"/>
      <property name="hibernate.tenant_identifier_resolver" value="br.com.teste.multitenant.SchemaResolver"/>
      <property name="hibernate.multi_tenant_connection_provider" value="br.com.teste.multitenant.MultiTenantProvider"/>
      <property name="hibernate.enable_lazy_load_no_trans" value="true"/>
      <property name="hibernate.c3p0.acquire_increment" value="2"/>
      <property name="hibernate.c3p0.iddle_teste_period" value="70"/>
      <property name="hibernate.c3p0.max_size" value="5"/>
      <property name="hibernate.c3p0.max_statements" value="0"/>
      <property name="hibernate.c3p0.min_size" value="1"/>
      <property name="hibernate.c3p0.timeout" value="60"/>
    </properties>
  </persistence-unit>
</persistence>

Multitenantprovider

public class MultiTenantProvider implements MultiTenantConnectionProvider, ServiceRegistryAwareService  {

    private static final long serialVersionUID = 4368575201221677384L;

    private C3P0ConnectionProvider connectionProvider = null;

    @Override
    public boolean supportsAggressiveRelease() {
        return false;
    }

    @Override
    public void injectServices(ServiceRegistryImplementor serviceRegistry) {
        Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings();

        connectionProvider = new C3P0ConnectionProvider();
        connectionProvider.injectServices(serviceRegistry);
        connectionProvider.configure(lSettings);
    }

    @Override
    public boolean isUnwrappableAs(Class clazz) {
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> clazz) {
        return null;
    }

    @Override
    public Connection getAnyConnection() throws SQLException {
        final Connection connection = connectionProvider.getConnection();
        return connection;
    }

    @Override
    public Connection getConnection(String tenantIdentifier) throws SQLException {
        final Connection connection = getAnyConnection();
        try {
            connection.createStatement().execute("use " + tenantIdentifier + ";");
        }
        catch (SQLException e) {
            throw new ClienteInvalidoException("Cliente inválido!");
        }
        return connection;
    }

    @Override
    public void releaseAnyConnection(Connection connection) throws SQLException {
        try {
            connection.createStatement().execute("use online;");
        }
        catch (SQLException e) {
            throw new ClienteInvalidoException("Cliente inválido!");
        }
        connectionProvider.closeConnection(connection);
    }

    @Override
    public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
        releaseAnyConnection(connection);
    }
}
  • What is the real need to perform automatically?

  • Programmers' workstations have their own mysql (localhost). Already on the production server mysql will be on another dedicated server. I don’t want to have to keep exchanging the connection data whenever I have to deploy the application.

  • I don’t know how your deployment structure works, but here at the company, some files at deploy time are disregarded when it comes to getting up to production.

  • @Diegosantos I have a Jenkins server that periodically checks the changes and builds the project. At some point the person responsible for updating the app takes this build and deploys it to the production server.

  • 1

    The JNDI It’s nice when you let the container create the connection, because it simplifies. In this case you wrote code to create the connection, so you are already ready to get the parameters of a configuration file as I suggested in my reply. You may have a configuration file with the default information and devs change on your machine but don’t commit, or rather a file dev and another prod, Prod is renamed and included in the distribution package during continuous integration while the dev is ignored. But I would do this with a file apart from persistence.xml.

2 answers

1


If I understand correctly, you want a dynamic bank access using an architecture multi-tenant. There are several exits.

Personally I would use Spring to define different profiles as dev, uat and prod.

Then it would be possible to define different versions of EntityManagerFactory or a single version initialized via files properties specific to each environment.

A more "manual" approach is to generate WAR or EAR packages for different environments. This is quite easy with Maven.

It would also be possible to define different persistence Units and use each one according to the environment. This can be done by instantiating the EntityManagerFactory through the class Persistence, because there is a version of the method createEntityManagerFactory who receives a String, which is the name of persistence Unit.

Another alternative is to use the same persistence Unit, but pass a map with the user and password of each connection, loaded from some configuration file. This can be done through another version of the method createEntityManagerFactory class Persistence or the method createEntityManager of EntityManagerFactory already created.

  • Look at the code I just posted... I already have a multi-tenant structure working. I tried to make the connection via JNDI but I think it conflicts with C3P0... I don’t know which implementation I could use instead of C3p0connectionprovider.

  • @Nilsonuehara No need to use JNDI. It is possible to pass the properties hibernate.connection.username and hibernate.connection.password on the excerpt map connectionProvider.configure(lSettings);, instead of leaving fixed on persistence.xml. You can, for example, check some environment variable that tells you what environment you are in and then load the respective user and password from some separate file.

  • I get it... I’m gonna run a test and I’ll get it right.

  • It worked! I created <Environment name="db_url"... in the server context.xml and set the parameters before connectionProvider.configure(! Thanks @utluiz

  • @Nilsonuehara Legal, good to know!

-1

The persistence.xml is not a production configuration file. While you can provide all details of access to the database in this file, you should not do so for production use or approval - in these cases the database access setting needs to be open to the role of "implanter" system. while the file persistence.xml is the responsibility of the role "developer" of the system.

Although eventually the same person assumes these two roles, as this file belongs to the set of source files and is distributed already configured, you would literally have a system change every time the environment changed (databases change place, name, username and password also change).

If you use an application server (Glassfish, for example), do the database access setting using resource JNDI. If not, consider placing the database access standards in an encrypted configuration file.

  • I use Tomcat. I tried to configure the connection via JNDI, but I ran into some errors. Maybe because I was using a multitentant structure. I was only able to make it work by putting all the data in the same persistence.

  • I think it should work anyway. Maybe if you post the problems found... Anyway, if you use continuous integration, you can write a script to replace a file in the distribution package. I wouldn’t do that to the persistence.xml, but... it’s up to you.

  • I think the problem is that, in the current configuration, I am using C3P0 (it seems that it would not be correct to use it with Tomcat). I will post the complete classes to give a broader idea.

Browser other questions tagged

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