最近在学习springboot使用jpa操作数据库,总结一下。
Dao层创建与JavaBean对应的接口,继承JpaRepository
@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>>
}
在这里有几个注意点
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
写上上面的代码我主要是想说说他的弊端,我原以为他是万能的,结果个人认为还是比较局限的。
所以我觉得稍微复杂一点的查询就要费事了。
于是继续众里寻他在百度,终于找到了你想怎样就怎样的方法。
@PersistenceContext
lateinit var em: EntityManager
java中是这么写的:
@PersistenceContext
private EntityManager em;
他有一些创建Query对象的方法,有了Query对象,就可以自己拼接条件查询了。
下面放上我使用它的一点代码:
/**
* 自定义拼接条件查询
*/
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
//遍历结果集组装想要的结果...
如有不对之处还请大神告知一二