The data support framework of Spring provides extensive utilities to integrate Hibernate. It is common to find many production applications using Hibernate as an ORM framework in Spring applications. In fact, they go pretty well with each other in leveraging the productivity of the developer.
Watch Out
As a Java programmer, sooner or later, you will encounter these two architectures and any seasoned programmer will tell you that the worst part of integrating two frameworks can be a nightmare. The dependency and configuration issues can be irritating at times, especially due to version mismatch or any other configuration changes, etc. While integrating Hibernate and Spring there are a lot of configuration, stereotype annotation issues that needs to be taken care of. However, do not let that stop you; be assured that the end result is always sweet. Assuming one has some experience with Spring and Hibernate but has never combined them, let us integrate and go hands-on with the help of a sample application.
Dependency Workout
We can use any project management and comprehension tool for integrating the required dependencies in our project. This can be especially helpful while dealing with complex dependencies. Maven is a popular project management tool that provides a uniform build system. But we will be a little rigid here, and go the old-fashioned way of downloading the frameworks and required jars individually. Once downloaded, create a library of each of them and import them into the project. To be specific our project has following dependencies apart from Java SDK, Java EE7 (especially for using JPA), MySQL5 and Netbeans IDE.
1. Spring Framework (version 4.x): Spring is comprehensively featured framework, specifically for enterprise development. Creating EJB in J2EE used to be a cumbersome experience, however that is not the case now, especially from EE7. Spring eased this difficulty to a great extent with many other features. However, one thing for sure Spring framework provides almost every feature that the current trend of enterprise development needs and one may not have to look back for more.
2. Hibernate ORM Framework (version 4.x): Where iBatis retired to namesake MyBatis, Hibernate rules the ORM world in Java. This de-facto standard of object relational modeling influenced JPA specification when Entity Beans of EJB were fumbling for an inspiration. Hibernate is going to stay in the time line of oblivion for a many years to come and evolve significantly. This can be an important ORM sidekick of the Spring framework when used effectively.
3. Apache Commons DBCP (version 2.x) and Commons Pool (version 2.x): Jestingly, Apache is full of commons (sense). They invariably provide something or the other for almost every framework in the market. These two jars provide data source related implementation and pooling of database connection. However, Spring also provides an inherent data source related implementation, for example, DriverManagerDataSource for JDBC. But apart from many other third party implementations, DBCP along with Commons Pool are some of the popular connection and pooling related implementations used in the industry. Read DBCP overview and Commons Pool overview for more detail.
4. Apache Commons AOP Alliance (Version 1.0.0): See, common (sense) again. This is one of the dependencies for the Spring framework especially for AOP (Aspect Oriented Programming). It generally comes with the Spring library. But the case is that a little older version of the Spring framework provides the beta version of this jar. So depending on the version one is using one may or may not have to specifically download this jar and add on to the project. I found a lot of errors and warnings due to the beta version of this jar in my Spring library.
5. Apache Commons Logging (Version 1.x): And yet again, this jar is necessary only if the logging feature is used in the project and it’s a common practice to log information of various actions at application run time. As said, unnecessary but good to include one.
6. MySQL Connector/J (Version 5.x): This is MySQL JDBC driver and comes in a jar file. Since we will be using MySQL database at the back end, this driver is a must.
Let’s Start
Our project will have two model classes – Contact and Address with one to many relation between them. One data access object interface – ContactDao and its implementation class ContactDaoImpl. The code given below is simple and self-explanatory for anyone with a little familiarity with these frameworks. Use the Java docs for additional information. The XML configuration file, spring.xml is the crux of integrating Spring and Hibernate.
Listing 1: spring.xml configuring Spring and Hibernate
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns_context="http://www.springframework.org/schema/context" xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns_tx="http://www.springframework.org/schema/tx" xmlns_p="http://www.springframework.org/schema/p" xsi_schemaLocation=" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--<context:annotation-config />--> <tx:annotation-driven /> <context:component-scan base-package="org.mano.app" /> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/contact_db?zeroDateTimeBehavior=convertToNull" /> <property name="username" value="user" /> <property name="password" value="secret" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan" value="org.mano.app.model" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">create-drop</prop> </props> </property> <property name="annotatedClasses"> <list> <value>org.mano.app.model.Contact</value> <value>org.mano.app.model.Address</value> </list> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager" p:sessionFactory-ref="sessionFactory"> </bean> </beans>
Listing 2: Contact.java model class for Contact
package org.mano.app.model; //...import statements @Entity @Table(name = "contact") public class Contact implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") private long id; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; @Temporal(TemporalType.DATE) @Column(name = "BIRTH_DATE") private Date birthDate; @Column(name = "EMAIL") private String email; @Column(name = "PHONE_NUMBER") private String phoneNumber; @OneToMany(fetch = FetchType.EAGER, mappedBy = "contact", cascade = CascadeType.ALL, orphanRemoval = true) private Set<Address> contactAddress = new HashSet<>(); public Contact() { super(); } public Contact(String firstName, String lastName, Date birthDate, String email, String phoneNumber) { this.firstName = firstName; this.lastName = lastName; this.birthDate = birthDate; this.email = email; this.phoneNumber = phoneNumber; } public void addAddress(Address address) { address.setContact(this); getContactAddress().add(address); } public void removeAddress(Address address) { getContactAddress().remove(address); } //...getters and setters @Override public String toString() { return firstName + " " + lastName + "nBirth Date: " + birthDate + "nEmail: " + email + " nPhone Number: " + phoneNumber ; } }
Listing 3: Address.java model class for Address
package org.mano.app.model; //...import statements @Entity @Table(name = "contact_address") public class Address implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") private long id; @Column(name = "STREET") private String street; @Column(name = "CITY") private String city; @Column(name = "COUNTRY") private String country; @Column(name = "ZIP") private String zip; @ManyToOne @JoinColumn(name = "CONTACT_ID") private Contact contact; public Address() { super(); } public Address(String street, String city, String country, String zip) { super(); this.street = street; this.city = city; this.country = country; this.zip = zip; } //...getters and setters @Override public String toString() { return street + ", city=" + city + ", country=" + country + ", zip=" + zip; } }
Listing 4: ContactDao.java, data access object interface
package org.mano.app.dao; //...import statement import org.mano.app.model.Contact; public interface ContactDao { public List<Contact> findAll(); public Contact save(Contact contact); public void delete(Contact contact); }
Listing 5: ContactDaoImpl.java, ContactDao implemenation
package org.mano.app.dao; //...import statements @Repository(value = "contactDao") @Transactional public class ContactDaoImpl implements ContactDao { private final Log log = LogFactory.getLog(ContactDaoImpl.class); @Autowired private SessionFactory sessionFactory; @Override public List<Contact> findAll() { return sessionFactory.getCurrentSession().createQuery("from Contact c").list(); } @Override public Contact save(Contact contact) { sessionFactory.getCurrentSession().saveOrUpdate(contact); log.info("Contact Saved..." + contact.getId()); return contact; } @Override public void delete(Contact contact) { sessionFactory.getCurrentSession().delete(contact); log.info("Contact removed..." + contact.getId()); } }
Listing 6: Main.java, from where we test our application
package org.mano.app; //...import statements public class Main { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF/spring.xml"); ContactDao contactDao = ctx.getBean("contactDao", ContactDao.class); Contact c[] = new Contact[5]; c[0] = new Contact("Andrew", "Jones", new Date(), "aj@gmail.com", "123456789"); c[0].addAddress(new Address("111 ABC Street", "LA", "US", "112233")); c[0].addAddress(new Address("222 ERT Street", "CA", "US", "445566")); c[0].addAddress(new Address("333 PPP Street", "LA", "US", "667788")); c[1] = new Contact("Peter", "Parker", new Date(), "pp@hotmail.com", "654213688"); c[1].addAddress(new Address("999, XYZ street", "SOMEWHERE", "UK", "223344")); c[1].addAddress(new Address("888, KKK street", "ANYWHERE", "UK", "776644")); c[2] = new Contact("Lee", "Falk", new Date(), "lf@hotmail.com", "73645542"); c[2].addAddress(new Address("555, QPQ street", "NOWHERE", "IN", "776655")); c[2].addAddress(new Address("444, WIW street", "INWHERE", "IN", "775544")); c[3] = new Contact("Arun", "Roy", new Date(), "ar@gmail.com", "617864872"); c[3].addAddress(new Address("767, TTT street", "DL", "IN", "667788")); c[3].addAddress(new Address("898, YYY street", "MB", "IN", "554477")); c[4] = new Contact("Simrit", "Ahuja", new Date(), "sa@hotmail.com", "78766542"); c[4].addAddress(new Address("90, ZZZ street", "GK", "SA", "339988")); for (int i = 0; i < 5; i++) { contactDao.save(c[i]); } List<Contact> contacts = contactDao.findAll(); for (Contact con : contacts) { System.out.println("---------------------------------------------------"); System.out.println(con.toString()); Set<Address> addresses = con.getContactAddress(); int i=1; for (Address a : addresses) { System.out.printf("Address %d: %s", i, a.toString()); System.out.println(); i++; } } } }
Fig 1: Snapshot of the project output from Netbeans
Conclusion
The main hurdle for first time users would be writing the right configuration parameters in the XML file. Working individually with Spring and Hibernate is not much of a problem. Watch out for version mismatch between Hibernate and Spring and any other jars; consult their manual if any error crops up during compilation or runtime execution. Also watch out for <beans xmlns… in spring.xml declaration parameters. A mistake in the code is visible most of the time but a mistake in the configuration parameters is not always obvious. Happy coding!