SpringMVC4 + Spring Data JPA + SpringSecurity configuration using JavaConfig

In this article we will see how to configure and integrate SpringMVC4, Spring Data JPA with Hibernate and SpringSecurity using JavaConfig.

1. First let’s configure all the necessary dependencies in pom.xml



  4.0.0
  com.sivalabs
  springmvc-datajpa-security-demo
  1.0
  war 
  
	
		1.7
		4.11
		1.7.5
		1.0.13
		4.0.0.RELEASE
		1.4.1.RELEASE
		3.2.0.RELEASE
		4.2.6.Final
		1.7.2
		5.1.26
		2.3.1
		1.2.2
		3.1
	
	
	
		${project.artifactId}
		
			
				org.apache.maven.plugins
				maven-compiler-plugin
				3.1
				
					${java.version}
					${java.version}
				
			
		
	

	
	
		
			org.slf4j
			jcl-over-slf4j
			${slf4j.version}
		

		
			org.slf4j
			slf4j-api
			${slf4j.version}
		
		
		
			ch.qos.logback
			logback-classic
			${logback.version}
				

				
		
			org.springframework
			spring-context-support
			
				
					commons-logging
					commons-logging
				
			
		
		
		
			org.springframework
			spring-webmvc
		
		
			org.springframework
			spring-test
		
		
		
		
			org.springframework.data
			spring-data-jpa
			${spring-data-jpa.version}
		

		
			org.hibernate
			hibernate-entitymanager
			${hibernate.version}
		
		
		
		
			org.springframework.security
			spring-security-core
			${spring-security.version}
		
		
			org.springframework.security
			spring-security-web
			${spring-security.version}
		
		
			org.springframework.security
			spring-security-config
			${spring-security.version}
		
		
			org.springframework.security
			spring-security-taglibs
			${spring-security.version}
			
		
		
			org.aspectj
			aspectjweaver
			${aspectj.version}
		
		
			org.aspectj
			aspectjrt
			${aspectj.version}
			

		
		
			junit
			junit
			${junit.version}
			test
				

		
		
			mysql
			mysql-connector-java
			${mysql.version}
		
				
		
			commons-dbcp
			commons-dbcp
			${commons-dbcp.version}
		
				
		
			com.fasterxml.jackson.core
			jackson-databind
			${jackson-json.version}
		
		
		
		    javax.mail
		    mail
		    1.4.3
	    
	    
		
		
			javax.servlet
			javax.servlet-api
			3.0.1
			provided
		

		
			taglibs
			standard
			1.1.2
			compile
		
		
			jstl
			jstl
			1.2
			compile
		
	

	
		
			
				org.springframework
				spring-framework-bom
				${spring.version}
				pom
				import
					
		
	
	


2. Configure database connection properties and email settings in application.properties


################### DataSource Configuration ##########################

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=admin

init-db=false

################### Hibernate Configuration ##########################

hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update

################### JavaMail Configuration ##########################
smtp.host=smtp.gmail.com
smtp.port=465
smtp.protocol=smtps
smtp.username=sivaprasadreddy.k@gmail.com
smtp.password=
support.email=sivaprasadreddy.k@gmail.com

3. Configure common Service Layer beans such as PropertySourcesPlaceholderConfigurer and JavaMailSender etc in com.sivalabs.springapp.config.AppConfig.java



package com.sivalabs.springapp.config;

import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@ComponentScan(basePackages={"com.sivalabs.springapp"},
		excludeFilters=@ComponentScan.Filter(type=FilterType.REGEX, pattern={"com.sivalabs.springapp.web.*"}))
@PropertySource(value = { "classpath:application.properties" })
@EnableScheduling
@EnableAspectJAutoProxy
@EnableCaching
public class AppConfig 
{
	@Autowired
	private Environment env;

	@Bean
	public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer()
	{
		return new PropertySourcesPlaceholderConfigurer();
	}
	
	@Bean
	public JavaMailSenderImpl javaMailSenderImpl() {
		JavaMailSenderImpl mailSenderImpl = new JavaMailSenderImpl();
		mailSenderImpl.setHost(env.getProperty("smtp.host"));
		mailSenderImpl.setPort(env.getProperty("smtp.port", Integer.class));
		mailSenderImpl.setProtocol(env.getProperty("smtp.protocol"));
		mailSenderImpl.setUsername(env.getProperty("smtp.username"));
		mailSenderImpl.setPassword(env.getProperty("smtp.password"));

		Properties javaMailProps = new Properties();
		javaMailProps.put("mail.smtp.auth", true);
		javaMailProps.put("mail.smtp.starttls.enable", true);

		mailSenderImpl.setJavaMailProperties(javaMailProps);

		return mailSenderImpl;
	}
		
	@Bean
	public CacheManager cacheManager()
	{
		return new ConcurrentMapCacheManager();
	}
}

Observe that we have excluded the package “com.sivalabs.springapp.web.*” from component scanning using new REGEX excludeFilter type.
If we don’t exclude web related packages and tries to run JUnit test for service layer beans we will encounter the following Exception:

java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling

Also note that we have enabled Caching using @EnableCaching, so we should declare CacheManager bean.

4. Configure Persistence Layer beans in com.sivalabs.springapp.config.PersistenceConfig.java as follows:


package com.sivalabs.springapp.config;

import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages="com.sivalabs.springapp.repositories")
public class PersistenceConfig 
{
	@Autowired
	private Environment env;

	@Value("${init-db:false}")
	private String initDatabase;
	
	@Bean
	public PlatformTransactionManager transactionManager()
	{
		EntityManagerFactory factory = entityManagerFactory().getObject();
		return new JpaTransactionManager(factory);
	}

	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory()
	{
		LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();

		HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		vendorAdapter.setGenerateDdl(Boolean.TRUE);
		vendorAdapter.setShowSql(Boolean.TRUE);

		factory.setDataSource(dataSource());
		factory.setJpaVendorAdapter(vendorAdapter);
		factory.setPackagesToScan("com.sivalabs.springapp.entities");

		Properties jpaProperties = new Properties();
		jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
		factory.setJpaProperties(jpaProperties);

		factory.afterPropertiesSet();
		factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
		return factory;
	}

	@Bean
	public HibernateExceptionTranslator hibernateExceptionTranslator()
	{
		return new HibernateExceptionTranslator();
	}
	
	@Bean
	public DataSource dataSource()
	{
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
		dataSource.setUrl(env.getProperty("jdbc.url"));
		dataSource.setUsername(env.getProperty("jdbc.username"));
		dataSource.setPassword(env.getProperty("jdbc.password"));
		return dataSource;
	}
	
	@Bean
	public DataSourceInitializer dataSourceInitializer(DataSource dataSource) 
	{
		DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
		dataSourceInitializer.setDataSource(dataSource);
		ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
		databasePopulator.addScript(new ClassPathResource("db.sql"));
		dataSourceInitializer.setDatabasePopulator(databasePopulator);
		dataSourceInitializer.setEnabled(Boolean.parseBoolean(initDatabase));
		return dataSourceInitializer;
	}	
}

Here we have configured DataSource and JPA EntityManagerFactory bean using Hibernate implementation.
Also we have configured DataSourceInitializer bean to initialize and populate our tables with seed data. We can enable/disable executing this db.sql script by changing init-db property value in application.properties.
And finally we have enabled Spring Data JPA repositories scanning using @EnableJpaRepositories to scan “com.sivalabs.springapp.repositories” package for JPA repository interfaces.

5. Now let us configure Web related beans in com.sivalabs.springapp.web.config.WebMvcConfig.java


package com.sivalabs.springapp.web.config;

import java.util.Properties;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;


@Configuration
@ComponentScan(basePackages = { "com.sivalabs.springapp.web"}) 
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter
{
	@Override
	public void addViewControllers(ViewControllerRegistry registry)
	{
		super.addViewControllers(registry);
		registry.addViewController("login/form").setViewName("login");		
		registry.addViewController("welcome").setViewName("welcome");
		registry.addViewController("admin").setViewName("admin");
	}

	@Bean
	public ViewResolver resolver()
	{
		InternalResourceViewResolver url = new InternalResourceViewResolver();
		url.setPrefix("/WEB-INF/jsp/");
		url.setSuffix(".jsp");
		return url;
	}

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry)
	{
		registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
	}

	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
	{
		configurer.enable();
	}

	@Bean(name = "messageSource")
	public MessageSource configureMessageSource()
	{
		ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
		messageSource.setBasename("classpath:messages");
		messageSource.setCacheSeconds(5);
		messageSource.setDefaultEncoding("UTF-8");
		return messageSource;
	}

	@Bean
	public SimpleMappingExceptionResolver simpleMappingExceptionResolver()
	{
		SimpleMappingExceptionResolver b = new SimpleMappingExceptionResolver();
		Properties mappings = new Properties();
		mappings.put("org.springframework.dao.DataAccessException", "error");
		b.setExceptionMappings(mappings);
		return b;
	}
}

6. Configure DispatcherService using AbstractAnnotationConfigDispatcherServletInitializer convinient class.



package com.sivalabs.springapp.web.config;

import javax.servlet.Filter;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.sivalabs.springapp.config.AppConfig;

public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{

	@Override
	protected Class[] getRootConfigClasses()
	{
		return new Class[] { AppConfig.class};
	}

	@Override
	protected Class[] getServletConfigClasses()
	{
		return new Class[] { WebMvcConfig.class };
	}

	@Override
	protected String[] getServletMappings()
	{
		return new String[] { "/" };
	}

	@Override
    protected Filter[] getServletFilters() {
       return new Filter[]{ new OpenEntityManagerInViewFilter()	  };
    }

}

Here few things to note are we configured AppConfig.class as RootConfig classes and WebMvcConfig.class as ServletConfigClasses which is similar to how we configure in web.xml using ContextLoaderListener and DispatcherServlet’s contextConfigLocation .
Also we have registered OpenEntityManagerInViewFilter to enable lazy loading of JPA entity graphs in view rendering phase.

7. Let us configure SpringSecurity.

First let us create a SecurityUser class which extends our application specific User class and implements org.springframework.security.core.userdetails.UserDetails.


package com.sivalabs.springapp.web.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.sivalabs.springapp.entities.Role;
import com.sivalabs.springapp.entities.User;

public class SecurityUser extends User implements UserDetails
{

	private static final long serialVersionUID = 1L;
	public SecurityUser(User user) {
		if(user != null)
		{
			this.setId(user.getId());
			this.setName(user.getName());
			this.setEmail(user.getEmail());
			this.setPassword(user.getPassword());
			this.setDob(user.getDob());
			this.setRoles(user.getRoles());
		}		
	}
	
	@Override
	public Collection getAuthorities() {
		
		Collection authorities = new ArrayList<>();
		Set userRoles = this.getRoles();
		
		if(userRoles != null)
		{
			for (Role role : userRoles) {
				SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleName());
				authorities.add(authority);
			}
		}
		return authorities;
	}

	@Override
	public String getPassword() {
		return super.getPassword();
	}

	@Override
	public String getUsername() {
		return super.getEmail();
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}	
}

We will implement a custom UserDetailsService and use Spring Data JPA repositories to load User details.


package com.sivalabs.springapp.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.sivalabs.springapp.entities.User;
import com.sivalabs.springapp.services.UserService;
import com.sivalabs.springapp.web.config.SecurityUser;

@Component
public class CustomUserDetailsService implements UserDetailsService
{
	@Autowired
	private UserService userService;
	
	@Override
	public UserDetails loadUserByUsername(String userName)
			throws UsernameNotFoundException {
		User user = userService.findUserByEmail(userName);
		if(user == null){
			throw new UsernameNotFoundException("UserName "+userName+" not found");
		}
		return new SecurityUser(user);
	}
}

Now create com.sivalabs.springapp.config.SecurityConfig.java which contains SpeingSecurity related bean definitions.


package com.sivalabs.springapp.config;

import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
//import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
	@Autowired
	private DataSource dataSource;

	@Autowired
	private CustomUserDetailsService customUserDetailsService;

	@Override
    protected void configure(AuthenticationManagerBuilder registry) throws Exception {
	/*
        registry
        .inMemoryAuthentication()
        .withUser("siva")
          .password("siva")
          .roles("USER")
          .and()
        .withUser("admin")
          .password("admin")
          .roles("ADMIN","USER");
        */
        
        //registry.jdbcAuthentication().dataSource(dataSource);
	registry.userDetailsService(customUserDetailsService);
    }


	  @Override
	  public void configure(WebSecurity web) throws Exception {
	    web
	      .ignoring()
	         .antMatchers("/resources/**");
	  }

	  @Override
	  protected void configure(HttpSecurity http) throws Exception {
	    http
	    .csrf().disable()
	    .authorizeRequests()
	        .antMatchers("/login","/login/form**","/register","/logout").permitAll()
	        .antMatchers("/admin","/admin/**").hasRole("ADMIN")
	        .anyRequest().authenticated()
	        .and()
	    .formLogin()
	        .loginPage("/login/form")
	        .loginProcessingUrl("/login")
	        .failureUrl("/login/form?error")
	        .permitAll();
	  }
}

Update SpringWebAppInitializer which we created eariler to add SecurityConfig configuration class.


public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
	@Override
	protected Class[] getRootConfigClasses()
	{
		return new Class[] { AppConfig.class};
		//As we have SecurityConfig.java in same package as AppConfig.java 
                // and enabled ComponentScan to scan "com.sivalabs.springapp.config" we don't need to explicitely configure it.
		//otherwise we should add SecurityConfig.class to getRootConfigClasses()
		//return new Class[] { AppConfig.class, SecurityConfig.class};
	}
	...
	...
	@Override
    protected Filter[] getServletFilters() {
       return new Filter[]{ 
    		   new DelegatingFilterProxy("springSecurityFilterChain"),
    		   new OpenEntityManagerInViewFilter()};
    } 

}	

As per our SpringSecurity custom Form Login configuration, we will use the following login form in login.jsp.



<%@taglib uri="http://www.springframework.org/tags"  prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>



Login






		
Invalid UserName and Password.
You have been logged out.

User Login Form

Once we successfully login we can obtain the authenticated use details using and secure parts of the view using as follows:


Email:

Administration

Logout

You can find the source code at github https://github.com/sivaprasadreddy/sivalabs-blog-samples-code/tree/master/springmvc-datajpa-security-demo

There are few issues while running the same application on JBoss AS 7.1. I have made few changes to run on JBossAS7.1 and published code at https://github.com/sivaprasadreddy/sivalabs-blog-samples-code/tree/master/springmvc-datajpa-security-demo-jboss7

Spring3+JPA2+JavaEE6AppServer = Confusion Over Configuration

Spring is great, JavaEE6 is great and latest JavaEE6 Application servers are also great. This post is not a rant on Spring Vs JavaEE6, but my experience of porting a Spring3+JPA2(Hibernate) application on JBoss AS-7.1 App Server.

My application requirement is very simple: Developing a couple of SOAP based webservices using Spring3.1 and JPA2(Hibernate) and host it on JBoss AS 7.1.

So I started creating a multi-module maven project with one jar module containing the service implementations using Spring & JPA and another war module which exposes those services as SOAP based webservices. But the key part is services needs to talk to multiple databases for some of the service methods.

 I am aware of JPA2 integration support from Spring without persistence.xml and cool packagesToScan attribute which makes life a bit easier. I configured 2 dataSources, 2 LocalContainerEntityManagerFactoryBeans, registered 2 JpaTransactionManagers and enabled Annotation based Transaction Management Support.

	<tx:annotation-driven transaction-manager="txnManager1"/>
<tx:annotation-driven transaction-manager="txnManager2"/>

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/><!-- This will throw error because it found multiple EntityManagerFactory beans -->

<bean id="txnManager1"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="emf1"/>

<bean id="txnManager2"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="emf2"/>

<bean id="emf1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Sivalabs1PU"></property>
<property name="dataSource" ref="dataSource1"></property>
<property name="jpaVendorAdapter">
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="${hibernate.show_sql}"/>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
</props>
</property>
<property name="packagesToScan" value="com.sivalabs.springdemo.entities"></property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>

</bean>

<bean id="emf2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Sivalabs2PU"></property>
<property name="dataSource" ref="dataSource2"></property>
<property name="jpaVendorAdapter">
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="${hibernate.show_sql}"/>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
</props>
</property>
<property name="packagesToScan" value="com.sivalabs.springdemo.entities"></property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>

</bean>

<bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${node1.jdbc.driverClassName}"></property>
<property name="url" value="${node1.jdbc.url}"></property>
<property name="username" value="${node1.jdbc.username}"></property>
<property name="password" value="${node1.jdbc.password}"></property>
</bean>

<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${node2.jdbc.driverClassName}"></property>
<property name="url" value="${node2.jdbc.url}"></property>
<property name="username" value="${node2.jdbc.username}"></property>
<property name="password" value="${node2.jdbc.password}"></property>
</bean>

After this I realized to bind Entitymanager with the correct PersistenceUnit I need to give persistenceUnitName to LocalContainerEntityManagerFactoryBean.

	
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
<property name="persistenceUnits" >
<map>
<entry key="unit1" value="Sivalabs1PU"/>
<entry key="unit2" value="Sivalabs2PU"/>
</map>
</property>
</bean>

<bean id="emf1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Sivalabs1PU"></property>
<property name="dataSource" ref="dataSource1"></property>
....
....
</bean>

<bean id="emf2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Sivalabs2PU"></property>
<property name="dataSource" ref="dataSource2"></property>
....
....
</bean>

Then in my Service Bean EntityManagers and transaction managers are glued together as follows:

@Service
public class AdminUserService implements UserService
{
@PersistenceContext(unitName="Sivalabs1PU")
private EntityManager sivalabs1EM;
@PersistenceContext(unitName="Sivalabs2PU")
private EntityManager sivalabs2EM;

@Override
@Transactional("txnManager1")
public List<User> getAllUsersFromSivalabs1DB() {
return sivalabs1EM.createQuery("from User", User.class).getResultList();
}

@Override
@Transactional("txnManager2")
public List<User> getAllUsersFromSivalabs2DB() {
return sivalabs2EM.createQuery("from User", User.class).getResultList();
}

}

With this setup now I got the Exception saying “No persistence unit with name ‘Sivalabs1PU’ found“. Then after some googling I created META-INF/persistence.xml file as follows:

<persistence>

<persistence-unit name="Sivalabs1PU" transaction-type="RESOURCE_LOCAL">
</persistence-unit>

<persistence-unit name="Sivalabs2PU" transaction-type="RESOURCE_LOCAL">
</persistence-unit>

</persistence>

Now the persistence unit name error got resolved and got other Exception saying “User is not mapped [from User]“. The User class is annotated with @Entity and is in “com.sivalabs.springdemo.entities” package which I configured to “packagesToScan” attribute. I didn’t understand why “packagesToScan” attribute is not working which is working fine without persistence.xml. So for time being I configured entity classes in persistence.xml file.

<persistence>

<persistence-unit name="Sivalabs1PU" transaction-type="RESOURCE_LOCAL">
<class>com.sivalabs.springdemo.entities.User</class>
</persistence-unit>

<persistence-unit name="Sivalabs2PU" transaction-type="RESOURCE_LOCAL">
<class>com.sivalabs.springdemo.entities.User</class>
</persistence-unit>

</persistence>

Finally when I ran my JUnit Test which invokes AdminUserService methods everything looks good and working fine. Then I deployed the war file on JBoss AS 7.1 Server then again got a bunch of errors. JBoss is complaining that “Connection cannot be null when ‘hibernate.dialect’ not set” …. “[PersistenceUnit: Sivalabs1PU] Unable to build EntityManagerFactory”.

After thinking for a couple of minutes, I understood that JBoss server is trying to do what it is supposed to do with “Convention Over Configuration” rules. JBoss is trying to create EntityManagerFactory because it found META-INF/persistence.xml in classpath. But as it doesn’t contain jdbc connection details its throwing Error. 

Again after some googling I found we can rename persistence.xml to something else(spring-persistence.xml) and hook up this new name with Spring as follows:

	<bean id="emf1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Sivalabs1PU"></property>
<property name="persistenceXmlLocation" value="classpath:META-INF/spring-persistence.xml"/>
<property name="dataSource" ref="dataSource1"></property>
....
....
</bean>

<bean id="emf2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Sivalabs2PU"></property>
<property name="persistenceXmlLocation" value="classpath:META-INF/spring-persistence.xml"/>
<property name="dataSource" ref="dataSource2"></property>
....
....
</bean>

Finally I got this application working on my JBoss AS 7.1 successfully(Still I don’t know how many other holes are there that I haven’t yet found).

But here I didn’t understand few Spring concepts:
1. When I try to give persistenceUnitName why Spring is checking for that name to be existed in persistence.xml? Anyway that persistence.xml doesn’t contain anything exception the unit-name!!

2. Why packagesToScan mechanism is failing when used with persistence.xml? Is it a Spring Bug?

Everything seems to be working fine except one thing is missing, a smile on my face which usually I have when working with Spring and Tomcat 🙁

I like Spring framework very much and I am using it since 2006 and I do enjoy while writing Spring code. That doesn’t mean I don’t like CDI, EJB3, JAX-RS 🙂

 Anyway, with all the above exercise I feel like Spring3+JPA2+JavaEE6AppServer=Confusion Over Configuration and it is my(an average java developer) opinion only.

Again say one more time : Spring is great, JavaEE6 is great and latest JavaEE6 Application servers are also great :-).

@GeneratedValue not setting up auto increment in mysql and h2 dialects

Hi,
In earlier versions of Hibernate if we want to have an auto_increment primary key we can use the following:

@Id @GeneratedValue(strategy=GenerationType.AUTO)
@Column(name=”user_id”)
private Integer userId;

But in latest version of Hibernate(may be Hibernate4, whatever is used in JBoss AS7) this doesn’t work as expected. The generated table primary key is not auto_increment column.

To resolve this configure <property name=”hibernate.id.new_generator_mappings” value=”false”> in persistence.xml.

SpringMVC + Hibernate Error: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

While developing a web application using SpringMVC and Hibernate I got “No Hibernate Session bound to thread Exception” becuase of some configuration issue.
Here I am going to explain how I resolved the issue.

I used the SpringMVC/@Controller approach and configured the Web related Spring configuration in dispatcher-servlet.xml as follows:


<context:annotation-config"/>
<context:component-scan base-package="com.sivalabs"/>




p:prefix="/WEB-INF/jsp/" p:suffix=".jsp">

class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename="Messages">



I have configured my business serices and DAOs in applicationContext.xml as follows:



<context:component-scan base-package="com.sivalabs"/>
<context:property-placeholder location="classpath:app-config.properties"/>

<tx:annotation-driven/>

class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory">






UserAccount.hbm.xml
Contact.hbm.xml




${hibernate.dialect}
${hibernate.show_sql}





.....
.....


To enable the transaction management I have used @Transactional annotation on my business services.

package com.sivalabs.homeautomation.useraccounts;
@Service
@Transactional
public class UserAccountsService
{
@Autowired
private UserAccountsDAO userAccountsDAO;

public UserAccount login(Credentials credentials) {
return userAccountsDAO.login(credentials);
}
}

But when I invoked UserAccountsService.login() method I got the the below error:

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:544)
at com.sivalabs.homeautomation.useraccounts.UserAccountsDAO.login(UserAccountsDAO.java:30)
at com.sivalabs.homeautomation.useraccounts.UserAccountsService.login(UserAccountsService.java:25)
at com.sivalabs.homeautomation.useraccounts.LoginController.login(LoginController.java:51)

Here I have enabled the Annotation based configuration using <context:annotation-config/>.
I have configured the base package containing the Spring beans using <context:component-scan base-package=”com.sivalabs”/>.
I have enabled the Annotation based Transaction Management using <tx:annotation-driven/> and @Transactional.

But still I am getting “No Hibernate Session bound to thread” Exception. Why?

Here is the reason:

In Spring reference documentation we can found the below mentioned important note:

“<tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in. This means that, if you put <tx:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services.”

So when my application is started first it loads the beans configured in dispatcher-servlet.xml and then look in applicationContext.xml. As i mentioned “com.sivalabs” as my base-package to scan for Spring beans my business services and DAOs which are annotated with @Service, @Repository will also be loaded by the container. Later when Spring tries to load beans from applicationContext.xml it won’t load my services and DAOs as they are already loaded by parent ApplicaationContext. So the <tx:annotation-driven/> wont be applied for business services or DAOs annotated with @Transactional.

Solution1: If you are following package-by-layer approach:
Probably you may put all your controllers in one package say com.sivalabs.appname.web.controllers

Then change the <context:annotation-config/> configuration in dispatcher-servlet.xml as:

<context:component-scan base-package="com.sivalabs.appname.web.controllers"/>

With this only the controllers annotated with @Controller in com.sivalabs.appname.web.controllers package will be loaded by parent ApplicationContext and rest of the services, DAOs will be loaded by child ApplicationContext.

Solution2: If you are following package-by-feature approach:
If you follow the package-by-feature approach, you will put all the Controller, Service, DAOs related to one feature in one package.
With this the Controllers, Services, DAOs will be spanned across the packages.

Then change the <context:annotation-config/> configuration in dispatcher-servlet.xml as:


<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>

With this only the controllers annotated with @Controller will be loaded by parent ApplicationContext.
And the Services, DAOs will be loaded by child ApplicationContext and <tx:annotation-driven/> will be applied.

SpringMVC3 Hibernate CRUD Sample Application

To learn any web framework starting with a HelloWorld application is a good idea. Once we get familiarity with the framework configuration it would be better to do a CRUD(Create,Read,Update,Delete) application which covers various aspects of a web framework like Validations, Request URL Mappings, Request Parameter Binding,
Pre-populating forms etc.

Now I am going to explain how to write a Simple CRUD application using SpringMVC3, Hibernate and MySQL.
Our Application is ContactsManagements where you can view or search contacts, create new contacts, edit or delete existing contacts.

Step#1: Create the CONTACTS Table

CREATE TABLE  CONTACTS 
(
id int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(45) NOT NULL,
address varchar(45) DEFAULT NULL,
gender char(1) DEFAULT 'M',
dob datetime DEFAULT NULL,
email varchar(45) DEFAULT NULL,
mobile varchar(15) DEFAULT NULL,
phone varchar(15) DEFAULT NULL,
PRIMARY KEY (id)
);

Step#2: Copy the SpringMVC, Hibernate and their dependent jars into WEB-INF/lib folder.
If you are using Maven you can mention the following dependencies.



junit
junit
4.8.1
jar
compile


org.springframework
spring-web
3.0.5.RELEASE
jar
compile


org.springframework
spring-core
3.0.5.RELEASE
jar
compile


commons-logging
commons-logging




log4j
log4j
1.2.14
jar
compile


org.springframework
spring-tx
3.0.5.RELEASE
jar
compile


jstl
jstl
1.1.2
jar
compile


taglibs
standard
1.1.2
jar
compile


org.springframework
spring-webmvc
3.0.5.RELEASE
jar
compile


org.springframework
spring-aop
3.0.5.RELEASE
jar
compile


commons-digester
commons-digester
2.1
jar
compile


commons-collections
commons-collections
3.2.1
jar
compile


org.hibernate
hibernate-core
3.3.2.GA
jar
compile


javax.persistence
persistence-api
1.0
jar
compile


c3p0
c3p0
0.9.1.2
jar
compile


org.springframework
spring-orm
3.0.5.RELEASE
jar
compile


org.slf4j
slf4j-api
1.6.1
jar
compile


org.slf4j
slf4j-log4j12
1.6.1
jar
compile


cglib
cglib-nodep
2.2
jar
compile


org.hibernate
hibernate-annotations
3.4.0.GA
jar
compile


jboss
javassist
3.7.ga
jar
compile


mysql
mysql-connector-java
5.1.14
jar
compile


Step#3: Configure SpringMVC

a) Configure DispatcherServlet in web.xml


dispatcher
org.springframework.web.servlet.DispatcherServlet
1



dispatcher
*.do



org.springframework.web.context.ContextLoaderListener


contextConfigLocationclasspath:applicationContext.xml

b) Configure View Resolver in WEB-INF/dispatcher-servlet.xml



c) Configure Annotation support, PropertyPlaceHolderConfigurer, ResourceBundleMessageSource in WEB-INF/classes/applicationContext.xml











Step#4: Configure JDBC connection parameters and Hibernate properties in config.properties

################### JDBC Configuration ##########################
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sivalabs
jdbc.username=root
jdbc.password=admin

################### Hibernate Configuration ##########################
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
#hibernate.hbm2ddl.auto=update
hibernate.generate_statistics=true

Step#5: Configure DataSource, SessionFactory, TransactionManagement support in WEB-INF/classes/applicationContext.xml








${hibernate.dialect}
${hibernate.show_sql}










Step#6: Configure the Labels, error messages in WEB-INF/classes/Messages.properties

App.Title=SivaLabs
typeMismatch.java.util.Date={0} is Invalid Date.
dob=DOB

Step#7: Create the Entity class Contact.java

package com.sivalabs.contacts;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.commons.lang.builder.ToStringBuilder;

@Entity
@Table(name="CONTACTS")
public class Contact
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Column private String name;
@Column private String address;
@Column private String gender;
@Column private Date dob;
@Column private String email;
@Column private String mobile;
@Column private String phone;

@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this);
}
//setters & getters
}

Step#8: Create the ContactsDAO.java which performs CRUD operations on CONTACTS table.

package com.sivalabs.contacts;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional
public class ContactsDAO
{
@Autowired
private SessionFactory sessionFactory;

public Contact getById(int id)
{
return (Contact) sessionFactory.getCurrentSession().get(Contact.class, id);
}

@SuppressWarnings("unchecked")
public List<Contact> searchContacts(String name)
{
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Contact.class);
criteria.add(Restrictions.ilike("name", name+"%"));
return criteria.list();
}

@SuppressWarnings("unchecked")
public List<Contact> getAllContacts()
{
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Contact.class);
return criteria.list();
}

public int save(Contact contact)
{
return (Integer) sessionFactory.getCurrentSession().save(contact);
}

public void update(Contact contact)
{
sessionFactory.getCurrentSession().merge(contact);
}

public void delete(int id)
{
Contact c = getById(id);
sessionFactory.getCurrentSession().delete(c);
}
}

Step#9: Create ContactFormValidator.java which performs the validations on saving/updating a contact.

package com.sivalabs.contacts;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

@Component("contactFormValidator")
public class ContactFormValidator implements Validator
{
@SuppressWarnings("unchecked")
@Override
public boolean supports(Class clazz)
{
return Contact.class.isAssignableFrom(clazz);
}

@Override
public void validate(Object model, Errors errors)
{
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name","required.name", "Name is required.");
}
}

Step#10: Create ContactsControllers.java which processes all the CRUD requests.

package com.sivalabs.contacts;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class ContactsControllers
{
@Autowired
private ContactsDAO contactsDAO;

@Autowired
private ContactFormValidator validator;

@InitBinder
public void initBinder(WebDataBinder binder)
{
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}

@RequestMapping("/searchContacts")
public ModelAndView searchContacts(@RequestParam(required= false, defaultValue="") String name)
{
ModelAndView mav = new ModelAndView("showContacts");
List<Contact> contacts = contactsDAO.searchContacts(name.trim());
mav.addObject("SEARCH_CONTACTS_RESULTS_KEY", contacts);
return mav;
}

@RequestMapping("/viewAllContacts")
public ModelAndView getAllContacts()
{
ModelAndView mav = new ModelAndView("showContacts");
List<Contact> contacts = contactsDAO.getAllContacts();
mav.addObject("SEARCH_CONTACTS_RESULTS_KEY", contacts);
return mav;
}

@RequestMapping(value="/saveContact", method=RequestMethod.GET)
public ModelAndView newuserForm()
{
ModelAndView mav = new ModelAndView("newContact");
Contact contact = new Contact();
mav.getModelMap().put("newContact", contact);
return mav;
}

@RequestMapping(value="/saveContact", method=RequestMethod.POST)
public String create(@ModelAttribute("newContact")Contact contact, BindingResult result, SessionStatus status)
{
validator.validate(contact, result);
if (result.hasErrors())
{
return "newContact";
}
contactsDAO.save(contact);
status.setComplete();
return "redirect:viewAllContacts.do";
}

@RequestMapping(value="/updateContact", method=RequestMethod.GET)
public ModelAndView edit(@RequestParam("id")Integer id)
{
ModelAndView mav = new ModelAndView("editContact");
Contact contact = contactsDAO.getById(id);
mav.addObject("editContact", contact);
return mav;
}

@RequestMapping(value="/updateContact", method=RequestMethod.POST)
public String update(@ModelAttribute("editContact") Contact contact, BindingResult result, SessionStatus status)
{
validator.validate(contact, result);
if (result.hasErrors()) {
return "editContact";
}
contactsDAO.update(contact);
status.setComplete();
return "redirect:viewAllContacts.do";
}

@RequestMapping("deleteContact")
public ModelAndView delete(@RequestParam("id")Integer id)
{
ModelAndView mav = new ModelAndView("redirect:viewAllContacts.do");
contactsDAO.delete(id);
return mav;
}
}

Step#11: Instead of writing the JSTL tag library declerations in all the JSPs, declare them in one JSP and include that JSP in other JSPs.
taglib_includes.jsp

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>

Step#12: Create the JSPs.

a)showContacts.jsp

<%@include file="taglib_includes.jsp" %>

<html>
<head>

<title> </title>

</head>
<body style="font-family: Arial; font-size:smaller;">



Enter Contact Name

  

  






Id Name Address Mobile
No Results found


 Edit

  Delete


</body>
</html>

b)newContact.jsp

<%@include file="taglib_includes.jsp" %>

<html>
<head>

<title> </title>
</head>
<body style="font-family: Arial; font-size:smaller;">




Edit Contact Form











Name



DOB
Gender

Address

Email
Mobile





 



 

</body>
</html>

a)editContact.jsp

<%@include file="taglib_includes.jsp" %>

<html>
<head>

<title> </title>
</head>
<body style="font-family: Arial; font-size:smaller;">




Edit Contact Form












Id



Name



DOB
Gender

Address

Email
Mobile





 



 

</body>
</html>

Step#13: Write the javascript file js/contacts.js containing the utility methods

function go(url)
{
window.location = url;
}

function deleteContact(url)
{
var isOK = confirm("Are you sure to delete?");
if(isOK)
{
go(url);
}
}

Step#14: The welcome file index.jsp

<%
response.sendRedirect("viewAllContacts.do");
%>

Step#15: Start the server and point your browser URL to http://localhost:8080/SpringMVCHibernate

You can download the source code at
SpringMVCHibernate.zip