Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting JPA persistence properties dynamically

Let's say I have the following persistence.xml with the connection url, user & password all hard-coded.

The following is for Hibernate 3.2. For Hibernate 3.5 ++, we have to change "hibernate.connection" to "javax.persistence". But let me ask this question regardless of the literals "hibernate.connection" or "javax.persistence".

<persistence-unit name="obamacare" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <exclude-unlisted-classes>false</exclude-unlisted-classes>
  <properties>
    <property name="hibernate.archive.autodetection" value="class, hbm"/>
    <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver"/>
    <property name="hibernate.connection.url" value="blah blah blah"/>
    <property name="hibernate.connection.username" value="careuser"/>
    <property name="hibernate.connection.password" value="carepass"/>
    <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/>
    <property name="hibernate.show_sql" value="true"/>
  </properties>
  </persistence-unit>
</persistence>

However, we need to set the url, user & password dynamically. There is a proposed authentication server which serves the url, user & password. So that we do not need to individually configure the myriads of webapps that use some form of jdbc, hibernate or JPA. Besides the security issue of not wanting to store/manage passwords on visible text files.

As far as JPA is concerned, how can I set these JPA properties dynamically? I am seeking two sets of answers:

  1. For a solution that is JPA vendor independent (toplink, eclipselink, hibernate, etc) - Is there any JPA functionality that would let me set these three properties dynamically?

  2. If I am allowed to depend totally on Hibernate, besides the possible JPA only avenue, is there a way to achieving this without involving Spring framework (which seems like a huge monstrosity with tentacles all over the place)?

I would be elated if you also wish to throw in two cents/quids/rupees on JNDI and how I could use it to replace the functionality of persistence.xml properties. However, that is not the priority of the question.

like image 270
Blessed Geek Avatar asked Jul 27 '12 20:07

Blessed Geek


2 Answers

It depends how you bootstrap your EntityManagerFactory. The 2 spec defined methods each allow you to pass in a java.util.Map of values. These values are supposed to take precedence over values defined in persistence-unit.

In the "SE approach" no problem since the bootstrap process is typically controlled by your app: javax.persistence.Persistence#createEntityManagerFactory(String puName, Map config. Now you might have problems here if something else (ahem, Spring) is "managing" the EMF for you...

In the "EE approach" I am not aware of a good global approach. This Map of values still exists in the bootstrapping, but the problem is that the EE container is the one calling this method.

One Hibernate-specific approach that would work in either case would be to use config variable replacement. So in your persistence-unit you'd define user name or password using ${some.key} and Hibernate would replace those for you. Whether this would work through really depends how you want to set these values ultimately; Hibernate still needs access to a config value named some.key for this to work...

Yet another "global approach"... The "EE approach" to bootstrap the EMF is for the container to instantiate the javax.persistence.spi.PersistenceProvider and call its javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory. createContainerEntityManagerFactory has an interesting signature here. Essentially it is passed a javax.persistence.spi.PersistenceUnitInfo which is an object representation of the parsed persistence unit plus some other stuff. An option would be to use this approach to bootstrap and pass in an instance of javax.persistence.spi.PersistenceUnitInfo that you construct your self. javax.persistence.spi.PersistenceProvider is an interface. To instantiate it, you'd need to know the provider you want to use and the FQN to their impl. But thats usually not a problem since these are pretty well known.

You are specifically asking about JDBC connection creation/pooling. You have additional options there specifically. You could have your "credential service" create DataSources and your JPA provider simply use that DataSource. All JPA providers support locating DataSources via JNDI lookup. In "EE bootstrapping" providers can also be passed a DataSource to use via PersistenceUnitInfo#getJtaDataSource and/or PersistenceUnitInfo#getNonJtaDataSource. Hibernate alternately accepts a DataSource instance in place of the typical DataSource JNDI name setting. If you don't want to use DataSource (for some odd reason), a Hibernate-specific alternative is to implement Hibernate's ConnectionProvider contract yourself, this is the contract (interface) Hibernate uses to obtain and release JDBC connections when it needs to. Implementing ConnectionProvider you could configure the underlying Connections in any way you wanted to.

Lots of options :)

like image 78
Steve Ebersole Avatar answered Sep 18 '22 20:09

Steve Ebersole


For your second question I can provide a Hibernate-only solution.

package dev.stackoverflow;

import java.util.Properties;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class DynamicHibernateSessionFactory {
    public Session setProperties(final String provider,
                                 final Boolean excludeUnlisted,
                                 final Properties properties) {
        properties.setProperty("provider", provider);
        properties.setProperty("exclude-unlisted-classes", excludeUnlisted.toString());
        Configuration configuration = new Configuration();
        configuration.setProperties(properties);
        SessionFactory sessionFactory = configuration.configure().buildSessionFactory();
        return sessionFactory.openSession();
    }
}
like image 43
chris.tian Avatar answered Sep 18 '22 20:09

chris.tian