SpringDataJPA系列06自定义操作(JPQLSQL)
6、自定义操作(JPQL / SQL)
在我们经过了上面的学习,我们会发现一个问题:那就是我们所支持的就是一些简单的增删查改等等的操作,对于复杂的一些操作并不支持,所以我们也需要进行一些自定义,可以通过SQL或者 JPQL进行自定义操作!
自定义操作: 1、JPQL(原生SQL)@Query 查询如果返回单个实体,就使用pojo类进行接收即可,如果是多个就使用list进行接收! 参数设置方式 索引:?数字 具名::参数名 结合@Param注解指定参数名称 | 增删改: 要加上事务的支持: 如果是插入方法:一定只能在hibernate下才支持(Insert into … select) @Transactional // 开启事务!通常会放在业务逻辑层上去声明! @Modifying // 通知springdatajpa 是增删改的操作! 复制代码
测试代码如下: 创建repository/CustomerRepository package com.yykk.repositories; import com.yykk.pojo.Customer; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import java.util.List; public interface CustomerRepositories extends PagingAndSortingRepository{ // 使用JPQL实现增删改查 // 查询 //@Query("from Customer where custName=?1") @Query(value = "from Customer where custName=:custName") List findCustomerByCustName(@Param("custName") String custName); /** * 更新操作! * 在这里如果没有事务的支持,那么我们会报错!需要定义在业务逻辑层里面! * @Modifying // 通知springdatajpa 是增删改的操作! */ @Query("update Customer c set c.custName=:custName where c.id=:id") @Transactional // 开启事务! @Modifying // 通知springdatajpa 是增删改的操作! int updateCustomerById(@Param("custName") String custName,@Param("id") Long id); // 删除 @Query("delete from Customer c where c.id=?1") @Transactional // 开启事务! @Modifying // 通知springdatajpa 是增删改的操作! String deleteCustomer(Long id); // 新增 JPQL 默认是不支持的,但是这里是使用的伪插入,底层是hibernate! // 通知springdatajpa 是增删改的操作! //@Transactional // 开启事务! //@Modifying //@Query(value = "insert into Customer(custName) select cust_name from Customer where id=?1") //这里推荐使用其他方法insert方法不推荐使用!如果要使用可以使用原生的!这里没有values报错!可以尝试一下拼接! //int insertCustomerBySelect(Long id); // 原生SQL查询 // 在这个查询中写成custName之后就报错! @Query(value = "select * FROM tb_Customer where cust_name= ? " ,nativeQuery = true) List findCustomerByCustNameBySql(@Param("custName") String custName); } 复制代码测试! import com.yykk.config.SpringDataJPAConfig; import com.yykk.pojo.Customer; import com.yykk.repositories.CustomerRepositories; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.List; @ContextConfiguration(classes = SpringDataJPAConfig.class) @RunWith(SpringJUnit4ClassRunner.class) public class JPQLTest { @Autowired CustomerRepositories repositories; /** * 查询测试! * 因为可能会遇到返回的结果是多个相同的,就使用list接收! */ @Test public void testQuery() { List customer = repositories.findCustomerByCustName("yykk"); System.out.println(customer); } /** * 更新操作! * 在这里如果没有事务的支持,那么我们会报错!需要定义在业务逻辑层里面! */ @Test public void testUpdate() { int result = repositories.updateCustomerById("apple", 3L); System.out.println(result); } @Test public void testDelete() { String result = repositories.deleteCustomer( 9L); System.out.println(result); } //@Test //public void testInsert() { // int result = repositories.insertCustomerBySelect(1L); // System.out.println(result); //} @Test public void testQuery_sql() { List list = repositories.findCustomerByCustNameBySql("yykk"); System.out.println(list); } } 复制代码2、规定方法名只支持查询方法主题关键字(前缀)只有查询和删除!决定当前方法作用!查询主题关键字
关键字
描述
find…By 、`read…By 、get…By 、query..By 、search…By 、stream…By
通过查询方法通常返回存储库类型、Collection 或Streamable 子类型或结果包装器,例如:Page 、GeoResults 或任何其他特定于商店的结果包装器。可用于 findBy… ,findMyDomainTypeBy…
exists…By
存在投影,通常返回 boolean 结果
count…By
计数投影返回数字结果。
delete…By、remove…By
删除查询方法返回无结果( void )或删除计数。
…First… ,…Top…
将查询结果限制为第一个 结果。此关键字可以出现在主题的find (和其他关键字)和之间的任何位置by 。
…Distinct…
使用不同的查询仅返回唯一的结果。查询特定与商店的文档是否支持该功能。此关键字可以出现在主题的 find (和其他关键字)和之间的任意位置 by 。支持的查询方法谓词关键字和修饰符决定查询条件
Keyword
Sample
JPQL snippet
And
findByNameAndPwd
where name= ? and pwd =?
Or
findByNameOrSex
where name= ? or sex=?
Is,Equals
findById,findByIdEquals
where id= ?
Between
findByIdBetween
where id between ? and ?
LessThan
findByIdLessThan
where id < ?
LessThanEquals
findByIdLessThanEquals
where id <= ?
GreaterThan
findByIdGreaterThan
where id > ?
GreaterThanEquals
findByIdGreaterThanEquals
where id > = ?
After
findByIdAfter
where id > ?
Before
findByIdBefore
where id < ?
IsNull
findByNameIsNull
where name is null
isNotNull,NotNull
findByNameNotNull
where name is not null
Like
findByNameLike
where name like ?
NotLike
findByNameNotLike
where name not like ?
StartingWith
findByNameStartingWith
where name like ‘?%’
EndingWith
findByNameEndingWith
where name like ‘%?’
Containing
findByNameContaining
where name like ‘%?%’
OrderBy
findByIdOrderByXDesc
where id=? order by x desc
Not
findByNameNot
where name <> ?
In
findByIdIn(Collection<?> c)
where id in (?)
NotIn
findByIdNotIn(Collection<?> c)
where id not in (?)
TRUE
findByAaaTue
where aaa = true
FALSE
findByAaaFalse
where aaa = false
IgnoreCase
findByNameIgnoreCase
where UPPER(name)=UPPER(?)
top
findTop10
top 10/where ROWNUM <=10
Distinct
findDistinctByLastnameAndFirstname
select distinct … where x.lastname = ?1 and x.firstname = ?2
1、repository/CustomerMethodNameRepositories package com.yykk.repositories; import com.yykk.pojo.Customer; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.transaction.annotation.Transactional; import java.util.List; public interface CustomerMethodNameRepositories extends PagingAndSortingRepository{ List findByCustName(String custName); boolean existsByCustName(String custName); @Transactional @Modifying int deleteByid(Long id); List findByCustNameLike(String custName); } 复制代码
2、测试! import com.yykk.config.SpringDataJPAConfig; import com.yykk.pojo.Customer; import com.yykk.repositories.CustomerMethodNameRepositories; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.List; @ContextConfiguration(classes = SpringDataJPAConfig.class) @RunWith(SpringJUnit4ClassRunner.class) public class MethodName { @Autowired CustomerMethodNameRepositories repository; @Test public void list(){ List list = repository.findByCustName("yykk"); System.out.println(list); } @Test public void exists(){ boolean exists = repository.existsByCustName("yykk"); System.out.println(exists); } @Test public void delete(){ int del = repository.deleteByid(12L); System.out.println(del); } @Test public void like(){ List list = repository.findByCustNameLike("y%"); System.out.println(list); } } 复制代码
这里的都是静态的固定查询,对于动态的查询要根据以下的这几种方法!3、通过Query by Example只支持查询不支持嵌套或分组的属性约束,如firstname = ?0 or(firstname = ? 1 and lastname = ? 2)只支持字符串 start/contains/ends/regex 匹配和其他属性类型的精确匹配。
实现:
1、将Repository继承QueryByExampleExecutor package com.yykk.repositories; import com.yykk.pojo.Customer; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; public interface CustomerQBERepositories extends PagingAndSortingRepository , QueryByExampleExecutor { } 复制代码
2、测试代码! import com.yykk.config.SpringDataJPAConfig; import com.yykk.pojo.Customer; import com.yykk.repositories.CustomerQBERepositories; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration(classes = SpringDataJPAConfig.class) @RunWith(SpringJUnit4ClassRunner.class) public class QueryByExampleExecutor { @Autowired CustomerQBERepositories repository; /** * 简单实例:客户名称,客户地址动态查询! */ @Test public void list() { Customer customer = new Customer(); customer.setCustName("yykk"); customer.setCustAddress("上海"); // 通过 Example构造查询条件! Example example = Example.of(customer); Iterable all = repository.findAll(example); System.out.println(all); } /** * 通过匹配器,进行条件的限制! * 简单实例:客户名称,客户地址动态查询! */ @Test public void test() { Customer customer = new Customer(); customer.setCustName("kk"); customer.setCustAddress("HAI"); // 通过匹配器对条件行为进行设置! ExampleMatcher matcher = ExampleMatcher.matching() .withIgnorePaths("custName") // 设置忽略的属性! //.withIgnoreCase("cust_address") // 设置忽略大小写,默认不写就是全部属性的设置! //.withStringMatcher(ExampleMatcher.StringMatcher.ENDING); // 对字符串进行结尾匹配 .withMatcher("cust_address",m -> m.endsWith().ignoreCase(true)); // 针对单个条件进行设置,会使withIgnoreCase失效! //.withMatcher("custAddress", ExampleMatcher.GenericPropertyMatchers.endsWith()); // 通过 Example构造查询条件! Example example = Example.of(customer,matcher); Iterable list = repository.findAll(example); System.out.println(list); } } 复制代码4、通过Specifications
之前使用Query by Example只能针对字符串进行条件设置,那如果希望对所有类型支持,可以使用Specifcations
实现
1、继承接口 package com.yykk.repositories; import com.yykk.pojo.Customer; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.PagingAndSortingRepository; public interface CustomerSpecificationsRepositories extends PagingAndSortingRepository ,JpaSpecificationExecutor { } 复制代码root:查询哪个表(关联查询)= fromCriteriaQuery:查询哪些字段,排序是什么 = 组合(order by 、where)CriteriaBuilder:条件之间是什么关系,如何生成一个查询条件,每一个查询条件是什么类型(> between in …)= wherePredicate(Expression):每一条查询条件的详细描述
2、测试! import com.yykk.config.SpringDataJPAConfig; import com.yykk.pojo.Customer; import com.yykk.repositories.CustomerSpecificationsRepositories; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.StringUtils; import javax.persistence.EntityManager; import javax.persistence.criteria.*; import java.util.ArrayList; import java.util.List; @ContextConfiguration(classes = SpringDataJPAConfig.class) @RunWith(SpringJUnit4ClassRunner.class) public class SpecificationsTest { @Autowired CustomerSpecificationsRepositories repository; @Autowired EntityManager entityManager; @Test public void test_All(){ List customer = repository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { // root from Customer ,获取列 // CriteriaBuilder 设置各种条件 (< > in ...) // query 组合(order by ,where ) return null; } }); } /** * 查询客户范围(in) * id > 大于 * 地址:精确 */ @Test public void test_Coll(){ List customer = repository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { // root from Customer ,获取列 // CriteriaBuilder 设置各种条件 (< > in ...) // query 组合(order by ,where ) Path id = root.get("id"); Path custName = root.get("custName"); Path custAddress = root.get("custAddress"); // 参数1:为哪个字段设置条件 参数2:值 Predicate address = criteriaBuilder.equal(custAddress, "SHANGHAI"); Predicate ids = criteriaBuilder.greaterThan(id, 0L); CriteriaBuilder.In in = criteriaBuilder.in(custName); in.value("yykk").value("张三"); Predicate predicate = criteriaBuilder.and(address, ids,in); return predicate; } }); System.out.println(customer); } /** * 查询客户范围(in) * id > 大于 * 地址:精确 * 将这里的值设置为动态的! */ @Test public void test_dynamic(){ Customer params = new Customer(); params.setId(0L); params.setCustAddress("SHANGHAI"); params.setCustName("yykk,apple"); List customer = repository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { // root from Customer ,获取列 // CriteriaBuilder 设置各种条件 (< > in ...) // query 组合(order by ,where ) Path id = root.get("id"); Path custName = root.get("custName"); Path custAddress = root.get("custAddress"); // 参数1:为哪个字段设置条件 参数2:值 List list = new ArrayList<>(); if (StringUtils.isEmpty(params.getCustAddress())) { list.add(criteriaBuilder.equal(custAddress, "SHANGHAI")); } if (params.getId() > -1) { list.add(criteriaBuilder.greaterThan(id, 0L)); } if (StringUtils.isEmpty(params.getCustName())) { CriteriaBuilder.In in = criteriaBuilder.in(custName); in.value("yykk").value("张三"); list.add(in); } Predicate predicate = criteriaBuilder.and(list.toArray(new Predicate[list.size()])); return predicate; } }); System.out.println(customer); } /** * 查询客户范围(in) * id > 大于 * 地址:精确 * 将这里的值设置为动态的! */ @Test public void test_dynamicx(){ CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery