«

如何将应用程序进行Spring6迁移

时间:2024-7-23 09:42     作者:韩俊     分类: Java


这篇文章主要介绍“如何将应用程序进行Spring6迁移”,在日常操作中,相信很多人在如何将应用程序进行Spring6迁移问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何将应用程序进行Spring6迁移”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

    Java17

    首先,Spring 6 将最低 Java 版本提升到 17 个,这太棒了,因为你现在可以使用文本块和记录。

    文本块

    多亏了文本块,您的注释将更具可读性:

    @Query

    @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

    public record PostCommentRecord(
        Long id,
        String title,
        String review
    ) {}

    然后,您可以使用 Spring Data JPA 查询方法获取对象:

    PostCommentRecord

    @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 项目中注册了以下内容:

    PostCommentRecordClassClassImportIntegrator

    properties.put(
        "hibernate.integrator_provider",
        (IntegratorProvider) () -> Collections.singletonList(
            new ClassImportIntegrator(
                List.of(
                    PostCommentRecord.class
                )
            )
        )
    );

    有关 Java 记录的更多详细信息,请查看本文。

    这还不是全部!Java 17 改进了 forand 的错误消息,并添加了模式匹配 forand。

    NullPointerExceptionswitchinstanceOf

    JPA 3.1

    默认情况下,Spring 6 使用 Hibernate 6.1,而 Hibernate 6.1 又使用 Jakarta Persistence 3.1。

    现在,3.0版本标志着从Java持久性到Jakarta Patersistence的迁移,因此,出于这个原因,您必须将软件包导入替换为命名空间。

    javax.persistencejakarta.persistence

    这是迁移到 JPA 3 必须进行的最重要的更改。与此同时,发布了3.1版本,但这个版本只包括Hibernate已经支持的一些小改进。

    UUID 实体属性

    例如,JPA 3 现在支持基本类型:

    UUID

    @Column(
        name = "external_id",
        columnDefinition = "UUID NOT NULL"
    )
    private UUID externalId;

    您甚至可以将它们用作实体标识符:

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    但这只是一个糟糕的主意,因为使用 anfor 主键会导致很多问题:

    UUID

      索引页将很少填充,因为每个新的 UUID 都将在 B+树聚集索引中随机添加。

      由于主键值的随机性,将会有更多的页面拆分

      UUID很大,需要的字节数是列的两倍。它不仅影响主键,还影响所有关联的外键。

      bigint

    此外,如果您使用的是SQL Server,MySQL或MariaDB,则默认表将被组织为聚簇索引,从而使所有这些问题变得更糟。

    因此,最好避免使用 for 实体标识符。如果您确实需要从应用程序生成唯一标识符,那么最好使用64 位时间排序的随机 TSID。

    UUID

    新的 JPQL 函数

    JPQL 通过许多新功能得到了增强,例如,,,,,,数字函数。

    CEILINGFLOOREXPLNPOWERROUNDSIGN

    但是,我发现最有用的是日期/时间函数:

    EXTRACT

    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也遵循了这种做法:

    SessionSessionFactoryAutoClosableEntityManagerEntityManagerFactory

    Although you might rarely need to rely on that because Spring takes care of the on your behalf, it&rsquo;s very handy when you have to process the programmatically.EntityManagerEntityManager

    冬眠 6

    虽然Java 17和JPA 3.1为您带来了一些功能,但Hibernate 6提供了大量的增强功能。

    JDBC 优化

    以前,Hibernate使用关联的列别名读取JDBC列值,这被证明很慢。出于这个原因,Hibernate 6 已切换到按基础 SQL 投影中的位置读取基础列值。

    ResultSet

    除了速度更快之外,进行此更改还有一个非常好的副作用。基础 SQL 查询现在更具可读性。

    例如,如果您在 Hibernate 5 上运行此 JPQL 查询:

    Post post = entityManager.createQuery("""
        select p
        from Post p
        join fetch p.comments
        where p.id = :id
        """, Post.class)
    .setParameter("id", 1L)
    .getSingleResult();

    将执行以下 SQL 查询:

    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查询:

    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

    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

      全部联盟

      窗口函数

      派生表

      CTE和递归CTE

    方言增强功能

    在Hibernate 5中,您必须根据底层数据库版本选择大量版本,这在Hibernate 6中得到了极大的简化:

    Dialect

    此外,您甚至不需要在 Spring 配置中提供,因为它可以从 JDBC 解析。

    DialectDatabaseMetaData

    有关此主题的更多详细信息,请查看此文章。

    自动重复数据删除

    您还记得在使用时为实体重复数据删除提供关键字是多么烦人吗?

    DISTINCTJOIN FETCH

    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_THROUGHDISTINCT

    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

    情况不再如此,因为现在实体对象引用重复数据删除是自动完成的,因此您的查询不再需要关键字:

    JOIN FETCHDISTINCT

    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();

    标签: java spring

    热门推荐