Spring Data JPA Fun


Maven dependencies:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.21</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>4.1.9.Final</version>
</dependency>
<dependency>
  <groupId>javax.transaction</groupId>
  <artifactId>jta</artifactId>
  <version>1.1</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>3.2.0.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-orm</artifactId>
  <version>3.2.0.RELEASE</version>
</dependency>

Entity Manager Factory

Used to bootstrap JPA and also Hibernate inside our application. The LocalContainerEntityManagerFactoryBean which is packaged in the spring-orm.jar, references the defined persistence unit.

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="punit" />
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
        </bean>
    </property>
    <property name="jpaPropertyMap">
        <map>
            <entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
            <entry key="hibernate.hbm2ddl.auto" value="none" />
            <entry key="hibernate.format_sql" value="true" />
        </map>
    </property>
</bean>

Transaction Manager

The JpaTransactionManager (packaged within spring-tx.jar) which sucks in a reference to the entityManagerFactory, is responsible for taking care of transactions within the JPA layer.

JPA Annotations

  • @Entity - flags an object as a persistable thing
  • @Table - useful for describing more db related concerns such as schema name, and so on.
  • @Id - flags a member variable as a simple primary key
  • @GeneratedValue - compliments the @Id annotation. Four options, IDENTITY, AUTO, SEQUENCE and TABLE. The TABLE option be tied in with the @TableGenerator annotation.
  • @Column - like @Table, provides way to control how fields are manifested as physical db columns, such as its name, nullability, uniqueness, and so on.
  • @PersistenceContext - relates to a specific persistence unit. This guy will inject the entity manager into the running application.
  • @Service - for business logic, or the entry point to lots of business logic such as a facade.
  • @Repository - a place where database interaction occurs.
  • @Transactional - self explanitory, it takes care of transactions. Actually its quite awesome, and rids boilerplate type begin/commit/rollback cruft.

    import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table;

    @Entity @Table(name=”goals”) public class Goal {

    @Id
    @GeneratedValue
    private Long id;
    ...
    

Here’s a simple repository pattern, that makes use of JPA’s EntityManager. Note the flush command here is essential, to force the EntityManager to commit here and now.

package net.bencode.repository;

import net.bencode.model.Protein;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Repository("proteinRepository")
public class ProteinRepository implements IProteinRepository {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public Goal save(Goal goal) {
        entityManager.persist(goal);
        entityManager.flush();
        return goal;
    }
}

The above repository alone will fail, due to the absence of a transaction. If you are invoking your repository from a service layer, that is a great place to do so using the @Transactional annotation, for example:

package net.bencode.service;

import net.bencode.model.Goal;
import net.bencode.repository.ProteinRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("proteinService")
public class ProteinService implements IProteinService {

    @Autowired
    private ProteinRepository proteinRepository;

    @Override
    @Transactional
    public Protein save(Protein protein) {
        return proteinRepository.save(protein);
    }
}

Joining things

  • @OneToOne
  • @OneToMany - one of the things I am in, can contain many other things.
  • @ManyToOne - the thing I am in, belongs to something.
  • @ManyToMany

Protein.java

@OneToMany(mappedBy="protein", cascade=CascadeType.ALL, fetch=FetchType.LAZY) //FetchType.EAGER
private List<Store> stores = new ArrayList<Store>();

Store.java

@ManyToOne
private Protein protein;

JPQL (Java Persistence Query Language)

A query language that focuses on objects.

Traditional SQL

select * from supplements

JPQL

Query query = entityManager.createQuery("Select s from Supplement s")

LazyInitializationException

A common issue when using FetchType.LAZY in the context of a web application, is that the lifecycle of a JPA context/session is that of a single HTTP request/response cycle.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: net.bencode.model.HaloSkull, could not initialize proxy - no Session org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566) org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186) org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545) org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124) org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:266) org.apache.taglibs.standard.tag.common.core.ForEachSupport.toForEachIterator(ForEachSupport.java:348) org.apache.taglibs.standard.tag.common.core.ForEachSupport.supportedTypeForEachIterator(ForEachSupport.java:224) org.apache.taglibs.standard.tag.common.core.ForEachSupport.prepare(ForEachSupport.java:155) javax.servlet.jsp.jstl.core.LoopTagSupport.doStartTag(LoopTagSupport.java:256) org.apache.jsp.WEB_002dINF.jsp.getGoals_jsp._jspx_meth_c_005fforEach_005f1(getGoals_jsp.java:172) org.apache.jsp.WEB_002dINF.jsp.getGoals_jsp._jspx_meth_c_005fforEach_005f0(getGoals_jsp.java:132) org.apache.jsp.WEB_002dINF.jsp.getGoals_jsp._jspService(getGoals_jsp.java:81) org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) javax.servlet.http.HttpServlet.service(HttpServlet.java:728)

The OpenEntityManagerInViewFilter prevents the JPA transactional session from being closed, as a result of the request/response cycle. To use it simply register the filter in your web.xml:

<filter>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Spring Data JPA

Spring Data JPA is a wrapper for JPA, that eliminates a large amount of boilerplate data access layer code that plain JPA requires. It also is very extensible, catering for more complex scenarios. Take for example the save implementation on a repository class:

public HaloSkull save(HaloSkull skull) {
  if (skull.getId() == null) {
    entityManager.persist(skull);
    entityManager.flush();
  }
  else {
    skull = entityManager.merge(skull);
  }
  return skull;
}

First up, dependencies. Hack your pom.xml and add a new dependency. Note the transisitive dependency on spring-aop is incompatible with Spring MVC, and should be excluded if consumed in the context of an MVC application.

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-jpa<artifactId/>
  <version>1.3.0.RELEASE</version>
  <exclusions>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop<artifactId/>
  </exclusions>
</dependency>

Edit your jpaContext.xml and register the Spring Data JPA repositories element:

<jpa:repositories base-package="net.bencode.repository" />

Then for the real magic, when it comes to cleaning up repostiories implementations. Repository concrete classes can more or less be dumped. Interfaces become the implementations.

@Repository("haloRepository")
public interface HaloRepository extends JpaRepository<Halo, long> { }

That’s it. No handcoded repository implementation classes required (unless specific customisations are needed).