Annotation Driven JSF-Spring-JPA
March 4, 2008 19 Comments
JSF-Spring-JPA is the popular stack of choice these days, mostly to be used in my consulting and training purposes I’ve created a base project called MovieStore demonstrating the annotation-driven integration of JSF-Spring-JPA. JSF backing beans, spring service level beans and DAO’s are configured and integrated with annotations. Only the core infrastructure like datasource, entityManagerFactory or transactionManager are configured with xml.
Following are the package contents;
– JSF (MyFaces 1.2.2 with Facelets)
– Spring 2.5
– JPA with Hibernate
– HSQLDB for persistence
– Jetty for deployment
– Maven2 for build
The example application is simple; a MovieStore(Yes, I’m a movie maniac so all my examples are about movies)
There are two formats DVD and BLU-RAY, not including HD-DVD because it’s dead.
Movie
package com.prime.tutorial.moviestore.domain; //imports not displayed @Entity public class Movie implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Basic private String title; @Basic private Integer discs; @Enumerated private Format format; public Movie() {} public Movie(String title, Integer discs) { this.title = title; this.discs = discs; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Integer getDiscs() { return discs; } public void setDiscs(Integer discs) { this.discs = discs; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Format getFormat() { return format; } public void setFormat(Format format) { this.format = format; } }
For DAO part, I’ve included a GenericDAO too for the crud operations;
package com.prime.tutorial.moviestore.dao; //imports are not displayed public interface GenericDAO<t, ID extends Serializable> { T loadById(ID id); void persist(T entity); void update(T entity); void delete(T entity); List<t> loadAll(); }
Following is the GenericDAO implementation with JPA, note how the entityManager is injected. Instead of extending JPATemplate, with @PersistenceContext, the entityManager is resolved by spring. This way you don’t need to subclass JPATemplate.
package com.prime.tutorial.moviestore.dao; //imports are not displayed public abstract class GenericDAOWithJPA<t, ID extends Serializable> implements GenericDAO<t,ID> { private Class<t> persistentClass; @PersistenceContext protected EntityManager entityManager; @SuppressWarnings("unchecked") public GenericDAOWithJPA() { this.persistentClass = (Class<t>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; } public Class<t> getPersistentClass() { return persistentClass; } public T loadById(ID id) { return entityManager.find(persistentClass, id); } public void persist(T entity) { entityManager.persist(entity); } public void update(T entity) { entityManager.merge(entity); } public void delete(T entity) { entityManager.remove(entity); } @SuppressWarnings("unchecked") public List<t> loadAll() { return entityManager.createQuery("Select t from " + persistentClass.getSimpleName() + " t").getResultList(); } }
That’s all about the GenericDAO, for movie specific database operations here comes the MovieDAO.
MovieDAO
package com.prime.tutorial.moviestore.dao; //imports not displayed public interface MovieDAO extends GenericDAO{ public List findByTitle(String title); }
MovieDAOWithJPA is annotated with @Repository, by this way spring can manage it and can also allows ExceptionTranslation(PersistenceAnnotationBeanPostProcessor also needs to be configured in applicationContext.xml).
package com.prime.tutorial.moviestore.dao; //imports not displayed @Repository public class MovieDAOWithJPA extends GenericDAOWithJPA implements MovieDAO{ @SuppressWarnings("unchecked") public List findByTitle(String title) { Query query = entityManager.createQuery("Select m from Movie m where m.title = ?1"); query.setParameter(1, title); return query.getResultList(); } }
At service level there are two classes, MovieService and MovieServiceImpl.
MovieService – Notice the @Transactional annotation, this way transactions are configured with annotations.(<tx:annotation-driven /> also needs to be declared to enable this, see applicationContext.xml)
package com.prime.tutorial.moviestore.service; //imports not displayed public interface MovieService { public void createNew(Movie movie); public List findAll(); public List findByTitle(String title); }
MovieServiceImpl – @Service annotation makes this a spring bean, and movieDAO is injected by type using @AutoWired
package com.prime.tutorial.moviestore.service; //imports not displayed @Service public class MovieServiceImpl implements MovieService{ private MovieDAO movieDAO; @Autowired public MovieServiceImpl(MovieDAO movieDAO) { this.movieDAO = movieDAO; } @Transactional public void createNew(Movie movie) { movieDAO.persist(movie); } public List findAll() { return movieDAO.loadAll(); } public List findByTitle(String title) { return movieDAO.findByTitle(title); } }
Finally the JSF Bean and Facelets page to use the stuff above, the page is used for creating new movies.
createMovie.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" template="template.xhtml"> <ui:define name="content"> <h:messages showDetail="true"/> <h:form> <h:panelGrid columns="2"> <h:outputLabel for="title" value="Title"></h:outputLabel> <h:inputText id="title" value="#{createMovie.movie.title}"></h:inputText> <h:outputLabel for="discs" value="Number of Discs"></h:outputLabel> <h:inputText id="discs" value="#{createMovie.movie.discs}"></h:inputText> <h:outputLabel for="format" value="Format"></h:outputLabel> <h:selectOneMenu id="format" value="#{createMovie.movie.format}"> <f:selectItem itemLabel="DVD" itemValue="DVD" /> <f:selectItem itemLabel="BLU-RAY" itemValue="BLURAY" /> </h:selectOneMenu> </h:panelGrid> <h:commandButton value="Save" action="#{createMovie.save}"></h:commandButton> </h:form> </ui:define> </ui:composition>
CreateMovie is the backing bean to handle this page, also managed by spring. The MovieService is set through construction injection, one thing you cannot do in plain jsf ioc. @Component marks this bean as a spring bean with the “createMovie” name. In jsf views, this is the name to be resolved in el expressions like #{createMovie.movie.title}. @Scope is set to request, as you may already know with spring it’s also possible to provide custom scopes.
package com.prime.tutorial.moviestore.view; //imports not displayed @Component("createMovie") @Scope("request") public class CreateMovie implements Serializable{ private MovieService movieService; private Movie movie = new Movie(); @Autowired public CreateMovie(MovieService movieService) { this.movieService = movieService; } public Movie getMovie() { return movie; } public void setMovie(Movie movie) { this.movie = movie; } public String save() { movieService.createNew(movie); FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_INFO, "Movie is saved successfully", "OK"); FacesContext.getCurrentInstance().addMessage(null, facesMessage); movie = new Movie(); return null; } }
So where’s the xml? Let’s start with the spring config.
applicationContext.xml
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:property-placeholder location="classpath:application.properties"/> <context:component-scan base-package="com.prime.tutorial.moviestore" /> <tx:annotation-driven transaction-manager="txManager"/> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="moviestore"/> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="databasePlatform" value="${database.dialect}"/> <property name="showSql" value="${database.showSql}" /> <property name="generateDdl" value="${database.generateDdl}" /> </bean> </property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close"> <property name="driverClassName" value="${database.driver}"/> <property name="url" value="${database.url}"/> <property name="username" value="${database.username}"/> <property name="password" value="${database.password}"/> </bean> <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> </beans>
Notes on spring config;
– <context:component-scan base-package=”com.prime.tutorial.moviestore” /> scans the moviestore sub packages for annotated classes, with this it can find our jsf beans, services, daos and etc.
– <tx:annotation-driven transaction-manager=”txManager”/> enables annotation driven transaction management
– PersistenceAnnotationBeanPostProcessor is for exception translation, we don’t extend from JPATemplate but we can still have this feature.
persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="moviestore" transaction-type="RESOURCE_LOCAL"> <class>com.prime.tutorial.moviestore.domain.Movie</class> </persistence-unit> </persistence>
faces-config.xml – Pretty empty:)
<?xml version="1.0" encoding="utf-8"?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd" version="1.2"> <application> <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> <variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver> </application> </faces-config>
– DelegatingVariableResolver helps JSF to resolve spring managed beans like CreateMovie.
Finally the web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param> <context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> <filter> <filter-name>JPA Filter</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>JPA Filter</filter-name> <url-pattern>*.jsf</url-pattern> </filter-mapping> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> </web-app>
– org.springframework.web.context.request.RequestContextListener listener enables spring scopes with @Scope annotation
– JPA Filter is same as OpenSessionInViewFilter but for jpa.
Testing
I’ve also included an integration test for MovieDAOWithJPA, it does not subclass AbstractJPATests and configured completely on spring’s annotation support for testing.
package com.prime.tutorial.moviestore.dao; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; import com.prime.tutorial.moviestore.domain.Movie; import static org.junit.Assert.*; @RunWith(SpringJUnit4ClassRunner.class) @Transactional @TransactionConfiguration(transactionManager="txManager") @ContextConfiguration(locations={"/applicationContext.xml"}) public class MovieDAOWithJPATest{ private MovieDAO movieDAO; @Autowired public void setRepository(MovieDAO movieDAO) { this.movieDAO = movieDAO; } @Test public void shouldPersistNewMovie() { Movie movie = new Movie(); movie.setTitle("Scarface"); movie.setDiscs(new Integer(2)); movieDAO.persist(movie); assertNotNull(movie.getId()); } @Test public void shouldFindByTitle() { Movie movie = new Movie(); movie.setTitle("Carlito's Way"); movie.setDiscs(new Integer(1)); movieDAO.persist(movie); assertNotNull(movie.getId()); List results = movieDAO.findByTitle("Carlito's Way"); assertEquals(1, results.size()); assertEquals("Carlito's Way", results.get(0).getTitle()); } @Test public void shouldReadAllMovies() { Movie movie1 = new Movie(); movie1.setTitle("Godfather Part I"); movie1.setDiscs(new Integer(1)); Movie movie2 = new Movie(); movie2.setTitle("Godfather Part II"); movie2.setDiscs(new Integer(1)); movieDAO.persist(movie1); assertNotNull(movie1.getId()); movieDAO.persist(movie2); assertNotNull(movie2.getId()); List results = movieDAO.loadAll(); assertEquals(2, results.size()); } }
I’ve packaged my moviestore example and uploaded to http://people.apache.org/~cagatay/moviestore.rar, build is based on maven2 and you can easily get it running with;
mvn jetty:run
And then go to -> http://localhost:8080/moviestore
The database is hsqldb with inmemory setting.Final words;
I’ve actually created this sample app to be used in my consulting work but if you’ve decided to go with JSF-Spring-JPA, it is definitely a good resource for kickstart.
Good post! We are working on a similar thing for JPA, JSF and Spring 2.5. We are writing a series of articles for IBM devWork on combing the three tehnologies.
Good stuff Catagay. Thanks. You rock!
Nice post. Quick question – how do you define ‘ID’ in GenericDAO:
T loadById(ID id);
Tags are not appearing for me in all your references to xml files in this tutorial. I tried viewing this page on Safari 3.0 & Firefox 2.0 for Mac OS 10.5.
Can you escape these tags so I can see what goes where?
Merhabaler Ça?atay Bey,
Öncelikle elinize sa?l?k güzel bir çal??ma olmu?.
projeyi deniyorum ama ?öye bir hata ald?m neden olabilir. uzun olmas?n diye sadece ilgili dat?r? yaz?yorum
Te?ekkürler
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘entityManagerFactory’ defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: No persistence unit with name ‘moviestore’ found
Because of @Transaction: from the spring reference manual: “you must annotate the implementation class (and/or methods within that class), not the interface”
Always nice to see a post detailing the JSF/Spring/JPA stack (YES, ‘standard’ and ‘non-standard’ technologies can definitely co-exist). We are actually refactoring an existing application (Struts + J2EE 1.4) to an annotation driven JSF/Spring/JPA architecture, and so far the experience has been gratifying (especially from a configuration standpoint – it is a complete myth that Spring is XML heavy).
I highly recommend (if not turned off by JSF) to look into JSF/Spring/JPA and/or Seam as viable options.
Cheers.
Updated entry, add syntax fixes, html escapes and more…
Easy
perfect
thx
http://www.skilline.com
If you get some noise from maven about not being able to find the jta jar see this.
Pingback: Annotation Driven JSF-Spring-JPA « Cagatay Civici’s Weblog
Pingback: JSF vs Wicket, Job Opportunities « Cagatay Civici’s Weblog
Hey Cagatay Civici, this is a little off topic, but I’m not sure where else to post it. I found a bug (NullPointerException) in ChartCreator. Whenever I try to view a generated image in FireFox (right click -> view image), it crashes (NullPointer). Is this a known issue? Is there a fix? Is there a better forum to discuss this? Feel free to email me if you wish. Thanks.
-Brian
selam cagatay bey,
ben verdiginiz bu ornegi netbeans ile glassfish v2 app server uzerinde deniyim dedim. olmadi 🙂
deploy ederken glassfish persistance-unit’in tipinin JTA olmasi istiyor. JTA yapinca bu sefer hibernate “org.hibernate.HibernateException: The chosen transaction strategy requires access to the JTA TransactionManager” gibi bir hata veriyor. acaba bir fikriniz var mi bu konuda?
Excelente post, es uno de los pocos recursos claros acerca del uso de anotaciones en JSF que no usa Jboss Seam…Gracias.
congratulations on tutorial. very well written. 🙂
Thanks for this, Cagatay! This allowed me to add Spring and JPA to my JSF app very quickly. I find I like container-managed transactions more than I thought I would. Special thanks for making all the source code available. This is a fantastic way to get started.
first of all, many thanx for this good article…
I did download the source code and I tried to run the “MovieDAOWithJPATest” but the following exception appear:
### Start ###
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘entityManagerFactory’ defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Cannot apply class transformer without LoadTimeWeaver specified..
### End ###
your help is appreciated..
Mohammad
Hello and thanks for your clean and practical tutorial sample.
But I have a problem in my sample .I followed all your steps but unfortunately I have exception when I use data Table tag either with or with
.my list which is filled in my action has its value but the properties which are in my entity aren’t shown!
Would you please give me the exact Jar File which you have used with this sample.
Thanks for the excellent tutorial. Very useful