目录
一、Repository接口
1、Repository是一个空接口,即:是一个标记接口,表示任何继承它的接口都是仓库接口类。
2、若我们继承了Repository,则该接口会被Ioc容器表示为一个Repository Bean,放入到IOC容器中,进而可以在该接口中定义满足一定规范的方法。
3、实际上也可以通过@RepositoryDefinition(domainClass = Person.class, idClass = Long.class)来替代继承Repository接口
用法
继承:第一个参数是操作时对应的实体类的类名,第二个参数是主键的类型:
注解:还有一个方法是和继承Repository接口是一样的,那就是用@RepositoryDefinition注解效果都是一样的
1.命名规则
然后可以在此接口编写对数据库操作的方法(在接口中是静态方法,没有方法体的,但是SpringData有个方法的命名规则,可以直接采用命名规则的方式来实现操作,因为SpringData的底层已经帮你写好sql语句,不需要再次写sql语句)
详细规则请参考官方文档:命名规则
2.注解
(1)@Query
执行自定义的JPQL语句以更灵活的查询
// 查询id最大的那个person
@Query("from Person p1 where p1.id = (select max(p2.id) from Person p2)")
Person getPersonByMaxId();
a.使用占位符:参数顺序必须和JPQL中的顺序一致
@Query("from Person p where p.name = ?1 and p.gender = ?2")
Person query_getPersonByNameAndGender(String name, String gender);
b.使用命名参数:这种写法可以随便设置参数顺序
@Query("from Person p where p.name = :name and p.age = :age")
Person query_getPersonByNameAndAge(@Param("age") Integer age, @Param("name") String name);
c.spring data允许在占位符上添加%(适用于模糊查询like:%占位符%)
d.@Query支持原生sql查询
@Query(value = "select count(1) from t_person", nativeQuery = true)
Long getTotal();
- 这里的
nativeQuery=true
代表在执行这个方法的时候执行原生sql语句,直接写数据库中的实际表名
和表中的实际字段名。
如果不写的话那么需要使用实体类去声明sql,实体类对应表,实体类中的属性名对应表中的字段名。
(2)@Modifying:可以使用该注解来实现通过JPQL修改和删除(JPQL不支持添加 )
@Modifying
@Query(value = "update Person p set p.name = ?1, p.age = ?2 where p.id = ?3")
void updatePerson(String name, Integer age, Long id);
- 注意:更新和删除操作需要事务支持
- 在@Query中编写JPQL语句,但是必须添加@Modifying进行修饰来告诉JPA这是修改的操作
- 为什么@Query可以执行呢?:所有的
Repository
方法都有一个事务,但是却是只读事务
二、CrudRepository接口
继承Repository,实现一组CURD相关的方法
功能
- save(entity):添加一条数据
- save(entities):添加多条数据entities为集合
- findOne(id):根据id查询一条数据
- exists(id):判断id是否存在
- findAll():查询全部数据
- delete(id):根据id删除数据
- delete(entity):根据一条数据的信息删除数据
- delete(entities):根据多条数据的信息删除数据
- deleteAll():删除全部信息
三、JpaRepository接口
功能
继承PagingAndSortingRespository,实现了一组JPA规范相关的方法。JpaRepository
接口同时拥有基本CRUD
功能以及分页功能
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);
当我们需要定义自己的Repository
接口的时候,我们可以直接继承JpaRepository
,从而获得SpringBoot Data JPA
为我们内置的多种基本数据操作方法,例如
/**
* SpringDataJPA 实现JpaRepository
* 泛型 第一个参数是对应的Pojo类型
* 第二个参数是注解的包装类型
*/
public interface UserDao extends JpaRepository<Users,Integer> {
}
用法
/**
* @program: spring-data-jpa
* @description: 单元测试
* @author: 波波烤鸭
* @create: 2019-05-18 09:48
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestDemo {
@Autowired
private UserDao usersDao;
/**
* 添加用户
*/
@Test
@Transactional// 在测试类对于事务提交方式默认的是回滚。
@Rollback(false)//取消自动回滚
public void testInsertUsers(){
Users users = new Users();
users.setUserage(20);
users.setUsername("张三-jpa");
this.usersDao.save(users);
}
}
四、JpaSpecificationExecutor接口
功能
不属于Repository体系,实现一组 JPA Criteria 查询相关的方法,JpaSpecificationExecutor接口用于动态查询。有时我们在查询某个实体的时候,给定的条件是不固定的
,这时就需要动态构建相应的查询语句
,在Spring Data JPA中可以通过JpaSpecificationExecutor
接口查询。相比JPQL,其优势是类型安全,更加的面向对象
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
/**
* JpaSpecificationExecutor中定义的方法
**/
public interface JpaSpecificationExecutor<T> {
//根据条件查询一个对象
T findOne(Specification<T> spec);
//根据条件查询集合
List<T> findAll(Specification<T> spec);
//根据条件分页查询
Page<T> findAll(Specification<T> spec, Pageable pageable);
//排序查询查询
List<T> findAll(Specification<T> spec, Sort sort);
//统计查询
long count(Specification<T> spec);
}
- 但JpaSpecificationExecutor接口不能够单独使用,需要和其他接口一块使用 ,如下
/**
* JpaSpecificationExecutor 接口讲解
* @author Administrator
*注意:JpaSpecificationExecutor<Users>:不能单独使用,需要配合着 jpa 中的其他接口一起使用
*/
public interface CustomerDao extends JpaRepository<Customer, Integer>
, JpaSpecificationExecutor<Customer> {
}
Specification
对于JpaSpecificationExecutor,这个接口基本是围绕着Specification
接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。
Specification接口中只定义了如下一个方法
//构造查询条件
/**
* root :Root接口,代表查询的根对象,可以通过root获取实体中的属性
* query :代表一个顶层查询对象,用来自定义查询
* cb :用来构建查询,此对象里有很多条件方法
**/
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
用法
1.一个条件
public void testSpec() {
//匿名内部类
/**
* 自定义查询条件
* 1.实现Specification接口(提供泛型:查询的对象类型)
* 2.实现toPredicate方法(构造查询条件)
* 3.需要借助方法参数中的两个参数(
* root:获取需要查询的对象属性
* CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
* )
* 案例:根据客户名称查询,查询客户名为传智播客的客户
* 查询条件
* 1.查询方式
* cb对象
* 2.比较的属性名称
* root对象
*
*/
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//1.获取比较的属性
Path<Object> custId = root.get("custId");//查询的式属性名,不是表的字段名
//2.构造查询条件 : select * from cst_customer where cust_id = 3
/**
* 第一个参数:需要比较的属性(path对象)
* 第二个参数:当前需要比较的取值
*/
Predicate predicate = cb.equal(custId, 3);//进行精准的匹配 (比较的属性,比较的属性的取值)
return predicate;
}
};
Optional<Customer> customer = customerDao.findOne(spec);//Optional 是一个容器对象,可以存储对象、字符串等值,当然也可以存储 null 值
System.out.println(customer.get());
}
2. 多条件拼接
public void testSpec1() {
/**
* root:获取属性
* 客户名
* 所属行业
* cb:构造查询
* 1.构造客户名的精准匹配查询
* 2.构造所属行业的精准匹配查询
* 3.将以上两个查询联系起来
*/
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Object> custName = root.get("custName");//客户名
Path<Object> custAddress = root.get("custAddress");//地址
//构造查询
//1.构造客户名的精准匹配查询
Predicate p1 = cb.equal(custName, "黑马程序员");//第一个参数,path(属性),第二个参数,属性的取值
//2..构造所属行业的精准匹配查询
Predicate p2 = cb.equal(custAddress, "hangz");
//3.将多个查询条件组合到一起:组合(满足条件一并且满足条件二:与关系,满足条件一或满足条件二即可:或关系)
Predicate and = cb.and(p1, p2);//以与的形式拼接多个查询条件
// cb.or();//以或的形式拼接多个查询条件
return and;
}
};
Optional<Customer> customer = customerDao.findOne(spec);
System.out.println(customer);
}
3.模糊匹配
equal
:直接
的到path对象(属性),然后进行比较即可gt,lt,ge,le,like
: 得到path对象,根据path指定
比较的参数类型,再去进行比较
指定参数类型:path.as(类型的字节码对象)
public void testSpec3() {
//构造查询条件
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//查询属性:客户名
Path<Object> custName = root.get("custName");
//查询方式:模糊匹配
Predicate like = cb.like(custName.as(String.class), "d%");
return like;
}
};
List<Customer> list = customerDao.findAll(spec);
for (Customer customer : list) {
System.out.println(customer);
}
}
4.排序
public void testSpec3() {
//构造查询条件
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//查询属性:客户名
Path<Object> custName = root.get("custName");
//查询方式:模糊匹配
Predicate like = cb.like(custName.as(String.class), "d%");
return like;
}
};
Sort sort = new Sort(Sort.Direction.DESC,"custId");
List<Customer> list = customerDao.findAll(spec, sort);
for (Customer customer : list) {
System.out.println(customer);
}
}
- 其中Sort类常用的构造方法如下:
//1)通过Sort.Order对象创建Sort对象,适合对单一属性做排序
public Sort(Order... orders) {
this(Arrays.asList(orders));
}
//2)通过Sort.Order对象的List集合创建Sort对象,适合所有情况,比较容易设置排序方式
public Sort(List<Order> orders) {
Assert.notNull(orders, "Orders must not be null!");
this.orders = Collections.unmodifiableList(orders);
}
//3)直接创建Sort对象,适合对单一属性做排序
public Sort(String... properties) {
this(DEFAULT_DIRECTION, properties);
}
public Sort(Direction direction, String... properties) {
this(direction, properties == null ? new ArrayList<>() : Arrays.asList(properties));
}
//4)通过属性的List集合创建Sort对象,适合对多个属性,采取同一种排序方式的排序
public Sort(Direction direction, List<String> properties) {
if (properties == null || properties.isEmpty()) {
throw new IllegalArgumentException("You have to provide at least one property to sort by!");
}
this.orders = new ArrayList<>(properties.size());
for (String property : properties) {
this.orders.add(new Order(direction, property));
}
}
(1)Direction是用来标识列属性升序还是降序排序的
(2)properties即为列属性
- Order的构造方法:
public Order (Direction direction,String properties); --------Order维护一个Direction和一个列属性
@RequestMapping("/sort")
public List<Person> sort(){
List<Sort.Order> orders=new ArrayList<>();
orders.add(new Sort.Order(Sort.Direction.DESC,"age")); ---age降序
orders.add(new Sort.Order(Sort.Direction.ASC,"name")); ---naem升序
return personRepository.findAll(Sort.by(orders));
}
详细链接:
【知识库】-简单理解CrudRepository接口中的方法 - Cynical丶Gary - 博客园 (cnblogs.com)
JPA(三)Specifications查询 - 简书 (jianshu.com)
(107条消息) Spring Data JPA 之 JpaRepository_西瓜游侠的博客-CSDN博客_jparepository
(116条消息) JPA学习笔记之接口JpaRepository 和 JpaSpecificationExecutor的简单使用_EmonX的博客-CSDN博客_jparepository接口
(107条消息) Spring Data JPA使用Sort进行排序(Using Sort)_apple125414的博客-CSDN博客_jpa sort使用