JPA的多表复杂查询
转 JPA的多表复杂查询:详细篇
原文链接: https://mp.weixin.qq.com/s/7J6ANppuiZJccIVN-h0T3Q
最近工作中由于要求只能用hibernate+jpa 与数据库进行交互,在简单查询中,jpa继承CrudRepository
栗子1:
以一个实体类User中的几个属性进行筛选。
名字
ID
手机号
这是一个单表的多条件复杂查询,由于是在几个属性中进行筛选,其中的属性的个数不知道有多少个,所以只需要利用Specification 查询就可以很方便的实现这个需求。 下面请看代码: 场景:页面上通过条件筛选,查询用户列表
这里有3个条件 在页面上我设置的id分别为searchName,searchId,searchMobile。 由于这个是user表 所以userRepository 继承JpaSpecificationExecutor接口,随后我创建了一个封装条件的类
public
class
PageParam
<
T
>
{
private
Integer
pageSize
=
10
;
private
Integer
pageNumber
=
1
;
private
String
searchName
;
private
String
searchMobile
;
private
String
searchId
;
}
由于我这个方法是直接分页的 所以pageNumber 和pageSize 也可以直接写入到这个类中,用于方便接收参数,主要是对下面3个参数的封装
Specification
<
T
>
specification
=
new
Specification
<
T
>()
{
@Override
public
Predicate
toPredicate
(
Root
<
T
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
cb
)
{
List
<
Predicate
>
list
=
new
ArrayList
<
Predicate
>();
if
(
StringUtils
.
isNotBlank
(
searchName
))
{
list
.
add
(
cb
.
like
(
root
.
get
(
"name"
).
as
(
String
.
class
),
"%"
+
searchName
+
"%"
));
}
if
(
StringUtils
.
isNotBlank
(
searchId
))
{
list
.
add
(
cb
.
equal
(
root
.
get
(
"id"
).
as
(
Long
.
class
),
searchId
));
}
if
(
StringUtils
.
isNotBlank
(
searchMobile
))
{
list
.
add
(
cb
.
like
(
root
.
get
(
"mobile"
).
as
(
String
.
class
),
"%"
+
searchMobile
+
"%"
));
}
Predicate
[]
p
=
new
Predicate
[
list
.
size
()];
return
cb
.
and
(
list
.
toArray
(
p
));
};
};
这里因为都是一个表,所以只要root.get('N ')这个N对应所要查的 属性的名字就好,属性名 属性名 重要的事情说三遍。
再接下来看一组多表的查询
栗子2:
这里有4张表
public
class
Living
{
Long
id
;
@ManyToOne
@JsonIgnore
@JoinColumn
(
name
=
"actorId"
,
foreignKey
=
@ForeignKey
(
name
=
"none"
,
value
=
ConstraintMode
.
NO_CONSTRAINT
))
public
Actor
actor
;
@ManyToOne
@JsonIgnore
@JoinColumn
(
name
=
"regionId"
,
foreignKey
=
@ForeignKey
(
name
=
"none"
,
value
=
ConstraintMode
.
NO_CONSTRAINT
))
public
Region
region
;
}
public
class
Actor
{
Long
id
;
@OneToMany
(
cascade
=
{
CascadeType
.
PERSIST
,
CascadeType
.
MERGE
,
CascadeType
.
REFRESH
},
fetch
=
FetchType
.
LAZY
)
@JoinColumn
(
name
=
"actorId"
)
@org
.
hibernate
.
annotations
.
ForeignKey
(
name
=
"none"
)
List
<
Living
>
livings
=
new
ArrayList
<>();
@OneToOne
(
cascade
=
{
CascadeType
.
PERSIST
,
CascadeType
.
MERGE
,
CascadeType
.
REFRESH
},
fetch
=
FetchType
.
LAZY
)
@org
.
hibernate
.
annotations
.
ForeignKey
(
name
=
"none"
)
@JoinColumn
(
name
=
"userDetailId"
,
foreignKey
=
@ForeignKey
(
name
=
"none"
,
value
=
ConstraintMode
.
NO_CONSTRAINT
))
UserDetail
userDetail
;
@Column
(
nullable
=
false
)
@Enumerated
(
value
=
EnumType
.
ORDINAL
)
ActorType
actorType
=
ActorType
.
A
;
public
enum
ActorType
{
A
,
B
,
C
}
}
public
class
UserDetail
{
Long
id
;
@OneToOne
(
cascade
=
{
CascadeType
.
PERSIST
,
CascadeType
.
MERGE
,
CascadeType
.
REFRESH
},
fetch
=
FetchType
.
LAZY
)
@org
.
hibernate
.
annotations
.
ForeignKey
(
name
=
"none"
)
@JoinColumn
(
name
=
"actorId"
,
foreignKey
=
@ForeignKey
(
name
=
"none"
,
value
=
ConstraintMode
.
NO_CONSTRAINT
))
Actor
actor
;
String
truename
;
}
public
class
Region
{
Long
id
;
String
name
;
@OneToMany
(
cascade
=
{
CascadeType
.
PERSIST
,
CascadeType
.
MERGE
,
CascadeType
.
REFRESH
},
fetch
=
FetchType
.
LAZY
)
@JoinColumn
(
name
=
"regionId"
)
@org
.
hibernate
.
annotations
.
ForeignKey
(
name
=
"none"
)
List
<
Living
>
Livings
;
}
现在要根据userdetai 种的 sex actor中的actortype 还有 region的id 为条件查询出满足条件的living。
public
class
PageParam
<
Living
>
{
private
Integer
pageSize
=
10
;
private
Integer
pageNumber
=
1
;
private
Sex
sex
;
private
ActorType
actortype
;
private
Long
cityid
;
}
首先我还是封装了这样一个类,但是这里的泛型 我是直接给到了想要的查询结果的泛型,接下来 因为这里涉及到了一个 多表的查询 所以上面的单表查询的例子 已经不适合这个查询了,但是Criteria 的join方法 给我们提供了一个模式
Specification
<
Living
>
specification
=
new
Specification
<
Living
>()
{
@Override
public
Predicate
toPredicate
(
Root
<
Living
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
cb
)
{
List
<
Predicate
>
list
=
new
ArrayList
<
Predicate
>();
if
(
null
!=
sex
)
{
Join
<
UserDetail
,
Living
>
join
=
root
.
join
(
"actor"
,
JoinType
.
LEFT
);
list
.
add
(
cb
.
equal
(
join
.
get
(
"userDetail"
).
get
(
"sex"
),
sex
));
}
if
(
null
!=
actortype
)
{
Join
<
Actor
,
Living
>
join
=
root
.
join
(
"actor"
,
JoinType
.
LEFT
);
list
.
add
(
cb
.
equal
(
join
.
get
(
"actorType"
),
actortype
));
}
if
(
null
!=
cityid
)
{
Join
<
Region
,
Living
>
join
=
root
.
join
(
"region"
,
JoinType
.
LEFT
);
list
.
add
(
cb
.
equal
(
join
.
get
(
"id"
),
cityid
));
}
//Join<A, B> join = root.join("bs", JoinType.LEFT);
//list.add(cb.equal(join.get("c").get("id"), id));
Predicate
[]
p
=
new
Predicate
[
list
.
size
()];
return
cb
.
and
(
list
.
toArray
(
p
));
};
};
这里是我对条件进行的封装。jpa 的多条件查询 主要是根据Criteria 为我们提供的方法封装条件,然后根据 给条件定义的位置,再生成sql语句,之后完成查询。 不得不说的地方,在这个多表的查询中以下面这句为例
Join
<
UserDetail
,
Living
>
join
=
root
.
join
(
"actor"
,
JoinType
.
LEFT
);
list
.
add
(
cb
.
equal
(
join
.
get
(
"userDetail"
).
get
(
"sex"
),
sex
));
jointype.LEFT
主要是说最终的这个属性 是在哪个表中, 而前面的 “actor” 则表示 从living表中 查询的 第一步的查询,比如我给出的例子 是要查询出 living 中的 actor 然后是actor 中的userdetail 之后才是 userdetail中的 sex属性 所以下面的join.get("userDetail").get("sex") ,这里就是get出相应的属性,一直到你得到想要的属性为止。 接下来的两个属性 也同理, 许多人多jpa 有很大的误解,认为jpa 的多表,多条件复杂查询,不如mybatis的查询,在之前我也是这么觉得,但自从通过jpa 实现了这个多表多条件的复杂查询之后,我觉得hibernate的复杂查询 不逊于mybatis ,尤其是对sql 语句不是很精通的码农,虽然hibernate的门槛较高可jpa 恰恰降低了hibernate 所需要的门槛,希望大家可以通过我的经验,更方便的与数据库进行交互。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性