0

I want to implement environment profiles in my Spring JSON API application to handle multiple environments, development, testing, production. I've seen examples such as Unable to use JNDI DataSource provided by Tomcat in Spring?. I really only need to handle different database settings at this point but later I may need additional bean configuration depending on environment.

My persistence.xml currently looks like this.

<?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="BPPersistenceUnit" transaction-type="RESOURCE_LOCAL">

        <!-- JNDI datasource -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <non-jta-data-source>java:comp/env/jdbc/BloodPressureDB</non-jta-data-source>

        <!-- Hibernate Settings -->
        <properties>

            <!-- java persistence settings -->
            <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://127.0.0.1:5432/jkratz" />
            <property name="javax.persistence.jdbc.user" value="tomcat" />
            <property name="javax.persistence.jdbc.password" value="t0mc@t2014" />

            <!-- hibernate settings -->
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.connection.charSet" value="UTF-8" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL9Dialect" />
            <property name="hibernate.hbm2ddl.auto" value="validate" />

        </properties>

    </persistence-unit>
</persistence>

I've seen different examples so I'm a little confused how to implement.

  1. Can you still use the Tomcat datasource configuration? If so how does it know which profile to use?

  2. How do you configure persistence.xml for multiple environments? Should the jdbc connection props even be in persistence.xml

  3. How do you set the active profile without recompiling?

Community
  • 1
  • 1
greyfox
  • 6,426
  • 23
  • 68
  • 114

2 Answers2

2

1. Can you still use the Tomcat datasource configuration? If so how does it know which profile to use?

You can use the Tomcat datasourse configuration however the datasource definition will have nothing to do with Spring or Spring profiles. If you are using JNDI approach then you may have multiple datasources defined and can specify that one used in you application via profile properties however the actual definition will have nothing to do with Spring.

Or, you can use a non-JNDI datasource i.e configured in Spring and perhaps using profiles.

2. How do you configure persistence.xml for multiple environments? Should the jdbc connection props even be in persistence.xml

No, you only need a minimal persistence.xml when using Spring or perhaps not at all.

3. How do you set the active profile without recompiling?

You can't

The following is all you need to get up and running with a non-JNDI datasource.

Example Spring Configuration (non-JNDI):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context 
          http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <jpa:repositories base-package="uk.co.certait.spring.data.repository" />

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="jpaVendorAdapter" ref="jpaAdapter"></property>
        <property name="persistenceUnitName" value="persistenceUnit" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.ddl.auto}</prop>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show.sql}</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.cache.use_second_level_cache">${hibernate.enable.cache}</prop>
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
            </props>
        </property>
    </bean>

    <bean id="jpaAdapter"
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

    <bean class="org.springframework.orm.jpa.JpaTransactionManager"
        id="transactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
</beans>

Example Spring Datasource Definition

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>${database.driver}</value>
        </property>
        <property name="url">
            <value>${database.url}</value>
        </property>
        <property name="username">
            <value>${database.username}</value>
        </property>
        <property name="password">
            <value>${database.password}</value>
        </property>
    </bean>

</beans>

Example Spring Profile Definition:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <beans profile="default">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/hsqldb.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="hsqldb">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/hsqldb.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="mysql">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/mysql.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="mssql">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/mssql.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

</beans>

Minimal Persisteance.XML

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="persistenceUnit"
        transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
            <property name="hibernate.connection.charSet" value="UTF-8" />
        </properties>
    </persistence-unit>
</persistence>

Sample Profile Properties File:

#MySQL
#database.url=jdbc:mysql://localhost:3306/test
database.url=jdbc:log4jdbc:mysql://localhost:3306/test
#database.driver=com.mysql.jdbc.Driver
database.driver=net.sf.log4jdbc.DriverSpy
database.username=root
database.password=password
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

hibernate.show.sql=false
hibernate.ddl.auto=create
hibernate.enable.cache=false
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • Thanks for the reply and code example! Question about JNDI vs Spring Datasource, is there any trade offs or draw backs to using JNDI over Non JNDI? – greyfox Sep 29 '14 at 15:36
1
  1. Since I didn't have much in common with Tomcat I will just provide a link to a similar question:
    Using dynamic Datasource with Tomcat
  2. JPA allows to define multiple persistence units in a single persistence.xml file so you would at least need to change their names to be unique within scope of its packaging (in terms of Web Archive, EJB JAR, etc.):

    <?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="BPPersistenceUnit-postgres" 
                          transaction-type="RESOURCE_LOCAL">
            <!-- JNDI datasource -->
            ...
            <!-- properties -->
            ...
        </persistence-unit>
        <persistence-unit name="BPPersistenceUnit-mysql" 
                          transaction-type="RESOURCE_LOCAL">
            <!-- JNDI datasource -->
            ...
            <!-- properties -->
            ...
        </persistence-unit>
    </persistence>
    
  3. JPA allows to specify persistence provider properties at runtime by using an overloaded createEntityManagerFactory method:

    // dynamic configuration for PostgreSQL
    Map<String, String> props = new HashMap<>();
    props.put("javax.persistence.jdbc.driver", "org.postgresql.Driver");
    ...
    EntityManagerFactory pgEmf = 
    Persistence.createEntityManagerFactory("BPPersistenceUnit-postgres", props);
    
    // dynamic configuration for MySQL
    ...
    

    The properties passed to the method are combined with those already specified in persistence.xml so this way you can get truly configurable, multi-purpose and dynamic configuration.

Community
  • 1
  • 1
wypieprz
  • 7,981
  • 4
  • 43
  • 46