Spring Data Jpa
最近博主越来越懒了,深知这样不行。还是决定努力奋斗,如此一来,就有了一下一波复习
演示代码都基于Spring Boot + Spring Data JPA
传送门: 博主的测试代码
------------------------------------------------------------------------------------------------------------------------------
什么是Spring Data JPA?
Spring Data 是Spring提供的操作数据的框架在Spring data JPA是Spring data的一个模块,通过Spring data 基于jpa标准操作数据的模块。
Spring Data的核心能力,就是基于JPA操作数据,并且可以简化操作持久层的代码。
Spring Data JPA提供的核心接口
前提数据:
-- MySQL dump 10.13 Distrib 5.6.16, for debian-linux-gnu (x86_64) -- -- Host: localhost Database: amber -- ------------------------------------------------------ -- Server version 5.6.16-1~exp1 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `table_user` -- DROP TABLE IF EXISTS `table_user`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `table_user` ( `id` int(11) NOT NULL DEFAULT '0', `name` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `address` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `table_user` -- LOCK TABLES `table_user` WRITE; /*!40000 ALTER TABLE `table_user` DISABLE KEYS */; INSERT INTO `table_user` VALUES (4,'tony',NULL,'shanghai',18),(5,'amber',NULL,'shanghai',18),(2,'amber1',NULL,'shanghai',18),(3,'tony',NULL,'shanghai',18),(0,'amber1',NULL,'shanghai',18),(6,'amber',NULL,'shanghai',18); /*!40000 ALTER TABLE `table_user` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2019-01-13 13:43:57
package com.amber.pojo; import lombok.Data; import javax.persistence.*; @Entity @Table(name = "table_user") @Data public class User { @Id @GeneratedValue private Integer id; private String name; private Integer age; private String address; }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>groupId</groupId> <artifactId>SpringBoot-JPA</artifactId> <version>1.0-SNAPSHOT</version> <properties> <java.version>1.8</java.version> <lombok.version>1.16.20</lombok.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>6.0.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.8</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> </dependencies> </project>
Repository:
@org.springframework.stereotype.Repository public interface UserRepository extends Repository<User, Integer> { }
- 提供了方法名成查询方式:
方法的名称要遵循 findBy + 属性名(首字母大写) + 查询条件(首字母大写 Is Equals)
findByNameLike(String name)
findByName(String name)
findByNameAndAge(String name, Integer age)
findByNameOrAddress(String name) 等...
- 基于@Query注解的查询和更新
/** * SQL nativeQuery的值是true 执行的时候不用再转化 * @param name * @return */ @Query(value = "SELECT * FROM table_user WHERE name = ?1", nativeQuery = true) List<User> findByUsernameSQL(String name);
//基于HQL
/** * 基于HQL * @param name * @param id * @return */ @Query("Update User set name = ?1 WHERE id = ?2") @Modifying int updateNameAndId(String name, Integer id);
CrudReposiroty : 继承了Repository
/* * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.repository; import java.util.Optional; /** * Interface for generic CRUD operations on a repository for a specific type. * * @author Oliver Gierke * @author Eberhard Wolff */ @NoRepositoryBean public interface CrudRepository<T, ID> extends Repository<T, ID> { /** * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the * entity instance completely. * * @param entity must not be {@literal null}. * @return the saved entity will never be {@literal null}. */ <S extends T> S save(S entity); /** * Saves all given entities. * * @param entities must not be {@literal null}. * @return the saved entities will never be {@literal null}. * @throws IllegalArgumentException in case the given entity is {@literal null}. */ <S extends T> Iterable<S> saveAll(Iterable<S> entities); /** * Retrieves an entity by its id. * * @param id must not be {@literal null}. * @return the entity with the given id or {@literal Optional#empty()} if none found * @throws IllegalArgumentException if {@code id} is {@literal null}. */ Optional<T> findById(ID id); /** * Returns whether an entity with the given id exists. * * @param id must not be {@literal null}. * @return {@literal true} if an entity with the given id exists, {@literal false} otherwise. * @throws IllegalArgumentException if {@code id} is {@literal null}. */ boolean existsById(ID id); /** * Returns all instances of the type. * * @return all entities */ Iterable<T> findAll(); /** * Returns all instances of the type with the given IDs. * * @param ids * @return */ Iterable<T> findAllById(Iterable<ID> ids); /** * Returns the number of entities available. * * @return the number of entities */ long count(); /** * Deletes the entity with the given id. * * @param id must not be {@literal null}. * @throws IllegalArgumentException in case the given {@code id} is {@literal null} */ void deleteById(ID id); /** * Deletes a given entity. * * @param entity * @throws IllegalArgumentException in case the given entity is {@literal null}. */ void delete(T entity); /** * Deletes the given entities. * * @param entities * @throws IllegalArgumentException in case the given {@link Iterable} is {@literal null}. */ void deleteAll(Iterable<? extends T> entities); /** * Deletes all entities managed by the repository. */ void deleteAll(); }
Crud主要是添加了对数据的增删改查的方法
PagingAndSortingRepository: 继承了CrudRepository
/* * Copyright 2008-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.repository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; /** * Extension of {@link CrudRepository} to provide additional methods to retrieve entities using the pagination and * sorting abstraction. * * @author Oliver Gierke * @see Sort * @see Pageable * @see Page */ @NoRepositoryBean public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> { /** * Returns all entities sorted by the given options. * * @param sort * @return all entities sorted by the given options */ Iterable<T> findAll(Sort sort); /** * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object. * * @param pageable * @return a page of entities */ Page<T> findAll(Pageable pageable); }
/** * 继承了Repository,缺点只能对所有的数据进行排序或者分页 */ @Repository public interface UserPagingAndSortingReposiroty extends PagingAndSortingRepository<User, Integer> { }
package com.amber; import com.amber.pojo.User; import com.amber.repository.UserPagingAndSortingReposiroty; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; 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.test.context.junit4.SpringJUnit4ClassRunner; import java.util.List; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = JPAApplication.class) public class UserPagingAndSortingReposirotyTest { @Autowired UserPagingAndSortingReposiroty userPagingAndSortingReposiroty; /** * 排序 */ @Test public void TestSort(){ //定义排序规则 Sort.Order order = new Sort.Order(Sort.Direction.DESC, "name"); Sort.Order order1 = new Sort.Order(Sort.Direction.DESC, "id"); //Sort Sort sort = new Sort(order, order1); List<User> users = (List<User>)userPagingAndSortingReposiroty.findAll(sort); System.out.println(users); } /** * 分页Pageable封装了分页的参数,当前页,每一页显示的条数,注意当前页是从0开始的 */ @Test public void TestPaging(){ //Pageable是个接口 Pageable pageable = new PageRequest(0, 10); //返回Page对象 Page<User> uses = userPagingAndSortingReposiroty.findAll(pageable); System.out.println(uses.getTotalElements()); System.out.println(uses.getTotalPages()); System.out.println(uses.getNumberOfElements()); } /** * 分页 + 排序 */ @Test public void TestPagingAndSort(){ //定义排序规则 Sort.Order order = new Sort.Order(Sort.Direction.DESC, "name"); Sort.Order order1 = new Sort.Order(Sort.Direction.DESC, "id"); //Sort Sort sort = new Sort(order, order1); //Pageable是个接口 Pageable pageable = new PageRequest(0, 10, sort); //返回Page对象 Page<User> uses = userPagingAndSortingReposiroty.findAll(pageable); System.out.println(uses.getTotalElements()); System.out.println(uses.getTotalPages()); System.out.println(uses.getNumberOfElements()); } }
JPARepository: 继承了PagingAndSortingRepository接口
在开发中常用JPARepository
优点: 对继承父接口中方法的返回值进行了适配,因为在父类接口中通常都返回迭代器,需要我们自己进行强制类型转化。而在JpaRepository中,直接返回了List
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.data.jpa.repository; import java.util.List; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; @NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { List<T> findAll(); List<T> findAll(Sort var1); List<T> findAllById(Iterable<ID> var1); <S extends T> List<S> saveAll(Iterable<S> var1); void flush(); <S extends T> S saveAndFlush(S var1); void deleteInBatch(Iterable<T> var1); void deleteAllInBatch(); T getOne(ID var1); <S extends T> List<S> findAll(Example<S> var1); <S extends T> List<S> findAll(Example<S> var1, Sort var2); }
JpaSpecificationExecutor: 这个接口单独存在,没有继承以上说的接口
主要提供了多条件查询的支持,并且可以在查询中添加分页和排序。
因为这个接口单独存在,因此需要配合以上说的接口使用,如:
/** * JpaSpecificationExecutor是单独存在的,需要配合这JpaRepository一起使用 */ @Repository public interface UserJpaSpecificationExecutor extends JpaSpecificationExecutor<User>, JpaRepository<User, Integer> { }
package com.amber; import com.amber.pojo.User; import com.amber.repository.UserJpaSpecificationExecutor; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = JPAApplication.class) public class UserJpaSecificationExecutorTest { @Autowired UserJpaSpecificationExecutor userJpaSpecificationExecutor; /** * 多条件查询的另外一种写法 */ @Test public void testUser2(){ //Specification是个接口,封装了查询信息 Specification<User> specification = new Specification<User>() { /** * Predicate封装了单个查询条件 * @param root 对查询对象属性的封装,比如我们这里是查询User,因此root可以看成是User * @param query CriteriaQuery封装了查询中的各部分信息, Select from order * @param cb CB查询条件的构造器 * @return */ @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Predicate p1 = cb.equal(root.get("name"), "amber"); Predicate p2 = cb.equal(root.get("age"), "18"); return cb.and(p1,p2); } }; List<User> users = userJpaSpecificationExecutor.findAll(specification); System.out.println(users); } /** * 多条件查询 */ @Test public void testUser1(){ //Specification是个接口,封装了查询信息 Specification<User> specification = new Specification<User>() { /** * Predicate封装了单个查询条件 * @param root 对查询对象属性的封装,比如我们这里是查询User,因此root可以看成是User * @param query CriteriaQuery封装了查询中的各部分信息, Select from order * @param cb CB查询条件的构造器 * @return */ @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) { List<Predicate> predicates = new ArrayList<>(); Predicate p1 = cb.equal(root.get("name"), "amber"); Predicate p2 = cb.equal(root.get("age"), "18"); predicates.add(p1); predicates.add(p2); Predicate[] predicateArr = new Predicate[predicates.size()]; return cb.and(predicates.toArray(predicateArr)); } }; List<User> users = userJpaSpecificationExecutor.findAll(specification); System.out.println(users); } /** * 单条件查询 */ @Test public void testUser(){ //Specification是个接口,封装了查询信息 Specification<User> specification = new Specification<User>() { /** * Predicate封装了单个查询条件 * @param root 对查询对象属性的封装,比如我们这里是查询User,因此root可以看成是User * @param query CriteriaQuery封装了查询中的各部分信息, Select from order * @param cb CB查询条件的构造器 * @return */ @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Predicate p1 = cb.equal(root.get("name"), "amber"); return p1; } }; List<User> users = userJpaSpecificationExecutor.findAll(specification); System.out.println(users); } }
总结:
Spring Data Jpa中一共提供了
- Repository:
- 提供了findBy + 属性方法
- @Query
- HQL: nativeQuery 默认false
- SQL: nativeQuery 默认true
- 更新的时候,需要配合@Modifying使用
- CurdRepository:
- 继承了Repository 主要提供了对数据的增删改查
PagingAndSortRepository:
- 继承了CrudRepository 提供了对数据的分页和排序,缺点是只能对所有的数据进行分页或者排序,不能做条件判断
- 开发中经常使用的接口,主要继承了PagingAndSortRepository,对返回值类型做了适配
- JpaSpecificationExecutor
- 提供多条件查询