当前位置: 首页 > 工具软件 > Java2JPA > 使用案例 >

Java JPA多表+多条件+分页+精确、模糊匹配

常雅达
2023-12-01

个人生活随笔记录,随意参考,不足之处,多多指出哈~

1、jpa对于简单的sql处理确实方便,上面都不用写,dao层方法名拼接好就对了,命名规则自行百度。
2、之前做了一个需要分页、多表关联、多条件查询的需求,当时项目集成的是jpa,如果是其他比如mybatis、mybatis-plus这些那太好实现了,过于复杂如果plus不好拼,大不了直接xml写sql就行。
3、jpa的dao层一般是这样写的

public interface XXXRepository extends JpaRepository<XXXEntity, ID类型>, JpaSpecificationExecutor<XXXEntity> {
}

之所以继承JpaSpecificationExecutor这个接口类,是因为觉得方便,直接可以在service调用以下分页接口

Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);

也可以不继承,自己手动在XXXRepository 里面定义一个相同接口就行

4、分页需要用到Pageable类,多条件的时候要用到Specification类,说实话如果不用这个,在dao层定义的接口上也可以实现,但是sql量太大了,拼起来麻烦,而且改动的时候不方便,比如单个查询的时候

//nativeQuery = true 意思是执行原生sql,都是直接对应数据库字段名的
@Query(value = "select * from tableName where code like concat('%',?1,'%')",nativeQuery = true)
    List<Entity> findByCode(String code);

多条件查询的时候

@Query(value = "select * from tableName where if(?1 !='',code like concat('%',?1,'%'),1=1)",nativeQuery = true)
    List<Entity> findByCode(String code);

单纯举个例,上面这些写法也行,但是sql量太大的时候,用Specification类来拼装SQL比较明智

5、分页+多条件查询写法

//page是当前页码,size是当前页最大显示数量
//由于查询page是从0开始的,所以如果前端传过来页码是1,那么这里记得page-1
Pageable pageable = PageRequest.of(page,size);
//拼接sql
Specification<XXXEntity> specification = (root, query, criteriaBuilder)->{
		...
}
//执行sql
Page<XXXEntity> page = repository.findAll(specification,pageable);

//page内返回内容,挑几个比较关键的

//当前页码,返回的值需要+1(这里看个人业务场景,正常情况前端页面的页面都是从1开始,所以这里+1,也可以前端处理)
page.getPageable().getPageNumber();  
//每一页最大返回数量
page.getPageable().getPageSize();
//返回分页数量,就是总的数据可以分出来多少页
page.getTotalPages();
//返回所有数据的总量
page.getTotalElements();

Specification内部拼装sql的写法

假设场景:用户表关联角色表,关系存在他们的关联表里面,每一条关联都存上了user_id、role_id

假设类:用户 User.class,角色Role.class,用户角色关联 UserRole.class

 (root, query, criteriaBuilder)->{
         //Join这里先写,在下面说明一下这种写法
		Join<User,UserRole> userRole = root.join("userRole",JoinType.LEFT);
        Join<User,Role> role = userRole.join("role",JoinType.LEFT);
        if(条件判断){
       			//查询用户性别
        		//这里是拼接的精确匹配  and xxx=?
				list.add(criteriaBuilder.equal(root.get("userSex"),"条件值"));
		}
		 if(条件判断){
		 		//查询拥有某个角色名的用户,模糊匹配
        		//这里是拼接的模糊匹配  and xxx like ‘%’ ? ‘%’
				list.add(criteriaBuilder.like(role.get("name"),"%"+输入内容+"%"));
		}
		 if(条件判断){
       			//查询关键字段,比如搜索框输入内容可以匹配用户名、角色名,任意匹配一个就返回这种
				list.add(criteriaBuilder.or(
                        criteriaBuilder.like(root.get("userName"),"%"+输入内容+"%"),
                        criteriaBuilder.like(root.get("roleName"),"%"+输入内容+"%")));
		}

		//数据去重 ,这里如果用query.groupBy有可能导致返会page里面总数为数据表内的总数
		//具体详情大家可以去百度了解一下,是因为会首先查询一次不带条件时的sql统计出来的
		query.distinct(true);
		
		//数据排序,比如这里根据创建日期排
		Order order = criteriaBuilder.desc(root.get("createdTime"));
        query.orderBy(order);

         //这里完成最终的拼接,这里也可以自己根据实际条件判断一下,比如list可能无值的情况下
		 Predicate[] p = new Predicate[list.size()];
         return criteriaBuilder.and(list.toArray(p));
}

6、Join关联查询这里,如何拼接成 select * from user_table u left join role_table r on r.user_id = u.id
就可以按照这个写法

	Join<User,UserRole> userRole = root.join("userRole",JoinType.LEFT);
    Join<User,Role> role = userRole.join("role",JoinType.LEFT);

上面代码在模糊匹配角色名的时候,注意用的是 role.get("")而不是root.get(),
因为root代表的是主表,而role是在左连接之后的Role表

root.join里面的userRole、role,这里的命名是按照下面的实体类取的值
User.class,Role.class,UserRole.class

//User实体映射类里面,比如和角色是一对多的关系按照以下的写法就行,上面的userRole也就是取的这里的userRole。
 @OneToMany(targetEntity = UserRole.class)
    @JoinColumn(name = "user_id",referencedColumnName = "id")
    private Set<UserRole> userRole;

name=“user_id” 代表的是 UserRole.class里面的字段user_id,
referencedColumnName =‘id’ 是当前User类的字段。这个写法就表示 r.user_id = u.id 关联关系

jpa多表+多条件+分页查询 写法大概就这样,具体写法需要根据具体的业务场景扩展或者根据个人需要改动就行

 类似资料: