介绍
在本文中,我们将了解如何将现有应用程序迁移到Spring 6以及如何充分利用此升级。
本文中的提示基于我在Hypersistence Optimizer和高性能 Java Persistence 项目中添加对 Spring 6 的支持所做的工作。
爪哇 17
首先,Spring 6 将最低 Java 版本提升到 17 个,这太棒了,因为你现在可以使用文本块和记录。
文本块
多亏了文本块,您的注释将更具可读性:@Query
1
2
3
4
5
6
7
8
9
10
|
@Query ( "" "
select p
from Post p
left join fetch p.comments
where p.id between :minId and :maxId
"" ")
List<Post> findAllWithComments(
@Param ( "minId" ) long minId,
@Param ( "maxId" ) long maxId
);
|
有关 Java 文本块的更多详细信息,请查看本文。
记录
Java Records 非常适合 DTO 投影。例如,你可以像这样定义 aclass:PostRecord
1
2
3
4
5
|
public record PostCommentRecord(
Long id,
String title,
String review
) {}
|
然后,您可以使用 Spring Data JPA 查询方法获取对象:PostCommentRecord
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Query ( "" "
select new PostCommentRecord(
p.id as id,
p.title as title,
c.review as review
)
from PostComment c
join c.post p
where p.title like :postTitle
order by c.id
"" ")
List<PostCommentRecord> findCommentRecordByTitle(
@Param ( "postTitle" ) String postTitle
);
|
我们之所以可以在 JPQL 构造函数表达式中使用 theJava 的简单名称,是因为我从Hibernate Type 项目中注册了以下内容:PostCommentRecord
Class
ClassImportIntegrator
1
2
3
4
5
6
7
8
9
10
|
properties.put(
"hibernate.integrator_provider" ,
(IntegratorProvider) () -> Collections.singletonList(
new ClassImportIntegrator(
List.of(
PostCommentRecord. class
)
)
)
);
|
有关 Java 记录的更多详细信息,请查看本文。
这还不是全部!Java 17 改进了 forand 的错误消息,并添加了模式匹配 forand。NullPointerException
switch
instanceOf
JPA 3.1
默认情况下,Spring 6 使用 Hibernate 6.1,而 Hibernate 6.1 又使用 Jakarta Persistence 3.1。
现在,3.0版本标志着从Java持久性到Jakarta Patersistence的迁移,因此,出于这个原因,您必须将软件包导入替换为命名空间。javax.persistence
jakarta.persistence
这是迁移到 JPA 3 必须进行的最重要的更改。与此同时,发布了3.1版本,但这个版本只包括Hibernate已经支持的一些小改进。
UUID 实体属性
例如,JPA 3 现在支持基本类型:UUID
1
2
3
4
5
|
@Column (
name = "external_id" ,
columnDefinition = "UUID NOT NULL"
)
private UUID externalId;
|
您甚至可以将它们用作实体标识符:
1
2
3
|
@Id
@GeneratedValue (strategy = GenerationType.UUID)
private UUID id;
|
但这只是一个糟糕的主意,因为使用 anfor 主键会导致很多问题:UUID
- 索引页将很少填充,因为每个新的 UUID 都将在 B+树聚集索引中随机添加。
- 由于主键值的随机性,将会有更多的页面拆分
- UUID 很大,需要的字节数是列的两倍。它不仅影响主键,还影响所有关联的外键。
bigint
此外,如果您使用的是SQL Server,MySQL或MariaDB,则默认表将被组织为聚簇索引,从而使所有这些问题变得更糟。
因此,最好避免使用 for 实体标识符。如果您确实需要从应用程序生成唯一标识符,那么最好使用64 位时间排序的随机 TSID。UUID
新的 JPQL 函数
JPQL 通过许多新功能得到了增强,例如,,,,,,数字函数。CEILING
FLOOR
EXP
LN
POWER
ROUND
SIGN
但是,我发现最有用的是日期/时间函数:EXTRACT
1
2
3
4
5
6
7
|
List<Post> posts = entityManager.createQuery( "" "
select p
from Post p
where EXTRACT(YEAR FROM createdOn) = :year
"" ", Post. class )
.setParameter( "year" , Year.now().getValue())
.getResultList();
|
这很有用,因为日期/时间处理通常需要特定于数据库的函数,并且拥有一个可以呈现适当的特定于数据库的函数的泛型函数肯定很方便。
可自动关闭的实体管理器和实体管理器工厂
虽然Hibernateand已经扩展了接口,但现在JPAand也遵循了这种做法:Session
SessionFactory
AutoClosable
EntityManager
EntityManagerFactory
Although you might rarely need to rely on that because Spring takes care of the on your behalf, it’s very handy when you have to process the programmatically.EntityManager
EntityManager
冬眠 6
虽然Java 17和JPA 3.1为您带来了一些功能,但Hibernate 6提供了大量的增强功能。
JDBC 优化
以前,Hibernate使用关联的列别名读取JDBC列值,这被证明很慢。出于这个原因,Hibernate 6 已切换到按基础 SQL 投影中的位置读取基础列值。ResultSet
除了速度更快之外,进行此更改还有一个非常好的副作用。基础 SQL 查询现在更具可读性。
例如,如果您在 Hibernate 5 上运行此 JPQL 查询:
1
2
3
4
5
6
7
8
|
Post post = entityManager.createQuery( "" "
select p
from Post p
join fetch p.comments
where p.id = :id
"" ", Post. class )
.setParameter( "id" , 1L)
.getSingleResult();
|
将执行以下 SQL 查询:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
SELECT
bidirectio0_.id AS id1_0_0_,
comments1_.id AS id1_1_1_,
bidirectio0_.title AS title2_0_0_,
comments1_.post_id AS post_id3_1_1_,
comments1_.review AS review2_1_1_,
comments1_.post_id AS post_id3_1_0__,
comments1_.id AS id1_1_0__
FROM post
bidirectio0_
INNER JOIN
post_comment comments1_ ON bidirectio0_.id=comments1_.post_id
WHERE
bidirectio0_.id=1
|
丑!
如果您在Hibernate 6上运行相同的JPQL,将如何改为运行以下SQL查询:
1
2
3
4
5
6
7
8
9
10
11
12
|
SELECT
p1_0.id,
c1_0.post_id,
c1_0.id,
c1_0.review,
p1_0.title
FROM
post p1_0
JOIN
post_comment c1_0 ON p1_0.id=c1_0.post_id
WHERE
p1_0.id = 1
|
好多了,对吧?
语义查询模型和条件查询
Hibernate 6提供了一个全新的实体查询解析器,能够从JPQL和Criteria API生成规范模型,即语义查询模型。
通过统一实体查询模型,现在可以使用 Jakarta 持久性不支持的功能(如派生表或公用表表达式)来增强条件查询。
有关 Hibernate 语义查询模型的更多详细信息,请查看本文。
旧的休眠标准已被删除,但标准 API 已通过 提供许多新功能得到增强。HibernateCriteriaBuilder
例如,您可以使用函数进行不区分大小写的 LIKE 匹配:ilike
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
HibernateCriteriaBuilder builder = entityManager
.unwrap(Session. class )
.getCriteriaBuilder();
CriteriaQuery<Post> criteria = builder.createQuery(Post. class );
Root<Post> post = criteria.from(Post. class );
ParameterExpression<String> parameterExpression = builder
.parameter(String. class );
List<Post> posts = entityManager.createQuery(
criteria
.where(
builder.ilike(
post.get(Post_.TITLE),
parameterExpression)
)
.orderBy(
builder.asc(
post.get(Post_.ID)
)
)
)
.setParameter(parameterExpression, titlePattern)
.setMaxResults(maxCount)
.getResultList();
|
但是,这只是一个基本示例。使用新的,您现在可以渲染:HibernateCriteriaBuilder
方言增强功能
在Hibernate 5中,您必须根据底层数据库版本选择大量版本,这在Hibernate 6中得到了极大的简化:Dialect
此外,您甚至不需要在 Spring 配置中提供,因为它可以从 JDBC 解析。Dialect
DatabaseMetaData
有关此主题的更多详细信息,请查看此文章。
自动重复数据删除
您还记得在使用时为实体重复数据删除提供关键字是多么烦人吗?DISTINCT
JOIN FETCH
1
2
3
4
5
6
7
8
9
|
List<Post> posts = entityManager.createQuery( "" "
select distinct p
from Post p
left join fetch p.comments
where p.title = :title
"" ", Post. class )
.setParameter( "title" , "High-Performance Java Persistence" )
.setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false )
.getResultList();
|
如果你忘记发送提示,那么Hibernate 5会将关键字传递给SQL查询,并导致执行计划运行一些额外的步骤,这些步骤只会让你的查询变慢:PASS_DISTINCT_THROUGH
DISTINCT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
Unique
(cost= 23.71 .. 23.72 rows= 1 width= 1068 )
(actual time= 0.131 .. 0.132 rows= 2 loops= 1 )
-> Sort
(cost= 23.71 .. 23.71 rows= 1 width= 1068 )
(actual time= 0.131 .. 0.131 rows= 2 loops= 1 )
Sort Key: p.id, pc.id, p.created_on, pc.post_id, pc.review
Sort Method: quicksort Memory: 25kB
-> Hash Right Join
(cost= 11.76 .. 23.70 rows= 1 width= 1068 )
(actual time= 0.054 .. 0.058 rows= 2 loops= 1 )
Hash Cond: (pc.post_id = p.id)
-> Seq Scan on post_comment pc
(cost= 0.00 .. 11.40 rows= 140 width= 532 )
(actual time= 0.010 .. 0.010 rows= 2 loops= 1 )
-> Hash
(cost= 11.75 .. 11.75 rows= 1 width= 528 )
(actual time= 0.027 .. 0.027 rows= 1 loops= 1 )
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on post p
(cost= 0.00 .. 11.75 rows= 1 width= 528 )
(actual time= 0.017 .. 0.018 rows= 1 loops= 1 )
Filter: (
(title)::text =
'High-Performance Java Persistence eBook has been released!' ::text
)
Rows Removed by Filter: 3
|
有关 JPA 中如何工作的更多详细信息,请同时阅读本文。
DISTINCT
情况不再如此,因为现在实体对象引用重复数据删除是自动完成的,因此您的查询不再需要关键字:JOIN FETCH
DISTINCT
1
2
3
4
5
6
7
8
|
List<Post> posts = entityManager.createQuery( "" "
select p
from Post p
left join fetch p.comments
where p.title = :title
"" ", Post. class )
.setParameter( "title" , "High-Performance Java Persistence" )
.getResultList();
|
厉害吧?
<img data-lazy-fallback=“1” src=“https://vladmihalcea.com/wp-content/uploads/2018/01/HPJP_h200.jpg” /正在上传…重新上传取消正在上传…重新上传取消正在上传…重新上传取消
结论
Spring 6 真的值得升级到。除了受益于Java 17提供的所有语言优化之外,所有其他框架依赖项还提供了大量新功能,这些依赖项已集成到Spring 6中。
例如,Hibernate 6提供了许多优化和新功能,可以满足您的许多日常数据访问需求。
转载:https://blog.csdn.net/allway2/article/details/128068846