package com.shoesobjects;
import java.io.Serializable;
public class MyRecord implements Serializable {
private Long id = null;
private String firstName = null;
private String lastName = null;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
} public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Here is the data access object using just hibernate. Not to bad but lots of cookie cutter try/catch blocks and redundant opening/closing of sessions. Notice it is 114 lines of code and would be even larger if I handled exceptions properly like any good programmer would.
package com.shoesobjects.dao;
import com.shoesobjects.MyRecord;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.Configuration;
import java.util.List;
public class MyRecordDAOHibernate implements MyRecordDAO {
private static SessionFactory sessionFactory = null;
public MyRecordDAOHibernate() {
Configuration cfg = null;
try {
cfg = new Configuration().addClass(MyRecord.class);
} catch (MappingException e) {
e.printStackTrace();
}
try {
sessionFactory = cfg.buildSessionFactory();
} catch (HibernateException e) {
e.printStackTrace();
}
}
private Session getSession() {
Session session = null;
try {
session = sessionFactory.openSession();
} catch (HibernateException e) {
e.printStackTrace();
}
return session;
}
public MyRecord getRecord(Long id) {
Session session = this.getSession();
MyRecord record = null;
try {
record = (MyRecord) session.load(MyRecord.class, id);
} catch (HibernateException e) {
e.printStackTrace();
} finally {
if (session != null) {
try {
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}
return record;
}
public List getRecords() {
Session session = this.getSession();
List list = null;
try {
Query query = session.
createQuery("select myrecord from com.shoesobjects.MyRecord");
list = query.list();
} catch (HibernateException e) {
e.printStackTrace();
} finally {
if (session != null) {
try {
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}
return list;
}
public void saveRecord(MyRecord record) {
Session session = this.getSession();
try {
session.saveOrUpdate(record);
session.flush();
} catch (HibernateException e) {
e.printStackTrace();
} finally {
if (session != null) {
try {
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}
}
public void removeRecord(Long id) {
Session session = this.getSession();
try {
MyRecord record = (MyRecord) session.load(MyRecord.class, id);
session.delete(record);
session.flush();
} catch (HibernateException e) {
e.printStackTrace();
} finally {
if (session != null) {
try {
session.close();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}
}
}
Hibernate uses a properties file or xml file for it's configuration information(datasource url, username, password, database dialect, etc). For simplicity, I'm listing only the necessary information.
#hibernate.properties #only used for MyRecordDAOHibernateTest
hibernate.connection.url = jdbc:hsqldb:mem:test
hibernate.connection.username = sa
hibernate.connection.password =
hibernate.dialect = net.sf.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class = org.hsqldb.jdbcDriver
hibernate.hbm2ddl.auto=create
The unit test to exercise this DAO class is listed below. It is a pretty straight forward JUnit test. The only thing that might look out of the ordinary is that we are instantiating the MyRecordDAOHibernate() class directly. In practice, it would be beneficial to hide this behind a Factory. Then you could more easily allow for alternate implementations.
package com.shoesobjects;
import com.shoesobjects.dao.MyRecordDAO;
import com.shoesobjects.dao.MyRecordDAOHibernate;
import junit.framework.Assert;
import junit.framework.TestCase;
public class MyRecordDAOHibernateTest extends TestCase {
private MyRecord record = null;
private MyRecordDAO dao = null;
protected void setUp() throws Exception {
super.setUp();
dao = new MyRecordDAOHibernate();
}
protected void tearDown() throws Exception {
super.tearDown();
dao = null;
}
public void testSaveRecord() throws Exception {
record = new MyRecord();
record.setFirstName("Gavin");
record.setLastName("King");
dao.saveRecord(record);
Assert.assertNotNull("primary key assigned", record.getId());
}
}
The version using Hibernate and Spring has a much cleaner implementation thanks to Springs. Notice this version doesn't have to deal with creating the SessionFactory or dealing with opening/closing sessions. The Spring Framework takes care of this under the covers for you. All you have to do is add the SessionFactory as a needed dependency in Spring's applicationContext.xml file. Notice this class is only 26 lines of code. Look at how clear and concise this version is.
package com.shoesobjects.dao;
import com.shoesobjects.MyRecord;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;
import java.util.List;
public class MyRecordDAOHibernateWithSpring extends HibernateDaoSupport
implements MyRecordDAO {
public MyRecord getRecord(Long id) {
return (MyRecord) getHibernateTemplate().get(MyRecord.class, id);
}
public List getRecords() {
return getHibernateTemplate().find("from MyRecord");
} public void saveRecord(MyRecord record) {
getHibernateTemplate().saveOrUpdate(record);
}
public void removeRecord(Long id) {
Object record = getHibernateTemplate().load(MyRecord.class, id);
getHibernateTemplate().delete(record);
}
}
The applicationContext.xml file contains all the information for Spring's Bean Factory to instantiate the necessary class es and wire the dependencies. Notice the bean with an id of myRecordDAO, it has a reference to the bean sessionFactory which is also defined in this config file.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans> <bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value></property>
<property name="url">
<value>jdbc:hsqldb:mem:test</value>
</property>
<property name="username"><value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/>
</property>
<property name="mappingResources">
<list>
<value>MyRecord.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
net.sf.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><
ref local="sessionFactory"/>
</property>
</bean>
<bean id="myRecordDAO"
class="com.shoesobjects.dao.MyRecordDAOHibernateWithSpring">
<property name="sessionFactory"><ref local="sessionFactory"/>
</property>
</bean>
</beans>
Here is the JUnit test for Hibernate+Spring implementation of the data access object. Notice it is just as straight forward as the last unit test but has one noticeable advantage. Look at line 22 where we instantiate the data access object. No concrete implementation is listed, only the interface. The concrete class is instantiated by Spring's BeanFactory behind the scenes allowing us to swap out the implementation by editing the applicationContext.xml descriptor. This flexibility allows our unit test to easily be modified to use a mock implementation without using command line parameters to the JVC or making code changes.
package com.shoesobjects;
import com.shoesobjects.dao.MyRecordDAO;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyRecordDAOHibernateWithSpringTest extends TestCase {
private ApplicationContext ctx = null;
private MyRecord record = null;
private MyRecordDAO dao = null;
public MyRecordDAOHibernateWithSpringTest() {
// Should put in a parent class that extends TestCase
String[] paths = {"applicationContext.xml"};
ctx = new ClassPathXmlApplicationContext(paths);
} protected void setUp() throws Exception {
super.setUp();
dao = (MyRecordDAO) ctx.getBean("myRecordDAO");
} protected void tearDown() throws Exception {
super.tearDown();
dao = null;
}
public void testSaveRecord() throws Exception {
record = new MyRecord();
record.setFirstName("Rod");
record.setLastName("Johnson");
dao.saveRecord(record);
Assert.assertNotNull("primary key assigned", record.getId());
}
}
In order to run this example, the following steps are required.
1) Install MySQL, login as root
2) Create a database called myrecord
create database myrecord;
3) Create a database user called myrecord with a password of myrecord.
grant all privileges on myrecord.* to myrecord@localhost identified by 'myrecord' with grant option;
4) Run the unit test's. The database table will be created upon running the test due to the following line in applicationContext.xml
<prop key="hibernate.hbm2ddl.auto">update</prop>
3) Run the unit tests
No comments:
Post a Comment