一 :使用JPA提供的基础CRUD操作
1.继承JPA提供的JpaRepository<T, ID>接口
/**
* @Description
*/
@Repository
public interface KqGoodsRepository extends JpaRepository<KqGoods,Integer> {
}
JpaRepository<T, ID>接口提供了基本的crud操作,可以直接调用
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);
}
2.编写cotroller、service调用响应的方法,进行crud操作
controller层代码:
@GetMapping("/kqGoods/{number}")
@ResponseBody
public KqGoods getKqGoodsInformation(@PathVariable Integer number){
Optional<KqGoods> kqGoods = kqGoodsService.selectKqGoods(number);
if(kqGoods.isPresent()){
return kqGoods.get();
}else{
return null;
}
}
@GetMapping("/kqGoods")
@ResponseBody
public List<KqGoods> getKqGoodsInformation(){
return kqGoodsService.selectKqGoods();
}
@PostMapping("/kqGoods")
@ResponseBody
public KqGoods addKqGoodsInformation(KqGoods kqGoods){
return kqGoodsService.insertKqGoods(kqGoods);
}
@PutMapping("/kqGoods")
@ResponseBody
public KqGoods updateKqGoodsInformation(KqGoods kqGoods){
return kqGoodsService.updateKqGoods(kqGoods);
}
@DeleteMapping("/kqGoods/{number}")
@ResponseBody
public List<KqGoods> deleteKqGoodsInformation(@PathVariable Integer number){
kqGoodsService.deleteKqGoods(number);
return kqGoodsService.selectKqGoods();
}
Service层代码:
/**
* @Description
*/
public interface KqGoodsService {
KqGoods insertKqGoods(KqGoods kqGoods);
void deleteKqGoods(Integer number);
KqGoods updateKqGoods(KqGoods kqGoods);
List<KqGoods> selectKqGoods();
Optional<KqGoods> selectKqGoods(Integer number);
}
/**
* @Description
*/
@Service
public class KqGoodsServiceImpl implements KqGoodsService {
@Autowired
private KqGoodsRepository KqGoodsRepository;
@Override
public KqGoods insertKqGoods(KqGoods kqGoods) {
return kqGoodsRepository.save(kqGoods);
}
@Override
public void deleteKqGoods(Integer number) {
KqGoodsRepository.deleteById(number);
}
@Override
public KqGoods updateKqGoods(KqGoods kqGoods) {
return kqGoodsRepository.save(kqGoods);
}
@Override
public List<KqGoods> selectKqGoods() {
return kqGoodsRepository.findAll();
}
@Override
public Optional<KqGoods> selectKqGoods(Integer number) {
return kqGoodsRepository.findById(number);
}
}
二 :构建动态条件查询
JPA是支持动态查询的,需要我们的repository 继承JpaSpecificationExecutor接口,使用的时候传入相应参数即可。先看一下JpaSpecificationExecutor接口:
public interface JpaSpecificationExecutor<T> {
Optional<T> findOne(@Nullable Specification<T> var1);
List<T> findAll(@Nullable Specification<T> var1);
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
List<T> findAll(@Nullable Specification<T> var1, Sort var2);
long count(@Nullable Specification<T> var1);
}
可以看到JpaSpecificationExecutor提供了五个方法,它们都必须传入的参数Specification<T> var1就是我们用来构建动态条件查询的关键。 其中,Pageable是分页查询用的,Sort是排序用的,这里先介绍Specification的使用。
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}
这里的参数Root<T> root
负责实体类与数据表的映射 ,CriteriaBuilder cb
负责构建查询语句, riteriaQuery<?> query
负责执行查询语句,具体使用如下:
1.继承JpaSpecificationExecutor接口
@Repository
public interface KqGoodsRepository extends JpaRepository<KqGoods,Integer>, JpaSpecificationExecutor {
}
2.Service层加入动态条件查询方法的实现
service:
List<KqGoods> selectKqGoods(KqGoods kqGoods);
serviceImpl:
@Override
public List<KqGoods> selectKqGoods(KqGoods kqGoods) {
Specification<KqGoods> query = new Specification<KqGoods>() {
@Override
public Predicate toPredicate(Root<KqGoods> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if(kqGoods.getNumber()!=null){
predicates.add(criteriaBuilder.equal(root.get("number"),kqGoods.getNumber()));
}
if(StringUtils.isNotEmpty(kqGoods.getGender())){
predicates.add(criteriaBuilder.equal(root.get("gender"),kqGoods.getGender()));
}
if(StringUtils.isNotEmpty(kqGoods.getName())){
predicates.add(criteriaBuilder.like(root.get("name"),"%"+kqGoods.getName()+"%"));
}
if(StringUtils.isNotEmpty(kqGoods.getIdCard())){
predicates.add(criteriaBuilder.like(root.get("idCard"),"%"+kqGoods.getIdCard()+"%"));
}
if(StringUtils.isNotEmpty(kqGoods.getWorksName())){
predicates.add(criteriaBuilder.like(root.get("worksName"),"%"+kqGoods.getWorksName()+"%"));
}
if(StringUtils.isNotEmpty(kqGoods.getWorksType())){
predicates.add(criteriaBuilder.equal(root.get("worksType"),kqGoods.getWorksType()));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
return kqGoodsRepository.findAll(query);
}
其中Root是查询的根对象,CriteriaBuilder可以用来构建查询关系。
以predicates.add(criteriaBuilder.equal(root.get("number"),kqGoods.getNumber()))
为例,"number"
指定了实体Player的属性number,通过root.get("number")
获取实体属性number对应库表中的字段名称,kqGoods.getNumber()
获取当前传入的查询条件值,equal指的是等于,criteriaBuilder将他们构建为对应的sql语句。此段代码的含义是where条件,如:number='19001'。(指定的属性名必须与查询实体的属性名一致,否则会报错。)相同的道理,predicates.add(criteriaBuilder.like(root.get("name"),"%"+kqGoods.getName()+"%"))
即为模糊匹配。
最后return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]))
返回Specification的实例,然后作为参数传入到kqGoodsRepository.findAll(query)
中执行拼接好的动态条件查询。
当然criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]))
取的and关系,还可以使用or,道理都一样。
3.Controller调用方法,用PostMan测试
controller:
@GetMapping("/kqGoods")
@ResponseBody
public List<KqGoods> getKqGoodsInformation(KqGoods kqGoods){
return kqGoodsService.selectKqGoods(kqGoods);
}
三 :构建排序和分页查询
我们在上面的动态分页查询的基础上增加分页查询的功能,先放更改后的代码。
@Override
public List<KqGoods> selectPlayer(KqGoods kqGoods, Integer page, Integer size) {
Sort.Direction sort = Sort.Direction.ASC;
Pageable pageable = PageRequest.of(page-1, size, sort, "number");
Specification<KqGoods> query = new Specification<KqGoods>() {
@Override
public Predicate toPredicate(Root<KqGoods> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if(kqGoods.getNumber()!=null){
predicates.add(criteriaBuilder.equal(root.get("number"),kqGoods.getNumber()));
}
if(StringUtils.isNotEmpty(kqGoods.getName())){
predicates.add(criteriaBuilder.like(root.get("name"),"%"+kqGoods.getName()+"%"));
}
if(StringUtils.isNotEmpty(kqGoods.getIdCard())){
predicates.add(criteriaBuilder.like(root.get("idCard"),"%"+kqGoods.getIdCard()+"%"));
}
if(StringUtils.isNotEmpty(kqGoods.getWorksName())){
predicates.add(criteriaBuilder.like(root.get("worksName"),"%"+kqGoods.getWorksName()+"%"));
}
if(StringUtils.isNotEmpty(kqGoods.getWorksType())){
predicates.add(criteriaBuilder.equal(root.get("worksType"),kqGoods.getWorksType()));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
return kqGoodsRepository.findAll(query,pageable).getContent();
}
我们使用的是JpaSpecificationExecutor提供的方法:
public interface JpaSpecificationExecutor<T> {
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
}
Sort.Direction
是个枚举有ASC(升序)和DESC(降序),Pageable pageable = PageRequest.of(page-1, size, sort, "number");
获取PageRequest对象,page页码从0开始,size页容量,sort排序方式,以number为准进行排序。
Page接口如下:
public interface Page<T> extends Iterable<T> {
int getNumber(); //当前第几页 返回当前页的数目。总是非负的
int getSize(); //返回当前页面的大小。
int getTotalPages(); //返回分页总数。
int getNumberOfElements(); //返回当前页上的元素数。
long getTotalElements(); //返回元素总数。
boolean hasPreviousPage(); //返回如果有上一页。
boolean isFirstPage(); //返回当前页是否为第一页。
boolean hasNextPage(); //返回如果有下一页。
boolean isLastPage(); //返回当前页是否为最后一页。
Iterator<T> iterator();
List<T> getContent(); //将所有数据返回为List
boolean hasContent(); //返回数据是否有内容。
Sort getSort(); //返回页的排序参数。
}
PS:实现多条件排序
Sort sort = new Sort(Sort.Direction.DESC, "createdate")
.and(new Sort(Sort.Direction.AES, "id"));
Pageable pageable = new PageRequest(1, 10, sort)
四、使用@Query使用自定义的SQL语句
使用@Query
注解,我们需要先了解它的参数:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@QueryAnnotation
@Documented
public @interface Query {
String value() default "";
String countQuery() default "";
String countProjection() default "";
boolean nativeQuery() default false;
String name() default "";
String countName() default "";
}
其中:value
指定JPQL语句,当nativeQuery=true
时是原生的sql语句countQuery
指定count的JPQL语句,不指定则自动生成,当nativeQuery=true
时是原生的sql语句,countQuery
主要用来配合 value
实现分页功能countProjection
依据哪个字段来count一般默认即可nativeQuery
默认是false,表示value 里面是不是原生的Sql 语句name
指定一个query 的名字,必须是唯一的。 如果不指定,默认的生成规则是:{$domainClass}.${queryMethodName}
countName
指定一个count 的query 名字,必须是唯一的。如果不指定,默认的生成规则是:{$domainClass}.${queryMethodName}.count
这里主要介绍常用的value
和nativeQuery
的使用。
1.一个使用@Query的简单例子(?n
引入参数,参数顺序不可调换)
@Query(value = "select k from KqGoods K where K.worksType = ?1 and K.age <= ?2 and K.gender = ?3")
List<kqGoods> findByCondition(String worksType, Integer age, String gender);
@Query(value = "select k.* from kqGoods k where k.works_type = ?1 and k.age <= ?2 and k.gender = ?3",nativeQuery = true)
List<KqGoods> findByCondition(String worksType, Integer age, String gender);
以上是@Query
的两种写法
第一种nativeQuery = false
,使用基于实体的查询方式,from后面跟的是实体名,where后面的p.worksType
对应的实体的字段;
第二种nativeQuery = true
,使用基于数据表的查询方式,from后面跟的是表名,where后面的p.works_type
对应的数据表中的列名;
2.Like表达式使用 (@Param
注解注入参数,:
引入参数,需要指定参数名)
@Query(value = "select k from kqGoods k where k.worksName like %:worksName% and k.gender = :gender")
List<kqGoods> findByCondition(@Param("worksName") String worksName, @Param("gender")String gender);
3.修改语句的自定义使用@Query
和@Modifying
同时使用用来自定义数据更改操作的SQL,@Transactional
负责提交事务
@Transactional
@Query(value = "update KqGoods k set k.worksName = '拍摄的照片' where k.worksType =?1")
@Modifying
Integer updateByCondition(String worksType);
五、自定义返回实体
我们这里示例返回一个自定义的实体类Student
1.定义需要返回的实体(需要getset方法和全参构造器,这里使用lombok的注解实现);
@Data
@AllArgsConstructor
public class Student {
private String name;
private String sex;
private Integer age;
private String school;
private String speciality;
}
2.编写Repository
/**
* 返回自定义实体
* @return
*/
@Query(value = "select new com.example.bootstraptable.entity.Student(k.name,k.gender,k.age,k.school,k.speciality) from KqGoods k")
List<Student> getStudents();
使用基于实体的查询来实现返回自定义实体
其中new com.example.bootstraptable.entity.Student(k.name,k.gender,k.age,k.school,k.speciality)
为Student
的全参构造函数,使用com.example.bootstraptable.entity.Student
引入实体类,相较于直接new Student()
,使用包名引入更能准确的找到对应的返回实体。
七 :自定义语句实现分页查询
1.JPA持久层 InvoiceRepository.java
@Repository
public interface InvoiceRepository extends JpaRepository<Invoice, Integer> {
@Query(
value =
"SELECT * from invoice_apply where company_id=?1 and IF (?2 is null ,1=1,status = ?2)",
countQuery =
"select count(*) from invoice_apply where company_id=?1 and IF (?2 is null ,1=1,status = ?2)",
nativeQuery = true)
Page<Map> findInvoice(int companyID, String status, Pageable pageable);
}
2.服务层
@Override
public List findInvoice(int companyID, String status, Integer page, Integer pageSize) {
Double amount = companyFinanceRepository.findDCompanyFinance(companyID);
//分页查询
Pageable pageable = PageRequest.of(page, pageSize, Sort.Direction.ASC, "id");
Page<Map> invoiceList = invoiceRepository.findInvoice(companyID, status, pageable);
return invoiceList.getContent();
}