Pages

Friday, September 27, 2013

Simple Example of Using Spring Data for JPA

Alright. Now that spring data has bee out for another abstract layer of data access, it is time to try it to see how easy it is for the real use.

Here I just built a very simple example based on the previous JPA example. It is recommended to go through the previous JPA example first before checking on this example. The source code of the updated example can be found at github.

To make it even simpler, I changed the JPA provider to hibernate as it seems hibernate works better with Spring than Eclipselink. I had to admit I encountered some weird errors when building this example with Eclipselink. It appears that Eclipselink is more strict or less error tolerant when it comes to JPQL.

We need an updated beans.xml to enable spring data for JPA. The following is the updated beans.xml file. Please note the bold font in it.
[beans.xml]
<?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:p="http://www.springframework.org/schema/p"
 xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/tx 
 http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd
 http://www.springframework.org/schema/data/jpa 
 http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
 ">

 <!-- the base package for spring data jpa repository interfaces -->
 <jpa:repositories base-package="com.mytechtip.example.springjpatest.repository" />

 <!-- Enable the component scan (auto wiring etc) for the following package -->
 <context:component-scan base-package="com.mytechtip.example.springjpatest" />
    
 <!-- Make sure the following is specified to enable transaction  -->
 <tx:annotation-driven />
    <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <!--  This defines the entity manager factory with some custom properties -->
 <bean id='entityManagerFactory' class='org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean'>
  <property name="persistenceUnitName" value="spring-jpa-test"/>
  <property name='dataSource' ref='dataSource' />
 </bean>

 <!-- This defines the hsqldb data source -->
 <bean id='dataSource' class='org.springframework.jdbc.datasource.DriverManagerDataSource'>
  <property name='driverClassName' value='org.hsqldb.jdbc.JDBCDriver' />
  <property name='url' value='jdbc:hsqldb:file:spring_jpa_test_db' />
  <property name='username' value='sa' />
  <property name='password' value='' />
 </bean>
</beans>

Then we just create the very simple JPA repository interface in the above mentioned base package as follows:
[UserRepository.java]
package com.mytechtip.example.springjpatest.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.mytechtip.example.springjpatest.User;

public interface UserRepository extends JpaRepository<User, Long> {
  
  List<User> getNameStartsWith(String prefix);

}

We need to update the User class to add the named query which should have the same name defined in the above repository interface.
[User.java]
package com.mytechtip.example.springjpatest;

import java.io.Serializable;
import java.lang.String;
import java.util.Date;
import javax.persistence.*;

/**
 * Entity implementation class for Entity: User
 *
 */
@Entity
@Table(name="user")
@NamedQueries({
  @NamedQuery(name="User.getNameStartsWith", 
      query="select u from User u where u.name LIKE ? order by u.name")
})
public class User implements Serializable {

  private static final long serialVersionUID = 1L;

  
  @Id
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  private Long id;
  private String name;
  private String address;
  
  @Temporal(TemporalType.DATE)
  private Date dob;

  public User() {
    super();
  }   
  public Long getId() {
    return this.id;
  }

  public void setId(Long id) {
    this.id = id;
  }   
  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }   
  public String getAddress() {
    return this.address;
  }

  public void setAddress(String address) {
    this.address = address;
  }   
  public Date getDob() {
    return this.dob;
  }

  public void setDob(Date dob) {
    this.dob = dob;
  }
   
}


An abstract user data access object has been extracted so I can have different implementation versions of DAO (the plain old one and the one using new spring data). The following is the implementation based on Spring Data.
[UserDaoRepo.java]
package com.mytechtip.example.springjpatest;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.mytechtip.example.springjpatest.repository.UserRepository;

@Repository
public class UserDaoRepo extends AbstractUserDao {
  
  private static final int ONE_YEAR = 1000 * 60 * 60 * 24 * 365;

  @Autowired
  UserRepository userRepository;
  
  @Transactional
  public User save(User u) {
    // some business logic
    if (u.getName()==null) {
      throw new IllegalArgumentException("Name can not be null");
    }
    if (u.getDob()==null) {
      throw new IllegalArgumentException("Dob can not be null");
    }
    if ((System.currentTimeMillis()-u.getDob().getTime())< 10 * ONE_YEAR) {
      throw new IllegalArgumentException("Must be 10+ year older");
    }
    return userRepository.save(u);
  }

  @Transactional
  public List<User> getNameStartsWith(String namePrefix) {
    // use the spring data jpa repository
    return userRepository.getNameStartsWith(namePrefix+"%");
  }
}

Then just update the Main class to use the UserDataRepo to run the simple example.

As mentioned above, the updated code is available from github if you want to try it yourself.




No comments:

Post a Comment