Data persistence, ORM, and transactions
JPQL query
2.5 Working with JPA (optional)
public void deleteContact(Long id) { contactDao.deleteById(id);
} }
As you can see, this “service” is a pass-through layer to the DAO. In a more realistic application, you’d expect to see service beans include more domain logic. Sometimes this is domain logic proper, and sometimes it’s other non-domain logic that for what-ever reason hasn’t been externalized. Examples are validation, security, messaging, and workflow. In any event, you’ve successfully moved the persistence concern out of the service bean into a dedicated tier.
This recipe was just a refactoring; rerun the application to ensure that it still func-tions before continuing on to the next recipe. Don’t forget to add
<context:component-scan
base-package="com.springinpractice.ch02.dao.hbn" />
to beans-service.xml so Spring can find the HbnContactDao you created.
Discussion
You’ve defined general persistence operations in the Dao interface. AbstractHbnDao implements this interface using Hibernate’s Session API. You create entity-specific interfaces by extending Dao, and you create entity-specific implementations by extend-ing AbstractHbnDao.
The next two recipes are optional because they present material that you don’t use elsewhere in the book. But we strongly recommend that you review them, because they present an official framework for building DAOs, similar to the earlier ones, based on JPA and Spring Data JPA. Spring Data JPA is a fairly recent addition to the Spring portfolio, and we didn’t have time to rework all the examples in the book to use JPA and Spring Data JPA instead of Hibernate and custom DAOs. But we would have liked to have done so. Look at the next two recipes, and consider using the approach described in your own projects.
2.5 Working with JPA (optional)
PREREQUISITES
Recipe 2.4, “Creating a data access layer”
KEY TECHNOLOGIES JPA, Hibernate Background
Hibernate was a pioneer in Java-based ORM. Eventually the idea caught on, and Sun created the Java Persistence API (JPA) standard around ORM, based in large part on Hibernate. Although you can use Hibernate in a standalone fashion, it’s a compliant
Figure 2.9 DAOs call the Hibernate API directly.
Figure 2.10 DAOs call JPA in-terfaces. You can bind an arbi-trary persistence provider to the interface (EclipseLink JPA, OpenJPA, Hibernate, and so on).
JPA implementation. So you also have the option of using JPA and treating Hibernate as a persistence provider. This allows you to use JPA-based frameworks like Spring Data JPA, which you’ll pursue in the follow-ing recipe.
Problem
Use the standard JPA API instead of using Hibernate’s proprietary persistence API.
Solution
So far, we’ve described a configuration in which applica-tions (the DAO part of apps, anyway) work directly with the Hibernate API. Although you do use standardized JPA annotations to declare the mappings on the entities (Hibernate understands them), you’ve been using Hibernate’s SessionFactory, Session, Query, and so forth to implement the DAOs (see figure 2.9).
There’s little danger in doing this because you’ve iso-lated all such code in the data access layer, and this is the approach used throughout the book. It’s a more or less straightforward configuration, and it’s nice for developers who are already familiar with the well-known Hibernate API.
But you may prefer to use standardized JPA interfaces such as EntityManagerFactory and EntityManager when you implement your DAOs. This approach offers additional flexibility with respect to choosing a persis-tence provider. You can, of course, continue to use Hiber-nate, because it’s a mature JPA implementation. But you have other options, such as EclipseLink and OpenJPA. Figure 2.10 shows the JPA-based configuration.
The following listing shows how to reimplement ContactDao using JPA.
package com.springinpractice.ch02.dao.jpa;
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import com.springinpractice.ch02.dao.ContactDao;
Listing 2.13 JpaContactDao.java: a JPA-based DAO
import com.springinpractice.ch02.model.Contact;
@Repository
public class JpaContactDao implements ContactDao {
@PersistenceContext private EntityManager entityManager;
public void create(Contact contact) {
entityManager.persist(contact);
}
public Contact get(Serializable id) {
return entityManager.find(Contact.class, id);
}
public List<Contact> getAll() {
return (List<Contact>) entityManager .createQuery("from Contact") .getResultList();
}
... other persistence methods ...
}
This implementation is similar to what you did with Hibernate in listings 2.10 and 2.11.
You begin by using the JPA @PersistenceContext annotation to inject not an Entity-ManagerFactory (the analog to Hibernate’s SessionFactory) but a shared, thread-safe EntityManager
B
. This is analogous to injecting a Hibernate Session, which you can do with JPA.You also have standard persistence methods, this time implemented against the EntityManager instead of against a Hibernate Session
C
.The other piece relevant to Spring/JPA integration is the application context con-figuration.
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/
➥ spring-context-3.1.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.1.xsd http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/Sip02DS"
Listing 2.14 beans-service.xml with a JPA-based persistence configuration
EntityManager
B
Persistence method
C
resource-ref="true" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.
➥ LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:packagesToScan="com.springinpractice.ch02.model">
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQL5Dialect </prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:annotation-driven />
<context:component-scan
base-package="com.springinpractice.ch02.dao.jpa" />
<context:component-scan
base-package="com.springinpractice.ch02.service.impl" />
</beans>
As with the DAO, the JPA version of bean-service.xml is similar to its Hibernate coun-terpart. You create a LocalContainerEntityManagerFactoryBean (analogous to the AnnotatedSessionFactoryBean) at
B
. There are other options, depending in part on what sort of container you want to use, but this is the one that makes sense for web containers such as Tomcat and Jetty. (See the Spring reference documentation for more information.) You don’t have to provide an explicit persistence.xml JPA configu-ration because you specify the DataSource, provider, and provider-specific properties right here in the Spring configuration. You create a JpaTransactionManager atC
, which takes the place of the HibernateTransactionManager you used formerly.You’ve completely decoupled the app from Hibernate. You can still use Hibernate as a JPA persistence provider, or you can use EclipseLink JPA, OpenJPA, or any other JPA provider.
As with previous recipes, verify that the contact-management app still works before continuing to the final recipe in this chapter.
Discussion
The next recipe builds on what you’ve done here with JPA. It presents the Spring Data JPA project, which allows you to simplify your already simple DAO layer even further.
EntityManagerFactory
B
TransactionManager