- Spring 3.0.3.RELEASE application
- that is a desktop application (no application server/servlet container required) but might be deployed to a server too
- and that connects to a database (HSQLDB)
- using JPA 2.0
- without using provider specific code (such as Hibernate) in Java files
- and that allows for declarative transaction management (next article).
You can download the source code and you are free to use it in your projects. I'm using Eclipse 3.5.2 for this.
JPA persistence file - persistence.xml
This file should be located in a folder called META-INF in the root of your classpath. It is a standard JPA configuration file where you put data source specific configuration.
Here's the content:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" 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_2_0.xsd"> <persistence-unit name="Dogs"> <class>me.m1key.springtx.entities.Dog</class> <properties> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" /> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" /> <property name="hibernate.connection.url" value="jdbc:hsqldb:hsql://localhost/dogsdb" /> <property name="hibernate.connection.username" value="sa" /> <property name="hibernate.c3p0.min_size" value="5" /> <property name="hibernate.c3p0.max_size" value="20" /> <property name="hibernate.c3p0.timeout" value="300" /> <property name="hibernate.c3p0.max_statements" value="50" /> <property name="hibernate.c3p0.idle_test_period" value="3000" /> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" /> <property name="hibernate.hbm2ddl.auto" value="create" /> </properties> </persistence-unit> </persistence>
Things to note:
- JPA is only an API that requires a concrete implementation - we are using Hibernate here; you can use anything you want though as long as it is JPA 2.0 compliant. There are going to be no references to Hibernate in Java code
- Persistence unit name is Dogs
- hibernate.hbm2ddl.auto is set to create so that tables are only dropped when the application starts (and not where it shuts down) - you can investigate the content after shutdown (see how)
If you are looking for more information on HSQLDB, I wrote an article on HSQLDB and Eclipse where you can find some basic information (and this project has it set up exactly the same way).
Maven pom.xml
Compile the project using Java 6
... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> ...
Add a repository
This is for the latest Hibernate distribution.
... <repositories> <repository> <id>r.j.o-groups-public</id> <url>https://repository.jboss.org/nexus/content/groups/public/</url> </repository> </repositories> ...
Correct dependencies
... <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>3.5.3-Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>3.5.3-Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>3.5.3-Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-commons-annotations</artifactId> <version>3.3.0.ga</version> </dependency> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> <version>1.0.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>3.5.3-Final</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>1.8.0.7</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.0.3.RELEASE</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.0.3.RELEASE</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jpa</artifactId> <version>2.0.8</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>3.0.3.RELEASE</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> <type>jar</type> <scope>compile</scope> </dependency> </dependencies> ...
Spring beans file - applicationContext.xml
This file is located directly in the classpath, I put it in src/main/resources (a Maven convention).
Entity Manager Factory
This is the JPA way of doing things in Spring. You do not declare a data source in your Spring beans file (it is declared in persistence.xml), but an entity manager factory instead. You give it the persistence unit name ("Dogs").
... <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="Dogs" /> </bean> ...
JPA template
We are going to use JPA template from our DAO classes. It makes DAO operations a bit easier (and sometimes it doesn't). Note that Hibernate Template also exists and has methods that are easier to use than those of JPA template, but I promised no Hibernate in the code.
... <bean id="jpaTemplate" class="org.springframework.orm.jpa.JpaTemplate"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> ...
A DAO class
DogsDao is our only DAO class and it needs the JPA Template.
... <bean id="dogsDao" class="me.m1key.springtx.dao.DogsDaoImpl"> <property name="jpaTemplate" ref="jpaTemplate" /> </bean> ...
An alternative approach would be to inject the entity manager factory directly, thus eliminating the need to use JPA templates at all.
A business logic class
Here's a business logic class and it receives the DAO class, setter-injected.
... <bean id="dogsBean" class="me.m1key.springtx.beans.DogsBean" scope="singleton"> <property name="dogsDao" ref="dogsDao" /> </bean> ...
Transaction management
The next article will tell you more about transaction management. For now, just use this (it is required by JPA).
... <tx:annotation-driven transaction-manager="myTransactionManager" /> <bean id="myTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> ...
Java code
The DAO class
package me.m1key.springtx.dao; import java.util.List; import me.m1key.springtx.entities.Dog; import org.springframework.orm.jpa.support.JpaDaoSupport; import org.springframework.transaction.annotation.Transactional; public class DogsDaoImpl extends JpaDaoSupport implements DogsDao { @SuppressWarnings("unchecked") public List<Dog> retrieveAllDogs() { return getJpaTemplate().find("from Dog"); } public Long retrieveNumberOfDogs() { return (Long) getJpaTemplate().getEntityManagerFactory() .createEntityManager() .createQuery("select count(d) from Dog d").getSingleResult(); } @Transactional public void persistDog(Dog dog) { getJpaTemplate().persist(dog); } }It extends JpaDaoSupport which gives us access to the getJpaTemplate() method. You don't have to extend this class - if you don't, just provide a getter and setter for the jpaTemplate property.
The first method retrieves all dogs and, sadly, needs a SuppressWarning annotation (collection cast).
The second method demonstrates how you can get access to the entity manager, if you need it. That kind of query (that returns a number) is called reporting query.
The third method persists an object. The @Transactional annotation is actually a requirement - without it no insertion occurs.
The DAO class
package me.m1key.springtx.dao; import java.util.List; import me.m1key.springtx.entities.Dog; public interface DogsDao { List<Dog> retrieveAllDogs(); Long retrieveNumberOfDogs(); void persistDog(Dog dog); }
The business logic class
A very basic sample that should give you an idea.
package me.m1key.springtx.beans; import me.m1key.springtx.dao.DogsDao; import me.m1key.springtx.entities.Dog; public class DogsBean { private DogsDao dogsDao; /** * Returns true if there is at least one dog in the database. * * @return true if there is a dog in the database */ public boolean containsDogs() { return dogsDao.retrieveNumberOfDogs() > 0; } /** * Persists the dog to the database. * * @param dog * dog to persist */ public void persistDog(Dog dog) { dogsDao.persistDog(dog); } protected DogsDao getDogsDao() { return dogsDao; } public void setDogsDao(DogsDao dogsDao) { this.dogsDao = dogsDao; } }
The entity class
This is out Dog entity. No Hibernate, as promised.
package me.m1key.springtx.entities; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "DOGS") public class Dog { private Long id; private String name; private static final long serialVersionUID = 1L; private List<String> nicknames = new ArrayList<String>(); public Dog() { super(); } @Id @GeneratedValue @Column(name = "DOG_ID") public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } @Column(name = "DOG_NAME") public String getName() { return this.name; } public void setName(String name) { this.name = name; } @ElementCollection(fetch = FetchType.EAGER) public List<String> getNicknames() { return nicknames; } public void setNicknames(List<String> nicknames) { this.nicknames = nicknames; } @Override public String toString() { return "[" + id + ", " + name + "]"; } }
Let's launch it!
Finally, here's how you can see whether it works.
// ... public static void main(String[] args) { ApplicationContext appContext = new ClassPathXmlApplicationContext( new String[] { "applicationContext.xml" }); DogsBean dogsBean = appContext.getBean("dogsBean", DogsBean.class); Dog doggie = new Dog(); doggie.setName("Sega"); System.out.println("Any dogs: " + dogsBean.containsDogs()); dogsBean.persistDog(doggie); System.out.println("Any dogs: " + dogsBean.containsDogs()); } // ...It should first print false, then true - and lots of useful (or not) logs.
I hope this gives you an idea how to build such an application.
Issues and Solutions
Missing artifact org.hibernate:hibernate:jar:3.5.3-Final:compile
It happened to me that Maven was unable to fetch this lib - hibernate-3.5.3-Final.jar. If it happens to you, just download the correct (3.5.3-Final) Hibernate distribution ZIP/TAR file and put the missing file (it might be called hibernate3.jar) into your local repository manually (or your company corporate repository).
Caused by: java.io.FileNotFoundException: class path resource [applicationContext.xml] cannot be opened because it does not exist
Might happen when you run your application from Eclipse before you packaged it with Maven. That's a classpath issue. Just do Maven -> Clean and Maven -> Package, and then launch the application again.
I got my knowledge from here: Java Persistence with Hibernate
It helps me run this website when you buy the book via this link - thanks!
Download source code for this article
This comment has been removed by the author.
ReplyDeleteabout yout first issue:
ReplyDeletecorrect solution is to fix dependency declaration.
to first dependency add type pom:
[dependency]
[groupId]org.hibernate[/groupId]
[artifactId]hibernate[/artifactId]
[version]3.5.3-Final[/version]
[type]pom[/type]
[/dependency]
I understand that it's [type]pom[/type] that makes the difference? What does it do exactly?
ReplyDeletethat artifact in repository is just pom file listing modules (actual jars) and its packaging type set to pom. You can check this in repository.
ReplyDeleteSo it's actually supposed to be like that because it just references other jars? And it's those other jars where my dependencies come from?
ReplyDeleteHi, is it possible for you to guide me to develop web application using Spring 3,JPA and Maven. I am trying use this article but unable to do job for web application using maven. Can you explain me step by step may be simple login module as example.
ReplyDelete@Naresh, what problems are you facing? You can download the working source code.
ReplyDeleteI followed your example about not using JpaTemplate. As it turns out, I created my own example and now I have a very nagging "Session was already closed" problem. Any insights from you would be greatly appreciated.
ReplyDeleteIlango, it probably is a Hibernate problem - you are reading a lazy initialized collection after the session is closed, I suppose.
ReplyDeleteThis helped a lot! Thanx :)
ReplyDeleteWhen i run the Launcher.java i got the below error
ReplyDeleteInvocation of init method failed; nested exception is java.lang.NoClassDefFoundError: org/hibernate/annotations/common/reflection/MetadataProvider
Then i did below changes-
org.hibernate
hibernate-commons-annotations
3.2.0.Final
Now i managed to run the example
Thanks for your input, swaroop!
ReplyDeletei get the below error while i supose to run the code .. help me pls
ReplyDeleteError: Could not find or load main class me.m1key.springtx.Launcher
thanks in advance
نقدم لكم عمئلنا الكرام افضل شركة تنظيف فى بالطائف شركة تنظيف بالطائف شركة اوبى كلين ان عمليه التنظيف من اهم العوامل والوسائل التى تتطلب عملية شديد من الحرص والجهد والتعب اثناء عملية التنظيف شركة تنظيف منازل بالطائف مع شركة اوبى كلين وداعا تمام للتعب والجهد والمشقهه لان الشركة توفر لك كل انواع والعموال التى تساعدك فى عمليه التنظيف كما ايضا شركة اوبى كلين تمتلك افضل العماله المدربة على اكمل وجهه شركة تنظيف خزانات بالطائف حتى نوفر لك خدمه غير متاحه ال لدى شركة اوبى كلين وداعا للتعب بعد الان مع شركة اوبى كلين توفر لك كل سبل الراحه شركة مكافحة حشرات بالطائف لان الشركه متخصصه فى عملية التنظيف منذ زمن بعيد
ReplyDelete