Spring-Data-JPA

Implementing persistence layer may need some effort to configure and develop some DAO classes.

Spring-Data-JPA framework can simplify the creation of JPA based repositories and to reduce the amount of code needed to communicate with a database

   

Photo credit Ivan Kmit

This post is an introduction to Spring-Data-JPA. It describes, how you can configure the framewok when you are using Hibernate as your JPA provider.
In this post I will take profit from some Spring 4 new feature as generics autowiring to create generic service.
You can get the sample project from this link to start using Spring-Data-jpa and try some more advenced features.

1- Project dependencies

Spring-Data-JPA references a Spring 3 version. Maven will resolve dependencies by using Spring 3 version instead of version 4.

We need to exclude spring-core and spring-context and import Spring 4 version.


   <!-- Spring -->
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.0.4.RELEASE</version>
   </dependency>
   <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>1.5.2.RELEASE</version>
      <exclusions>
         <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
         </exclusion>
         <exclusion>
            <artifactId>spring-context</artifactId>
            <groupId>org.springframework</groupId>
         </exclusion>
     </exclusions>
   </dependency>

2- Add an Entity

@Entity
@Table(name = "users")
public class User {

    @Id
    @GenericGenerator(name = "generator", strategy = "increment")
    @GeneratedValue(generator = "generator")
    @Column(name = "id", nullable = false)
    private Long id;

    @Column(name = "first_name", nullable = false)
    private String firstName;

    /* Add some extra attributes */     

    /* Add getters and setters */

}

3- Create repository

Declare an interface extending Repository.

import com.mycompany.myproject.persist.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepo extends JpaRepository<User, Long> {

    User findByLogin(String login);

}

Spring-Data-JPA analyses the method name and generates a query using the JPA criteria.
Query generation is base on key words like (And, Or, Is, Equals, Between …). For more details, you can read the official Spring-Data-JPA documentation in this link.

4- Create Service

We make a choice to create a generic service that will reference the repository created in previous step.

One of the new features of Spring4 is generic types qualifier. We will take profit from this feature to inject repository by its generic interface.

public class GenericServiceImpl<T, D, ID extends Serializable> implements GenericService<T, D, ID> {

    @Autowired
    private JpaRepository<T, ID> repository;

    @Autowired
    private DozerBeanMapper mapper;

    protected Class<T> entityClass;

    protected Class<D> dtoClass;

    @SuppressWarnings("unchecked")
    public GenericServiceImpl() {
        ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
        this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
        this.dtoClass = (Class<D>) genericSuperclass.getActualTypeArguments()[1];
    }

    public D findOne(ID id) {
        return mapper.map(repository.findOne(id), dtoClass);
    }

    public List<D> findAll() {
        List<D> result = new ArrayList<D>();
        for (T t : repository.findAll()) {
            result.add(mapper.map(t, dtoClass));
        }
        return result;
    }
    
    public void save(D dto) {
        repository.saveAndFlush(mapper.map(dto, entityClass));
    }

}

Next, we add UserService that extends GenericServcie.

@Service
public class UserServiceImpl extends GenericServiceImpl<User, UserDto, Long> {
}

5- Configure the project

  • line 15 : EnableJpaRepositories annotation enables scanning package to look for repository interfaces (created in staep 3).
  • line 21 : We use HSQL embedded database. We need to provide schema script and data script.

import javax.sql.DataSource;
import org.dozer.DozerBeanMapper;
import org.springframework.context.annotation.*;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.*;
import org.springframework.orm.jpa.*;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@ComponentScan({"com.mycompany.myproject.persist", "com.mycompany.myproject.service"})
@EnableJpaRepositories("com.mycompany.myproject.persist")
public class JPAConfig {

    @Bean(name = "dataSource")
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).setName("myDb")
                .addScript("classpath:schema.sql").addScript("classpath:data.sql").build();
    }

    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan(new String[] { "com.mycompany.myproject.persist" });
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setShowSql(true);
        factoryBean.setJpaVendorAdapter(vendorAdapter);
        return factoryBean;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    @Bean
    public DozerBeanMapper getMapper() {
        return new DozerBeanMapper();
    }
}

6- Test your application

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {JPAConfig.class })
public class GenericServiceTest {
	
	@Autowired
	private GenericService<User, UserDto, Long> userService;
	
	@Autowired
    private GenericService<Authority, AuthorityDto, Long> authorityService;
	
	@Test
	public void testFindOneUser() {
		UserDto userDto = userService.findOne(1L);
		Assert.assertNotNull(userDto);
		Assert.assertEquals(1, userDto.getId().longValue());
	}
	
	@Test
    public void testFindOneAuthority() {
        AuthorityDto authorityDto = authorityService.findOne(1L);
        Assert.assertNotNull(authorityDto);
        Assert.assertEquals(1, authorityDto.getId().longValue());
    }
	
	@Test
    public void testFindAll() {
        List<AuthorityDto> authorities = authorityService.findAll();
        Assert.assertFalse(authorities.isEmpty());
    }
	
	@Test
    public void testSave() {
        AuthorityDto authorityDto = new AuthorityDto();
        authorityDto.setName("test authority");
    }
}


If you are a concise code adept you will appreciate using Spring-Data-JPA. To get your repositories you just need to add some interfaces for entities. You don’t need to write redundant basic code for each persistent bean.

The mix of Spring-DATA-JPA and Spring4 gives some extra possibilities to write generic code for the persistence layer.

Advertisements

Using Hibernate JPA in Spring

JPA (Java Persistence API) provides a good ORM (Objet Relational Mapper) solution. It has a large success because it’sa standard and it’s implemented by different vendor.

Hibernate Implements JPA specification. Using this implementation has the benefit of avoiding hbm.xml mapping files and to use annotations metatdata to specify the mapping between objects and relational database tables.

This article will show by a project example how to persist data with Hibernate JPA implementation and Spring Framework.

   
Photo Credit : tanakawho

The picture bellow presents the project structure and the different files created. If you are using maven you need to add dependency as shown in the picture, otherwise you have to download jars from Springsource and Hibernate web sites and to manage dependencies.

   

1 – Hibernate JPA configuration
The JPA interface needs a configuration file named persistence.xml in the application’s META-INF directory. The sourcecode bellow present the persistence.xml file used in this project.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
    <persistence-unit name="acme" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>com.mycompany.entity.CustomerOrder</class>
        <class>com.mycompany.entity.Customer</class>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
        </properties>
    </persistence-unit>
</persistence>

JPA persist a simple POJO Classe with some metatdata wich are specified in annotations. For more information about JPA annottion, see Chapter 8 “Metadata Annotations” of the JPA Specification . Here is an example data class:

import java.io.Serializable;
import java.util.List;
import javax.persistence.*;
import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name = "Customer")
public class Customer implements Serializable {

    public Customer() {super();}

public Customer(Integer taxId, String name, String adresse, String city, 
String state, String zip, String phone, List<CustomerOrder> CustomerOrders) {
        this.taxId = taxId;
        this.name = name;
        this.adresse = adresse;
        this.city = city;
        this.state = state;
        this.zip = zip;
        this.phone = phone;
        this.CustomerOrders = CustomerOrders;
    }

    @Id
    @GenericGenerator(name = "generator", strategy = "increment")
    @GeneratedValue(generator = "generator")
    @Column(name = "customer_id", nullable = false)
    private Long customerId;

    @Column(name = "tax_id", nullable = false)
    private Integer taxId;

    @Column(name = "name", nullable = false)
    private String name;

    @Column(name = "adresse", nullable = false)
    private String adresse;

    @Column(name = "city", nullable = false)
    private String city;

    @Column(name = "state", nullable = false)
    private String state;

    @Column(name = "zip", nullable = false)
    private String zip;

    @Column(name = "phone", nullable = false)
    private String phone;

    @OneToMany
    private List<CustomerOrder> CustomerOrders;

    // Add getters and setters
}

2 – Using JPA in Spring
In this part we explain how to integrate Hibernate JPA implementation into Spring.
The source code bellow present the application context used to configure Hibernate JPA into Spring Framework.

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:flow="http://www.springframework.org/schema/webflow-config"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:osgi="http://www.springframework.org/schema/osgi"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd
          http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
          http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-3.0.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
          http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
">
    
	<context:annotation-config/>
    <context:component-scan base-package="com.mycompany"/>
    

    <bean id="entityManagerFactory"
     class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceXmlLocation" value="persistence.xml"  />
        <property name="jpaVendorAdapter">
            <bean
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="generateDdl" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />
            </bean>
        </property>
    </bean>

    <bean id="dataSource"
     class="org.apache.commons.dbcp.BasicDataSource"
     destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:mem:test" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>

	<tx:annotation-driven />
    <bean id="transactionManager"
     class="org.springframework.orm.jpa.JpaTransactionManager" >
        <property name="entityManagerFactory" ref="entityManagerFactory"  />
    </bean>
      

</beans>

The code source bellow present the customer DAO.

package com.mycompany.dao.hibernate;

import java.util.Collection;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.mycompany.dao.ICustomerDao;
import com.mycompany.entity.Customer;

@Repository
public class CustomerDao implements ICustomerDao{
	
	@PersistenceContext
	private EntityManager em;

    public Collection<Customer> getAll() {
    	Query query = em.createQuery("from Customer");
        return query.getResultList();
    }
    
    public Collection<Customer> findByName(String name) {
    	Query query = em.createQuery("from Customer as c where c.name like :name");
    	query.setParameter("name", name);
        return query.getResultList();
    }

    public Customer getById(Long id) {
        return em.find(Customer.class, id);
    }

    @Transactional
    public void save(Customer customer) {
        em.persist(customer);
    }

    @Transactional
    public void delete(Long id) {
    	Customer customer = em.find(Customer.class, id);  
    	em.remove(customer);
    }
}