Spring Data下---Spring Data JPA的使用
Spring Data下---Spring Data JPA的使用
一、SpringData的環境搭建
依賴新增:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.0.RELEASE</version> </dependency>
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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--1.配置資料來源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="url" value="jdbc:mysql://localhost:3306/spring_data"/> </bean> <!--2.配置EntityManagerFactory--> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/> </property> <property name="packagesToScan" value="com.hcx"/> <property name="jpaProperties"> <props> <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <!--方言--> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> <!--執行時顯示sql--> <prop key="hibernate.show_sql">true</prop> <!--格式化sql--> <prop key="hibernate.format_sql">true</prop> <!--自動建立表:根據實體建立表--> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <!--3.配置事務管理器--> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <!--4.支援註解的事務--> <tx:annotation-driven transaction-manager="transactionManager"/> <!--5.配置spring data--> <jpa:repositories base-package="com.hcx" entity-manager-factory-ref="entityManagerFactory"/> <context:component-scan base-package="com.hcx"/> </beans>
Employee:
package com.hcx.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; /** * 僱員:開發實體類,通過實體類自動生成資料表 * Created by HCX on 2019/3/11. */ @Entity public class Employee { private Integer id; private String name; private Integer age; @Id//主鍵 @GeneratedValue //自增 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(length = 20) //設定該欄位的長度 public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
repository:
package com.hcx.repository; import com.hcx.domain.Employee; import org.springframework.data.repository.Repository; /** * Created by HCX on 2019/3/11. */ public interface EmployeeRepository extends Repository<Employee,Integer> { //Repository<Employee,Integer> 操作的實體類和主鍵 /** * 根據名字查詢員工 * @param name名字 * @return */ public Employee findByName(String name); }
EmployeeRepositoryTest:
package com.hcx.repository; import com.hcx.domain.Employee; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import static org.junit.Assert.*; /** * Created by HCX on 2019/3/11. */ public class EmployeeRepositoryTest { private ApplicationContext ctx = null; private EmployeeRepository employeeRepository = null; @Before public void setup() { ctx = new ClassPathXmlApplicationContext("beans-jpa.xml"); employeeRepository = ctx.getBean(EmployeeRepository.class); System.out.println("setup"); } @After public void tearDown() { ctx = null; System.out.println("tearDown"); } @Test public void testFindByName() throws Exception { Employee employee = employeeRepository.findByName("zhangsan"); System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } }
二、Spring Data JPA介面
1.Repository介面:
Repository介面是Spring Data的核心介面,不提供任何方法
public interface Repository<T,ID extends Serializable>{}
- T:要處理的實體類
- ID:T中ID的型別
- Repository是一個空介面,標記介面(沒有包含方法宣告的介面)
@RepositoryDefinition註解的使用
不繼承自Repository則可以使用@RepositoryDefinition:
@RepositoryDefinition(domainClass = Employee.class,idClass = Integer.class) public interface EmployeeRepository {// extends Repository<Employee,Integer> //Repository<Employee,Integer> 操作的實體類和主鍵 /** * 根據名字查詢員工 * @param name名字 * @return */ public Employee findByName(String name); }
2.Repository子介面:
①CrudRepository:繼承Repository,實現了CRUD相關的方法
②PagingAndSortingRepository:繼承CrudRepository,實現了分頁排序相關的方法。
③JpaRepository:繼承PagingAndSortingRepository,實現JPA規範相關的方法。
3.Repository中查詢方法定義規則和使用
1.SpringData中查詢方法名稱的定義規則

查詢規則定義和使用.png
EmployeeRepository:
package com.hcx.repository; import com.hcx.domain.Employee; import org.springframework.data.repository.Repository; import org.springframework.data.repository.RepositoryDefinition; import java.util.List; /** * Created by HCX on 2019/3/11. */ @RepositoryDefinition(domainClass = Employee.class,idClass = Integer.class) public interface EmployeeRepository {// extends Repository<Employee,Integer> //Repository<Employee,Integer> 操作的實體類和主鍵 /** * 根據名字查詢員工 * @param name名字 * @return */ public Employee findByName(String name); /** * 根據名字和年齡來查詢 * @param name 名字以某個字元開頭 * @param age 年齡小於多少 * @return */ public List<Employee> findByNameStartingWithAndAgeLessThan(String name,Integer age); /** * 根據名字和年齡來查詢 * @param name 名字以某個字元結束 * @param age * @return 年齡小於多少 */ public List<Employee> findByNameEndingWithAndAgeLessThan(String name,Integer age); /** * 根據名字和年齡來查詢 * @param names 名字在names集合中 * @param age * @return */ public List<Employee> findByNameInOrAgeLessThan(List<String> names,Integer age); /** * 根據名字和年齡來查詢 * @param names * @param age * @return */ public List<Employee> findByNameInAndAgeLessThan(List<String> names,Integer age); }
單元測試:
@Test public void testFindByNameStartingWithAndAgeLessThan() throws Exception { List<Employee> employees = employeeRepository.findByNameStartingWithAndAgeLessThan("test", 23); for (Employee employee : employees) { System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } } @Test public void testFindByNameEndingWithAndAgeLessThan() throws Exception { List<Employee> employees = employeeRepository.findByNameEndingWithAndAgeLessThan("6", 60); for (Employee employee : employees) { System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } } @Test public void testFindByNameInOrAgeLessThan() throws Exception { List<String> names = new ArrayList<String>(); names.add("test1"); names.add("test2"); names.add("test3"); List<Employee> employees = employeeRepository.findByNameInOrAgeLessThan(names, 22); for (Employee employee : employees) { System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } } @Test public void testFindByNameInAndAgeLessThan() throws Exception { List<String> names = new ArrayList<String>(); names.add("test1"); names.add("test2"); names.add("test3"); List<Employee> employees = employeeRepository.findByNameInAndAgeLessThan(names, 22); for (Employee employee : employees) { System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } }
對於按照方法命名規則來使用的弊端:
- 方法名較長:約定大於配置
- 對於一些複雜的查詢,很難實現
2.使用SpringData完成複雜查詢方法名稱的命名
Query註解的使用:
在repository方法中使用,則不需要遵循查詢方法命名規則:
- 只需要將@Query定義在Repository中的方法之上即可
- 支援命名引數及索引引數的使用
- 支援本地查詢
EmployeeRepository:
/** * 查詢員工表中id最大的資料 * @return */ @Query("select o from Employee o where id=(select max(id) from Employee t1)") //注意:此處Employee是類名 public Employee getEmployeeByMaxId(); /** * 根據名字和年齡查詢 使用索引引數 * @param name * @param age * @return */ @Query("select o from Employee o where o.name=?1 and o.age=?2") public List<Employee> queryParams1(String name,Integer age); /** * 根據名字和年齡查詢 使用命名引數 * @param name * @param age * @return */ @Query("select o from Employee o where o.name=:name and o.age=:age") public List<Employee> queryParams2(@Param("name") String name, @Param("age") Integer age); /** * 模糊查詢 使用索引引數 * @param name * @return */ @Query("select o from Employee o where o.name like %?1%") public List<Employee> queryLike1(String name); /** * 模糊查詢 使用命名引數 * @param name * @return */ @Query("select o from Employee o where o.name like %:name%") public List<Employee> queryLike2(@Param("name")String name); /** * 使用原生態的方式查詢 * @return */ @Query(nativeQuery = true,value="select count(1) from employee") //原生態查詢,使用表名 public long getCount(); }
EmployeeRepositoryTest:
@Test public void testGetEmployeeByMaxId() throws Exception { Employee employee = employeeRepository.getEmployeeByMaxId(); System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } @Test public void testQueryParams1() throws Exception { List<Employee> employees = employeeRepository.queryParams1("zhangsan", 20); for (Employee employee:employees) { System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } } @Test public void testQueryParams2() throws Exception { List<Employee> employees = employeeRepository.queryParams2("zhangsan", 20); for (Employee employee:employees) { System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } } @Test public void testQueryLike1() throws Exception { List<Employee> employees = employeeRepository.queryLike1("test"); for (Employee employee:employees) { System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } } @Test public void testQueryLike2() throws Exception { List<Employee> employees = employeeRepository.queryLike2("test1"); for (Employee employee:employees) { System.out.println("id:" + employee.getId() + " " + ",name:" + employee.getName() + " age:" + employee.getAge()); } } @Test public void testGetCount() throws Exception { long count = employeeRepository.getCount(); System.out.println("count:"+count); }
更新及刪除操作:
@Modifying註解使用
@Modifying結合@Query註解執行更新操作
@Transaction在Spring Data中的使用
事務在service層中使用
EmployeeRepository:
/** * 通過id更新年齡 * @param id * @param age */ @Modifying @Query("update Employee o set o.age=:age where o.id=:id") public void update(@Param("id")Integer id,@Param("age")Integer age);
EmployeeService:
package com.hcx.service; import com.hcx.repository.EmployeeRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * Created by HCX on 2019/3/13. */ @Service public class EmployeeService { @Autowired private EmployeeRepository employeeRepository; @Transactional public void update(Integer id,Integer age){ employeeRepository.update(id,age); } }
EmployeeServiceTest:
package com.hcx.service; import com.hcx.domain.Employee; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import static org.junit.Assert.*; /** * Created by HCX on 2019/3/13. */ public class EmployeeServiceTest { private ApplicationContext ctx = null; private EmployeeService employeeService = null; @Before public void setup() { ctx = new ClassPathXmlApplicationContext("beans-jpa.xml"); employeeService = ctx.getBean(EmployeeService.class); System.out.println("setup"); } @After public void tearDown() { ctx = null; System.out.println("tearDown"); } @Test public void testUpdate() throws Exception { employeeService.update(1, 55); } }
三、JPA中的常用介面
1.CrudRepository介面的使用
- save(entity)
- save(entities)
- findOne(id)
- exists(id)
- findAll()
- delete(id)
- delete(entity)
- delete(entities)
- deleteAll()
EmployeeCrudRepository:
package com.hcx.repository; import com.hcx.domain.Employee; import org.springframework.data.repository.CrudRepository; /** * Created by HCX on 2019/3/14. */ public interface EmployeeCrudRepository extends CrudRepository<Employee,Integer>{ }
EmployeeService:
@Transactional public void save(List<Employee> employees){ employeeCrudRepository.save(employees); }
testSave:
@Test public void testSave() { List<Employee> employees = new ArrayList<Employee>(); Employee employee = null; for (int i = 0; i < 100; i++) { employee = new Employee(); employee.setName("test" + 199); employee.setAge(100); //employee.setId(1); employees.add(employee); } employeeService.save(employees); }
2.PagingAndSortingRespository介面的使用
該介面包含分頁和排序的功能
帶排序的查詢:findAll(Sort sort)
帶排序的分頁查詢:findAll(Pageable pageable)
EmployeePagingAndSortingRepository:
package com.hcx.repository; import com.hcx.domain.Employee; import org.springframework.data.repository.PagingAndSortingRepository; /** * Created by HCX on 2019/3/15. */ public interface EmployeePagingAndSortingRepository extends PagingAndSortingRepository<Employee,Integer>{ }
EmployeePagingAndSortingRepositoryTest:
package com.hcx.repository; import com.hcx.domain.Employee; import com.hcx.service.EmployeeService; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import java.util.ArrayList; import java.util.List; /** * Created by HCX on 2019/3/14. */ public class EmployeePagingAndSortingRepositoryTest { private ApplicationContext ctx = null; private EmployeePagingAndSortingRepository employeePagingAndSortingRepository = null; @Before public void setup() { ctx = new ClassPathXmlApplicationContext("beans-jpa.xml"); employeePagingAndSortingRepository = ctx.getBean(EmployeePagingAndSortingRepository.class); System.out.println("setup"); } @After public void tearDown() { ctx = null; System.out.println("tearDown"); } @Test public void testPage() { /** * PageRequest(int page, int size) * page:從0開始 */ Pageable pageable = new PageRequest(0,5); Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable); System.out.println("查詢的總頁數:"+page.getTotalPages()); System.out.println("查詢的總記錄數"+page.getTotalElements()); System.out.println("當前第幾頁"+page.getNumber()); System.out.println("當前頁面的集合"+page.getContent()); System.out.println("當前頁面的記錄數"+page.getNumberOfElements()); } @Test public void testPageAndSort(){ Sort.Order order = new Sort.Order(Sort.Direction.DESC,"id"); Sort sort = new Sort(order); Pageable pageable = new PageRequest(0,5,sort); Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable); System.out.println("查詢的總頁數:"+page.getTotalPages()); System.out.println("查詢的總記錄數"+page.getTotalElements()); System.out.println("當前第幾頁"+page.getNumber()); System.out.println("當前頁面的集合"+page.getContent()); System.out.println("當前頁面的記錄數"+page.getNumberOfElements()); } }
3.JpaRespository介面的使用
- findAll
- findAll(Sort sort)
- save(entities)
- flush
- deleteInBatch(entities)
EmployeeJpaRepository:
package com.hcx.repository; import com.hcx.domain.Employee; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.PagingAndSortingRepository; /** * Created by HCX on 2019/3/15. */ public interface EmployeeJpaRepository extends JpaRepository<Employee,Integer>{ }
EmployeeJpaRepositoryTest:
package com.hcx.repository; import com.hcx.domain.Employee; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import static org.junit.Assert.*; /** * Created by HCX on 2019/3/15. */ public class EmployeeJpaRepositoryTest { private ApplicationContext ctx = null; private EmployeeJpaRepository employeeJpaRepository = null; @Before public void setup() { ctx = new ClassPathXmlApplicationContext("beans-jpa.xml"); employeeJpaRepository = ctx.getBean(EmployeeJpaRepository.class); System.out.println("setup"); } @After public void tearDown() { ctx = null; System.out.println("tearDown"); } @Test public void testFind(){ Employee employee = employeeJpaRepository.findOne(99); System.out.println("employee:"+employee); System.out.println("employee(10):"+employeeJpaRepository.exists(10)); System.out.println("employee(1000):"+employeeJpaRepository.exists(1000)); } }
4.JpaSpecificationExecutor介面的使用
Specification封裝了JPA Criteria查詢條件
EmployeeJpaSpecificationExecutorRepository:
package com.hcx.repository; import com.hcx.domain.Employee; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * Created by HCX on 2019/3/15. */ public interface EmployeeJpaSpecificationExecutorRepository extends JpaRepository<Employee,Integer>,JpaSpecificationExecutor<Employee>{ }
EmployeeJpaSpecificationExecutorRepositoryTest:
package com.hcx.repository; import com.hcx.domain.Employee; import com.hcx.service.EmployeeService; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.*; import static org.junit.Assert.*; /** * Created by HCX on 2019/3/15. */ public class EmployeeJpaSpecificationExecutorRepositoryTest { private ApplicationContext ctx = null; private EmployeeJpaSpecificationExecutorRepository employeeJpaSpecificationExecutorRepository = null; @Before public void setup() { ctx = new ClassPathXmlApplicationContext("beans-jpa.xml"); employeeJpaSpecificationExecutorRepository = ctx.getBean(EmployeeJpaSpecificationExecutorRepository.class); System.out.println("setup"); } @After public void tearDown() { ctx = null; System.out.println("tearDown"); } @Test public void testQuery() { Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id"); Sort sort = new Sort(order); Pageable pageable = new PageRequest(0, 5, sort); Specification<Employee> specification = new Specification<Employee>() { /** * * @param root 要查詢的條件(Employee) * @param criteriaQuery新增查詢條件 * @param criteriaBuilder 構建Predicate * @return */ @Override public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { //root(employee(age)) Path path = root.get("age"); Predicate predicate = criteriaBuilder.gt(path, 50); return predicate; } }; Page<Employee> page = employeeJpaSpecificationExecutorRepository.findAll(specification,pageable); System.out.println("查詢的總頁數:" + page.getTotalPages()); System.out.println("查詢的總記錄數" + page.getTotalElements()); System.out.println("當前第幾頁" + page.getNumber()); System.out.println("當前頁面的集合" + page.getContent()); System.out.println("當前頁面的記錄數" + page.getNumberOfElements()); } }
補充:
更新操作時,先查詢出來,再設定某個屬性,此時updateTime不會自動更新,這是需要在實體上加上@DynamicUpdate註解
使用lombok外掛簡化實體的書寫:
xml:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
在IDEA中使用需要安裝相應的外掛
通過在實體上加上@Data註解,簡化get()、set()、toString()等方法的書寫
如果只想省略get方法,可以使用@Getter註解
@Entity @DynamicUpdate @Data public class ProductCategory { /** 類目id. */ @Id //主鍵 @GeneratedValue //自增 private Integer categoryId; /** 類目名字. */ private String categoryName; /** 類目編號. */ private Integer categoryType; private Date createTime; private Date updateTime; }
Demo連結: https://github.com/GitHongcx/springdataDemo
注:本文部分內容來自慕課網