Chapter 13. Hibernate with Spring What Is Spring? Writing a Data Access Object (DAO) Creating an Application Context Putting It All Together 1 / 24
What is Spring? The Spring Framework is an Inversion of Control (IoC) container with a number of additional integrated features and modules. To some it is just an IoC container; to others it is an entire platform for application development. Spring Framework Created by Rod Johnson in 2000. Spring rose to prominence as an alternative to Enterprise Java Beans (EJBs). Rod's seminal text, Expert One-on-One J2EE Design and Development, published by WROX in 2002, introduced a large number of developers to the idea of replacing EJBs with a simple IoC container and a number of APIs that served to isolate your programs from the rougher edges in Sun's APIs. 2 / 24
What is Inversion of Control (IoC)? There's no single definition of IoC, but a central concept is Dependency Injection. To some it is just an IoC container; to others it is an entire platform for application development. From Wikipedia:... a pattern in which responsibility for object creation and object linking is removed from the objects and transferred to a factory. Spring, the lightweight container, assumes responsibility for wiring together a set of components, and injects dependencies either via JavaBean properties or constructor arguments. If you are interested in a longer description of both Dependency Injection and Inversion of Control, you should read Martin Fowler's Inversion of Control Containers and the Dependency Injection Pattern. 3 / 24
Combining Spring and Hibernate Adding the Spring framework as a project dependency build.gradle : dependencies { compile( 'org.springframework:spring-core:4.+' ) { exclude group: 'commons-logging', module: 'commons-logging' compile 'org.springframework:spring-context:4.+' compile 'org.springframework:spring-orm:4.+' runtime 'org.slf4j:jcl-over-slf4j:1.+' 4 / 24
Writing a Data Access Object What is a Data Access Object (DAO)? The DAO pattern: Consolidates all CRUD operations (create, read, update, delete) into a single interface usually organized by table or object type. public interface AlbumDAO { void create(album album); Album read(integer id); int update(album album); int delete(album album); Provides one or more swappable implementations of this interface which can use any number of different persistence APIs and underlying storage media. public class AlbumHibernateDAO implements AlbumDAO { public void create(album album) { ; public Album read(integer id) { ; public int update(album album) { ; public int delete(album album) { ; 5 / 24
Book Example DAOs Three Entities Artist, Album, Track Three DAOs ArtistDAO, AlbumDAO, TrackDAO public interface ArtistDAO { Artist persist(artist artist); void delete(artist artist); Artist uniquebyname(string name); Artist getartist(string name, boolean create); 6 / 24
ArtistDAO interface package com.oreilly.hh.dao; import com.oreilly.hh.data.artist; public interface ArtistDAO { public Artist persist(artist artist); public void delete(artist artist); public Artist uniquebyname(string name); public Artist getartist(string name, boolean create); 7 / 24
ArtistHibernateDAO (Old Style: HibernateTemplate) public class ArtistHibernateDAO extends HibernateDaoSupport implements ArtistDAO { public Artist persist(artist artist) { return (Artist) gethibernatetemplate().merge(artist); public void delete(artist artist) { gethibernatetemplate().delete(artist); public Artist uniquebyname(final String name) { return (Artist) gethibernatetemplate().execute(new HibernateCallback() { public Object doinhibernate(session session) { Query query = getsession().getnamedquery( "com.oreilly.hh.artistbyname"); query.setstring("name", name); return (Artist) query.uniqueresult(); ); public Artist getartist(string name, boolean create) { Artist found = uniquebyname(name); if (found == null && create) { found = new Artist(name, new HashSet<Track>(), null); found = persist(found); if (found!= null && found.getactualartist()!= null) { return found.getactualartist(); return found; ch13-0-spring-annotation ch13-1-dao-service-annotation 8 / 24
ArtistHibernateDAO (New Style 1: xml injection) public class ArtistHibernateDAO implements ArtistDAO { private SessionFactory sessionfactory; public void setsessionfactory(sessionfactory sessionfactory) { this.sessionfactory = sessionfactory; public Artist persist(artist artist) { sessionfactory.getcurrentsession().saveorupdate(artist); return artist; public void delete(artist artist) { sessionfactory.getcurrentsession().delete(artist); public Artist uniquebyname(final String name) { Query query = sessionfactory.getcurrentsession().getnamedquery("com.oreilly.hh.artistbyname"); query.setstring("name", name); return (Artist) query.uniqueresult(); public Artist getartist(string name, boolean create) { ch13-2-hibernate-api-dao-annotation 9 / 24
ArtistHibernateDAO (New Style 2: annotation injection) @Repository public class ArtistHibernateDAO implements ArtistDAO { @Autowired private SessionFactory sessionfactory; public void setsessionfactory(sessionfactory sessionfactory) { this.sessionfactory = sessionfactory; public Artist persist(artist artist) { sessionfactory.getcurrentsession().saveorupdate(artist); return artist; public void delete(artist artist) { sessionfactory.getcurrentsession().delete(artist); public Artist uniquebyname(final String name) { Query query = sessionfactory.getcurrentsession().getnamedquery("com.oreilly.hh.artistbyname"); query.setstring("name", name); return (Artist) query.uniqueresult(); public Artist getartist(string name, boolean create) { ch13-3-autowired-annotation 10 / 24
Why would I want to use a DAO? Increased Flexibility Since you are coding to an interface, you can easily swap in a new DAO implementation if you need to use a different O/R mapping service or storage medium. When you put a DAO interface between your application's logic and the persistence layer, you've made it easier to swap in other implementations for a particular DAO class or method. ibatis, TopLink, Hibernate, Spring Template Isolation This flexibility and isolation goes both ways it is easier to replace both the implementation of a specific DAO class, and it is easier to reuse your persistence layer when you need to rewrite or upgrade your application logic. 11 / 24
Creating an Applicatin Context of Spring We discussed how Spring would assume responsibility for creating and connecting the components in our application. For Spring to do this, we need to tell it about the various components (which Spring calls beans) in our system and how they are connected to each other. We do this using an XML document that describes the class of each bean, assigns it an ID, and establishes its relationships to other beans. This XML document is then used by Spring to create an ApplicationContext object from which we can retrieve our components by name. 12 / 24
Our Spring Application Context Figure 13-2. Our Spring application context Three test components are connected to three DAO objects. The DAO objects all have a reference to the sessionfactory object. 13 / 24
Example 13-8. Spring applicationcontext.xml <?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" > <bean id="sessionfactory" class="org.springframework.orm.hibernate3.annotation.annotationsessionfactorybean"> <property name="annotatedclasses"> <list> <value>com.oreilly.hh.data.album</value> <value>com.oreilly.hh.data.albumtrack</value> <value>com.oreilly.hh.data.artist</value> <value>com.oreilly.hh.data.stereovolume</value> <value>com.oreilly.hh.data.track</value> </list> </property> <property name="hibernateproperties"> <props> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.jdbctransactionfactory</prop> <prop key="hibernate.connection.pool_size">0</prop> <prop key="hibernate.dialect">org.hibernate.dialect.hsqldialect </prop> <prop key="hibernate.connection.driver_class">org.hsqldb.jdbcdriver</prop > <prop key="hibernate.connection.url">jdbc:hsqldb:data/music;shutdown=true</prop> <prop key="hibernate.connection.username">sa</prop> <prop key="hibernate.connection.password"></prop> </props> </property> </bean> 14 / 24
Example 13-8. Spring applicationcontext.xml <!-- enable the configuration of transactional behavior based on annotations -- > <tx:annotation-driven transaction-manager="transactionmanager"/> <bean id="transactionmanager" class="org.springframework.orm.hibernate3.hibernatetransactionmanager"> <property name="sessionfactory"> <ref local="sessionfactory"/> </property> </bean> <!-- A default RequiredAnnotationBeanPostProcessor will be registered by the "context:annotation-config" and "context:component-scan" XML tags. --> <bean class="org.springframework.beans.factory.annotation.requiredannotationbean PostProcessor"/> <context:annotation-config /> 15 / 24
Example 13-8. Spring applicationcontext.xml <!-- Define our Data Access beans --> <bean id="albumdao" class="com.oreilly.hh.dao.hibernate.albumhibernatedao"> <property name="sessionfactory" ref="sessionfactory"/> </bean> <bean id="artistdao" class="com.oreilly.hh.dao.hibernate.artisthibernatedao"> <property name="sessionfactory" ref="sessionfactory"/> </bean> <bean id="trackdao" class="com.oreilly.hh.dao.hibernate.trackhibernatedao"> <property name="sessionfactory" ref="sessionfactory"/> </bean> 16 / 24
Example 13-8. Spring applicationcontext.xml <!-- Define our Test beans --> <bean id="createtest" class="com.oreilly.hh.createtest"> <property name="trackdao" ref="trackdao"/> <property name="artistdao" ref="artistdao"/> </bean> <bean id="querytest" class="com.oreilly.hh.querytest"> <property name="trackdao" ref="trackdao"/> </bean> <bean id="albumtest" class="com.oreilly.hh.albumtest"> <property name="albumdao" ref="albumdao"/> <property name="artistdao" ref="artistdao"/> <property name="trackdao" ref="trackdao"/> </bean> </beans> 17 / 24
Service Layer : the Test interface Example 13-9. The Test interface package com.oreilly.hh; import org.springframework.transaction.annotation.transactional; /** * A common interface for our example classes. We'll need this * because TestHarness needs to cast CreateTest, QueryTest, or * AlbumTest to a common interface after it retrieves the bean * from the Spring application context. */ public interface Test { /** * Runs a simple example */ @Transactional(readOnly=false) public void run(); The Transactional annotation takes care of binding a Session to the current Thread, starting a transaction, and either committing the transaction if the method returns normally, or rolling it back if there is an exception. 18 / 24
How do I activate the transactional annotation? applicationcontext.xml <tx:annotation-driven transaction-manager="transactionmanager"/> <bean id="transactionmanager" class="org.springframework.orm.hibernate3.hibernatetransactionmanager"> <property name="sessionfactory"> <ref local="sessionfactory"/> </property> </bean> The tx:annotation-driven element simply activates the Transactional annotation and points it to a PlatformTransactionManager. HibernateTransactionManager is an implementation of the Spring Framework's PlatformTransactionManager. It takes care of binding a Hibernate Session from the sessionfactory to the current Thread using SessionFactoryUtils. The Transactional annotation ensures that the same Session will remain open and bound to the current Thread during the execution of the annotated method. In this application, we're relying on the Transactional annotation to make sure that all of the code in any run() method implementation has access to the same Hibernate Session object. 19 / 24
Adapting CreateTest, QueryTest, and AlbumTest Example 13-10. CreateTest adapted for use in our Spring context public class CreateTest implements Test { private ArtistDAO artistdao; private TrackDAO trackdao; private static void addtrackartist(track track, Artist artist) { track.getartists().add(artist); public void run() { StereoVolume fullvolume = new StereoVolume(); Track track = new Track("Russian Trance", "vol2/album610/track02.mp3", Time.valueOf("00:03:30"), new HashSet<Artist>(), new Date(), fullvolume, SourceMedia.CD, new HashSet<String>()); addtrackartist(track, artistdao.getartist("ppk", true)); trackdao.persist(track); public ArtistDAO getartistdao() { return artistdao; public void setartistdao(artistdao artistdao) { this.artistdao = artistdao; public TrackDAO gettrackdao() { return trackdao; public void settrackdao(trackdao trackdao) { this.trackdao = trackdao; 20 / 24
Adapting CreateTest, QueryTest, and AlbumTest Example 13-12. QueryTest adapted for use with Spring public class QueryTest implements Test { private static Logger log = Logger.getLogger(QueryTest.class); private TrackDAO trackdao; public void run() { // Print the tracks that will fit in five minutes List<Track> tracks = trackdao.tracksnolongerthan( Time.valueOf("00:05:00") ); for (Track track : tracks) { // For each track returned, print out the title and the playtime log.info("track: \"" + track.gettitle() + "\", " + track.getplaytime()); public TrackDAO gettrackdao() { return trackdao; public void settrackdao(trackdao trackdao) { this.trackdao = trackdao; 21 / 24
Adapting CreateTest, QueryTest, and AlbumTest Example 13-13. Reimplementing AlbumTest public class AlbumTest implements Test { private static Logger log = Logger.getLogger(AlbumTest.class); private AlbumDAO albumdao; private ArtistDAO artistdao; private TrackDAO trackdao; public void run() { Artist artist = artistdao.getartist("martin L. Gore", true); Album album = new Album("Counterfeit e.p.", 1, new HashSet<Artist>(), new HashSet<String>(), new ArrayList<AlbumTrack>(5), new Date()); album.getartists().add(artist); album = albumdao.persist(album); addalbumtrack(album, "Compulsion", "vol1/album83/track01.mp3", Time.valueOf("00:05:29"), artist, 1, 1); addalbumtrack(album, "In a Manner of Speaking", "vol1/album83/track02.mp3", Time.valueOf("00:04:21"), artist, 1, 2); album = albumdao.persist( album ); log.info(album); 22 / 24
Running CreateTest, QueryTest, and AlbumTest 13-15. Executing TestRunner from Gradle task ctest(dependson: classes, type: JavaExec) { main = 'com.oreilly.hh.testrunner' args 'createtest' classpath = sourcesets.main.runtimeclasspath task qtest(dependson: classes, type: JavaExec) { main = 'com.oreilly.hh.testrunner' args 'querytest' classpath = sourcesets.main.runtimeclasspath task atest(dependson: classes, type: JavaExec) { main = 'com.oreilly.hh.testrunner' args 'albumtest' classpath = sourcesets.main.runtimeclasspath 23 / 24
Materials for Further Study Hibernate Home http://www.hibernate.org/ Hibernate Manual Hibernate Getting Started Guide 3.6 http://docs.jboss.org/hibernate/core/3.6/quickstart/en-us/html/ Hibernate Reference Documentation 3.6 http://docs.jboss.org/hibernate/core/3.6/reference/en-us/html/ http://docs.jboss.org/hibernate/core/3.6/reference/en-us/pdf/hibernate_reference.pdf Hibernate Reference Documentation 4.3 and 5.0 Hibernate Tutorial http://www.mkyong.com/tutorials/hibernate-tutorials/ 24 / 24