Spring MVC 4.x @Profile, Datasource configuration, Hibernate 5.x JPA configurations

elevysi · 25 August 2016 |

Moving an application from one environment to the other can be messy. If I want to put the application in production for instance, I need to change a few settings because my development environment does not use the same settings values. Some settings are also useful for development and not appropriate for production, like the logging level. In my case, I also have base upload paths that vary from one instance to the other.
I have a Spring MVC 4.2.1 application that has a jdbc datasource to a mysql database. I use hibernate 5.0.1 as the JPA provider.
To avoid having to change over and over the settings from one environment to the other, I use application properties files and profiles configuration files. The properties file has the settings for the environment while the configuration file will be referring to the properties values.
I have two java classes, one for dev and one for prod. Both are declared with the @Configuration and @Profile annotations. The first indicates that the class is to be considered as a configuration file while the second makes it a profile. The profile annotation has the name of the profile that will be used later on to make it active in web.xml in the form of @Profile(“profile_name”). Each configuration profile has a corresponding application properties file that has the right values for the concerned setting. The properties file is declared through the annotation @PropertySource. Below is my Production profile class:

package com.elevysi.site.profile;

import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

@Profile("prod")
@Configuration
@PropertySource("classpath:META-INF/application-prod.properties")
public class ProductionProfile {
	
	@Autowired
	private Environment environment;

	
	@Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
	      LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
	      em.setDataSource(dataSource());
	      em.setPackagesToScan(new String[] { "com.elevysi.site.entity" });
	 
	      JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
	      em.setJpaVendorAdapter(vendorAdapter);
	      em.setJpaProperties(hibernateProperties());
	 
	      return em;
    }
	
	@Bean
	public DataSource dataSource(){
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
	    
		dataSource.setDriverClassName(environment.getRequiredProperty("db.driver"));
		dataSource.setUrl(environment.getRequiredProperty("db.url"));
		dataSource.setUsername(environment.getRequiredProperty("db.username"));
	    dataSource.setPassword(environment.getRequiredProperty("db.password"));
	    
	     
	    return dataSource;
	}
	
	
	 private Properties hibernateProperties() {
		 
	        Properties properties = new Properties();
	        properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
	        properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
	        properties.put("hibernate.max_fetch_depth", environment.getRequiredProperty("hibernate.max_fetch_depth"));
	        properties.put("hibernate.fetch_size", environment.getRequiredProperty("hibernate.fetch_size"));
	        properties.put("hibernate.batch_size", environment.getRequiredProperty("hibernate.batch_size"));
	        return properties;        
    }
	     

    
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
       JpaTransactionManager transactionManager = new JpaTransactionManager();
       transactionManager.setEntityManagerFactory(emf);
  
       return transactionManager;
    }
	

}

Note that the Dev profile class has exactly the same details as the production one except for the @PropertySource and the @Profile(profile_name) 

import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

@Profile("dev")
@Configuration
@PropertySource("classpath:META-INF/application-dev.properties")
public class DevelopmentProfile {

Both properties files will enclose the same properties with different values:

#DB properties:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/db_name
db.username=your_sql_username
db.password=ypur_sql_password

#Hibernate Configuration:
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.show_sql=false
hibernate.max_fetch_depth=3
hibernate.fetch_size=50
hibernate.batch_size=10
entitymanager.packages.to.scan=com.elevysi.site.entity

To recap, I have two configuration profiles:
ProductionProfile using application-dev.properties
DevelopmentProfile using application-prod.properties
To make one or several profiles active, refer to your web.xml and add the following:

<context-param>
	<param-name>spring.profiles.active</param-name>
	<param-value>dev</param-value>
</context-param>

In the above code, adapt the active profile by changing the <param-value> to the profile name.