springboot+jpa查询(拼接条件查询)

最近在学习springboot使用jpa操作数据库,总结一下。
Dao层创建与JavaBean对应的接口,继承JpaRepository<K,E>接口

@Repository
public interface AccountDao extends JpaRepository<Account,Integer>{ }
//Account对应的是JavaBean实体类,Integer是实体类主键的类型

无需创建接口的实现类

个人学习到的jpa的几种查询方法:
1、jpa自带的方法:

使用的编程语言是Kotlin
//主要记录分页和排序
//service层
class AccountServiceImpl:AccountService{ 
	@Autewired
	lateinit var accountDao:AccountDao
	override fun queryOnLoad(apu: AccountPageUtil): Map<String, Any?> { 
		//创建排序对象
        val sort:Sort=Sort.by(Sort.Direction.ASC,"id")
        //创建分页对象
        val page:Pageable=PageRequest.of(apu.getStart(),apu.rows,sort)
        val map = hashMapOf<String,Any?>()
        //通过findAll方法进行分页查询
        var pageResult=accountDao.findAll(page)
        //findAll(Pageable)返回的是page对象,其中包含了分页所需要的总页数、总信息条数、每页中的对象等资源。可以查看源码获取所需要的信息
        //分页的结果
        map["rows"]=pageResult.content  
        //分页总数
        map["total"]=pageResult.totalPages
        return map
    }
}

2、jpa可以通过方法名进行查询

Dao层的接口
@Repository
interface SubjectDao:JpaRepository<Subject,Int>{ 
    /** * 根据Clazz查询 */
    fun findByClazz(clazz:String,pageable:Pageable):List<Subject>

    /** * 根据sid精确查询单个对象 */
    fun findBySid(id:Int):Subject
}    

推荐博客:http://www.ityouknow.com/springboot/2016/08/20/spring-boo-jpa.html

3、自定义SQL查询语句进行查询

Dao层的接口
@Repository
interface SubjectDao:JpaRepository<Subject,Int>{ 
	@Query(value="select sid as sid,sname as sname,slevel as slevel,clazz as clazz,pname as pname,pid as pid from Subject where sid like %?1%) ")
    fun myFindBySidLike(sid:Int,pageable: Pageable):List<Map<String,Any>>
}

在这里有几个注意点

  • @Query中有一个属性:nativeQuery = true 添加该属性等于true则是原生SQL语句查询,不添加则是HQL语句(Hibernate面向对象的查询)
  • like模糊查询时可在后面直接加%
  • 方法中给的参数类型必须和实体类中定义的类型一样,否则会报类型不一致的错
  • 方法的返回值好像只能返回List<Map<K,V>>类型(因为我只试过返回Page类型和List类型,都是类型不对)。Map中的Key就是查询语句中的别名,所以这里添加了别名。
  • 方法名不能是findBySidLike,因为这样他就不会执行自己写的SQL语句。

4、复杂的条件拼接查询
看到其他人的博客中说,Dao层接口再继承一个接口JpaSpecificationExecutor<实体类>

Dao层
@Repository
interface SubjectDao:JpaRepository<Subject,Int> , JpaSpecificationExecutor<Subject> { }
private Specification<Db1NewsFilesEntity> countByAppidAndKwAndSearchId(String appId, String wk , String searchId){ 
        return new Specification<Db1NewsFilesEntity>() { 
            @Override
            public Predicate toPredicate(Root<Db1NewsFilesEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { 
            	//创建存放Predicate对象的集合
                List<Predicate> list = new ArrayList<>();
               /*用于关联查询join方法的第二个参数可以设置 JoinType.LEFT、JoinType.RIGHT、JoinType.INNER*/
                Join<Db1NewsFilesEntity, Db1NewsItemsEntity> join = 
                	root.join("newsItem", JoinType.INNER);
                //以下为添加条件
                list.add(criteriaBuilder.equal(root.get("appId"),appId));
                list.add(criteriaBuilder.equal(root.get("img"),true));
                list.add(criteriaBuilder.equal(join.get("appId"),appId));
                list.add(criteriaBuilder.equal(join.get("isOk"),2));
                list.add(criteriaBuilder.isNotNull(join.get("topImgId")));
                //判断并拼接条件
                if(!StringUtils.isEmpty(searchId)){ 
                    list.add(criteriaBuilder.equal(join.get("searchId"),searchId));
                }
                if(!StringUtils.isEmpty(wk)){ 
                    list.add(criteriaBuilder.like(join.get("ItemTitle"),"%"+wk+"%"));
                }
                Predicate[] p = new Predicate[list.size()];
                query.where(criteriaBuilder.and(list.toArray(p)));
                return query.getRestriction();
            }
        };
    }
    //此段代码摘抄自网络:https://blog.csdn.net/qq587492/article/details/81629689

写上上面的代码我主要是想说说他的弊端,我原以为他是万能的,结果个人认为还是比较局限的。

  • 它是一种完全面向对象的。如果两个表之间没有明确的关联关系(在属性上标注OneToOne、OneToMany等)时,关联查询是不能实现的,肯定是类型对不上,等待着的就是各种报错。
  • 他不能实现自定义字段查询(投影查询),每次查询时会把所有的字段都差出来。
  • 他只能用在findAll方法上,使用自定义的方法,根本不回去拼接,而是去执行了自定义的方法。

所以我觉得稍微复杂一点的查询就要费事了。

于是继续众里寻他在百度,终于找到了你想怎样就怎样的方法。

  1. 在service层中放这么一个东西。
@PersistenceContext
   lateinit var em: EntityManager

java中是这么写的:

@PersistenceContext
private EntityManager em;

他有一些创建Query对象的方法,有了Query对象,就可以自己拼接条件查询了。《springboot+jpa查询(拼接条件查询)》
下面放上我使用它的一点代码:

/** * 自定义拼接条件查询 */
    fun predicateQuery(em: EntityManager,apu: AccountPageUtil): MyResult<ResultAccount> { 
        var baseSQL="select new com.sandrain.financial.entity.ResultAccount(a.id,a.createDate,a.proofNum,a.digest,a.come,a.go,a.balance,a.orientation,a.clazz,b.sname,c.sname) from Account a left outer join Subject b on a.subject=b.sid left outer join Subject c on a.sideSubject=c.sid where 1=1"
        var comditionalSQL=""
        var countSQL="select count(id) from Account a where 1=1"
        //map用来组装SQL占位符和对应的值
        var map= hashMapOf<String,Any>()
        if(!apu.startDate.equals("")&&!apu.endDate.equals("")){ 
            println("拼接条件查询:开始时间不为空,结束时间不为空")
            comditionalSQL+=" and (a.createDate between :startdate and :enddate)"
            map["startdate"]=apu.startDate
            map["enddate"]=apu.endDate
        }else if(!apu.startDate.equals("")&&apu.endDate.equals("")){ 
            println("拼接条件查询:开始时间不为空,结束时间为空")
            comditionalSQL+=" and (a.createDate between :startdate and :enddate)"
            map["startdate"]=apu.startDate
            map["enddate"]="${ LocalDate.now()}"//相当于Java中的""+LocalDate.now()
        }else if(apu.startDate.equals("")&&!apu.endDate.equals("")){ 
            println("拼接条件查询:开始时间为空,结束时间不为空")
            comditionalSQL+=" and (a.createDate between :startdate and :enddate)"
			//相当于java中的拼接字符串LocalDate.now().year+"-"+LocalDate.now().monthValue+"-01" 
            map["startdate"]="${ LocalDate.now().year}-${ LocalDate.now().monthValue}-01"
            map["enddate"]=apu.endDate
        }else{ 
            println("拼接条件查询:开始时间为空,结束时间为空")
            comditionalSQL+=" and (a.createDate between :startdate and :enddate)"
            map["startdate"]="${ LocalDate.now().year}-${ LocalDate.now().monthValue}-01"
            map["enddate"]="${ LocalDate.now()}"
        }
        if (apu.digest!=""){ 
            println("拼接条件查询:digest不为空")
            comditionalSQL+=" and a.digest=:digest"
           map["digest"]="${ apu.digest}"
        }
        if(apu.clazz!=""){ 
            println("拼接条件查询:clazz不为空")
            comditionalSQL+=" and a.clazz=:clazz"
            map["clazz"]="${ apu.clazz}"
        }
        if(apu.subject!=null) { 
            println("拼接条件查询:关联subject不为空")
            comditionalSQL+=" and a.subject=:subject"
            map["subject"]=apu.subject!!
        }
        //组装SQL
        var resSQL="${ baseSQL}${ comditionalSQL}"
        var countSQL="${ countSQL}${ comditionalSQL}"
        //创建查询对象
        /*这里是重点,将SQL放入方法中,创建一个查询对象,确切的说应该是HQL, 他接收的也是Hibernate查询语句(我写原生SQL报错)。重点是,SQL是自己写的, 这就灵活了很多。性能方面没有关注*/
        var res=em.createQuery(resSQL)
        var countRes=em.createQuery(countSQL)
        //创建封装结果集的对象
        var result=MyResult.of<ResultAccount>()
        //设置分页,注意,分页只能用这种方式,不能写在SQL中,否则报错
        res.setFirstResult(apu.getStart())
        res.setMaxResults(apu.rows)
        //添加参数,此处我用了kotlin的方式将占位符和参数一一对应放入Query对象中
        for ((index,ele) in map){ 
            res.setParameter(index,ele)
            countRes.setParameter(index,ele)
        }
        //组装结果集
        /* 此处注意的是:如果用普通是SQL样式查询,如:"select a,b,c from d where ..." 他返回的不是键值对形式对象,有些难以控制。 所以我在SQL中使用了Hibernate的投影查询方式: "select new com.sandrain.financial.entity.ResultAccount(a,b,c) from d where ..." com.sandrain.financial.entity.ResultAccount是我自定义的一个返回结果集的类, 这个类要有一个和查询的字段类型一一对应的构造方法。 那么返回的就是一个该对象的集合。当然需要强转一下 a as ResultAccount就是将a强转为ResultAccount */
        for (a in res.resultList){ 
            result.rows.add(a as ResultAccount)
        }
        //查询一个对象时使用singleResult()方法获得
        //查询一堆对象时用resultList()方法获得,
        //查询的数值返回的是伪Long型,所以先转为Long型,再转为Integer
        result.total=(countRes.singleResult as Long).toInt()
        return result
    }
//创建HQL语句
@PersistenceContext
 private EntityManager em;
String hql="select a,b from C where d=:e "// :e是占位符,也相当于给占位符起一个名字
//通过HQL语句创建query对象
Query query=em.createQuery(hql)
//给条件添加值,占位符不能使用?的形式
//query.setParameter(占位符,值)
query.setParameter("e",255)
//获取结果
List<C> list=query.getResultList
//遍历结果集组装想要的结果...

如有不对之处还请大神告知一二

    原文作者:沙雨
    原文地址: https://blog.csdn.net/weixin_41698864/article/details/84996052
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞