Spring Persistence with Hibernate
03 September 2014
After I prototyped a Spring standalone application with a deployment-friendly runnable jar, I want to prototype another Spring standalone application with added complexity, Hibernate persistence I mean. I'm going to use H2 to avoid 3rd party database dependency.
Just like my previous post, I'm going to use maven-archetype-generate
and will add Spring dependencies later on.
$ mvn archetype:generate -DgroupId=com.manalo.prototype -DartifactId=persistence -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom
[INFO]
[INFO] maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.manalo.prototype
[INFO] Parameter: packageName, Value: com.manalo.prototype
[INFO] Parameter: package, Value: com.manalo.prototype
[INFO] Parameter: artifactId, Value: persistence
[INFO] Parameter: basedir, Value: /home/drmanalo/workspace
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /home/drmanalo/workspace/persistence
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 52.401s
[INFO] Finished at: Thu Sep 04 23:17:16 BST 2014
[INFO] Final Memory: 14M/150M
[INFO] ------------------------------------------------------------------------
mvn eclipse:eclipse
$ mvn eclipse:eclipse
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building persistence 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] maven-eclipse-plugin:2.9:eclipse (default-cli) @ persistence
[INFO]
[INFO] maven-eclipse-plugin:2.9:eclipse (default-cli) @ persistence
[INFO]
[INFO] --- maven-eclipse-plugin:2.9:eclipse (default-cli) @ persistence ---
[INFO] Using Eclipse Workspace: /home/drmanalo/workspace
[INFO] Adding default classpath container: org.eclipse.jdt.launching.JRE_CONTAINER
[INFO] Not writing settings - defaults suffice
[INFO] Wrote Eclipse project for "persistence" to /home/drmanalo/workspace/persistence.
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.325s
[INFO] Finished at: Thu Sep 04 23:23:07 BST 2014
[INFO] Final Memory: 9M/150M
[INFO] ------------------------------------------------------------------------
pom.xml
Here's my complete pom.xml
with added dependencies apart from Spring persistence.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.manalo.prototype</groupId>
<artifactId>persistence</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>persistence</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>3.2.0.RELEASE</spring.version>
<hibernate.version>4.1.9.Final</hibernate.version>
</properties>
<dependencies>
<!-- Database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.173</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.0.1</version>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<pluginRepositories>
<pluginRepository>
<id>onejar-maven-plugin.googlecode.com</id>
<url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.dstovall</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<version>1.3.0</version>
<executions>
<execution>
<configuration>
<mainClass>com.manalo.rabbitmq.App</mainClass>
</configuration>
<goals>
<goal>one-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Spring Configurations
This contains database connection, connection pooling and Hibernate configurations. I defined the packages where Spring should look for its beans using context:component-scan elements.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<tx:annotation-driven />
<context:component-scan base-package="com.manalo.dao" />
<context:component-scan base-package="com.manalo.service" />
<!-- Load application properties -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:application.properties</value>
</list>
</property>
</bean>
<!-- DB connection -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.pass}" />
</bean>
<!-- Hibernate configuration -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<prop key="hibernate.generate_statistics">false</prop>
<prop key="hibernate.cache.use_structured_entries">false</prop>
</props>
</property>
<property name="packagesToScan" value="com.manalo.domain" />
</bean>
<!-- Transaction manager -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
application.properties
I put this under src/main/resources
which has to be added in your classpath in order for PropertyPlaceholderConfigurer
to find it.
# Embedded H2 database
db.driver=org.h2.Driver
db.user=testuser
db.pass=password
db.host=localhost
db.port=3336
db.name=test
db.vendor=h2
db.schemaname=PUBLIC
db.url=jdbc:h2:mem:${db.name};MODE=MySQL;USER=${db.user};PASSWORD=${db.pass};DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000;INIT=create schema if not exists test\
# Connection pool
db.pool.acquireincrement=1
db.pool.minpoolsize=1
db.pool.maxpoolsize=20
db.pool.maxidletime=14400
# Hibernate (ORM)
hibernate.hbm2ddl.auto=create
hibernate.show_sql=true
hibernate.dialect=org.hibernate.dialect.MySQLDialect
log4j.properties
As this is part of convention over configuration, you have to name this file as log4j.properties
and should also be within your classpath.
# Root logger
log4j.rootLogger=INFO, file
log4j.logger.com.manalo=DEBUG, stdout
log4j.logger.org.springframework=INFO, stdout
log4j.logger.org.hibernate=INFO, stdout
# Direct application log messages to a file
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=./logs/manalo.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p [%15.15t] %c - %m%n
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
Database Config
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">
<!-- Use a web browser to access http://localhost:11111 to view H2 console
that allows db queries to be run and be viewed. Parameters url, username
and password should be same as JDBC connection. e.g. jdbc:h2:mem:test, testuser,
password -->
<bean id="org.h2.tools.Server-WebServer" class="org.h2.tools.Server"
factory-method="createWebServer" depends-on="dataSource" init-method="start"
lazy-init="false">
<constructor-arg value="-web,-webPort,11111" />
</bean>
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:/initialise.sql"></jdbc:script>
</jdbc:initialize-database>
</beans>
initialise.sql
You will notice this file is being loaded using jdbc:script
element.
CREATE TABLE user (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR (32) NOT NULL,
password VARCHAR (255) NOT NULL,
UNIQUE (USERNAME)
);
Domain Object
package com.manalo.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private int id;
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Data Access Component
package com.manalo.dao;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.manalo.domain.User;
@Repository
public class UserDao {
@Autowired
private SessionFactory sessionFactory;
public Session getSession() {
return sessionFactory.getCurrentSession();
}
public Integer addUser(User user) {
return (Integer) getSession().save(user);
}
public User getUserByUserName(String username) {
String hql = "from User where username = :username";
Query query = getSession().createQuery(hql);
query.setString("username", username);
return (User) query.uniqueResult();
}
@SuppressWarnings("unchecked")
public List<User> getUsers() {
Criteria criteria = getSession().createCriteria(User.class);
return criteria.list();
}
}
Service Layer
package com.manalo.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.manalo.dao.UserDao;
import com.manalo.domain.User;
@Service
@Transactional
public class UserManager {
@Autowired
private UserDao userDao;
public int addUser(User user) {
return userDao.addUser(user);
}
public User getUserByUserName(String username) {
return userDao.getUserByUserName(username);
}
public List<User> getUsers() {
return userDao.getUsers();
}
}
App.java
package com.manalo.prototype;
import java.util.List;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.manalo.domain.User;
import com.manalo.service.UserManager;
public class App {
public static void main(String[] args) {
/**
* Order of xml files to be loaded is important
*/
final String[] CONFIG_FILES = { "database-init.xml", "spring-core.xml" };
AbstractXmlApplicationContext context = new ClassPathXmlApplicationContext(CONFIG_FILES);
UserManager userManager = context.getBean(UserManager.class);
User user = new User();
user.setUsername("drmanalo");
user.setPassword("secret");
userManager.addUser(user);
user = userManager.getUserByUserName("drmanalo");
System.out.println("Id: " + user.getId());
System.out.println("Username: " + user.getUsername());
System.out.println("Password: " + user.getPassword());
List<User> users = userManager.getUsers();
System.out.println("Number of users: " + users.size());
context.close();
}
}
Running the application
Within Eclipse, right-click App.java -> Run As -> Java Application. You should see this within Eclipse's console. There are extra noises since log4j is doing its job.
Hibernate: drop table if exists User
Hibernate: create table User (id integer not null auto_increment, password varchar(255) not null, username varchar(255) not null, primary key (id))
23:06:01,825 INFO SchemaExport:405 - HHH000230: Schema export complete
Hibernate: insert into User (password, username) values (?, ?)
Hibernate: select user0_.id as id0_, user0_.password as password0_, user0_.username as username0_ from User user0_ where user0_.username=?
Id: 1
Username: drmanalo
Password: secret
Hibernate: select this_.id as id0_0_, this_.password as password0_0_, this_.username as username0_0_ from User this_
Number of users: 1