面试官:@Transactional(readOnly=true) 有什么用?还有谁不会?!

这篇具有很好参考价值的文章主要介绍了面试官:@Transactional(readOnly=true) 有什么用?还有谁不会?!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

原文翻译自:https://medium.com

今天,我想谈谈 Spring 提供的@Transactional(readOnly = true)

之所以聊这个是因为我公司项目的代码里有很多@Transactional(readOnly = true),用过的同学都说@Transactional(readOnly = true)提高了性能。先思考以下几点:

  • @Transactional(readOnly = true)是如何工作的,为什么使用它可以提高性能?
  • 当我们使用 JPA 时,是否应该总是将@Transactional(readOnly = true)添加到服务层的只读方法?有什么取舍吗?

在开始之前,我们使用 Hibernate 来实现 JPA。

推荐一个开源免费的 Spring Boot 实战项目:

https://github.com/javastacks/spring-boot-best-practice

1、@Transactional(readOnly = true)是如何工作的,为什么使用它可以提高性能?

首先,让我们看一下事务接口。

/**
* A boolean flag that can be set to {@code true} if the transaction is
* effectively read-only, allowing for corresponding optimizations at runtime.
* <p>Defaults to {@code false}.
* <p>This just serves as a hint for the actual transaction subsystem;
* it will <i>not necessarily</i> cause failure of write access attempts.
* A transaction manager which cannot interpret the read-only hint will
* <i>not</i> throw an exception when asked for a read-only transaction
* but rather silently ignore the hint.
* @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()
*/
boolean readOnly() default false;

我们可以看到 readOnly = true 选项允许优化。事务管理器将使用只读选项作为提示。让我们看看用于事务管理器的JpaTransactionManager

@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
 JpaTransactionObject txObject = (JpaTransactionObject) transaction;
  // .
  // Delegate to JpaDialect for actual transaction begin.
  int timeoutToUse = determineTimeout(definition);
  Object transactionData = getJpaDialect().beginTransaction(em,
    new JpaTransactionDefinition(definition, timeoutToUse, txObject.isNewEntityManagerHolder()));
  //...
}

JpaTransactionManager中,doBegin方法委托JpaDialect来开始实际的事务,并在JpaDialect中调用beginTransaction。让我们来看看HibernateJpaDialect类。

@Override
public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition)
  throws PersistenceException, SQLException, TransactionException {
   // ...
   // Adapt flush mode and store previous isolation level, if any.
   FlushMode previousFlushMode = prepareFlushMode(session, definition.isReadOnly());
   if (definition instanceof ResourceTransactionDefinition &&
     ((ResourceTransactionDefinition) definition).isLocalResource()) {
    // As of 5.1, we explicitly optimize for a transaction-local EntityManager,
    // aligned with native HibernateTransactionManager behavior.
    previousFlushMode = null;
    if (definition.isReadOnly()) {
     session.setDefaultReadOnly(true);
    }
   }
   // ...
}

protected FlushMode prepareFlushMode(Session session, boolean readOnly) throws PersistenceException {
    FlushMode flushMode = session.getHibernateFlushMode();
    if (readOnly) {
     // We should suppress flushing for a read-only transaction.
     if (!flushMode.equals(FlushMode.MANUAL)) {
      session.setHibernateFlushMode(Flusode.MANUAL);
      return flushMode;
     }
    }
    else {
     // We need AUTO or COMMIT for a non-read-only transaction.
     if (flushMode.lessThan(FlushMode.COMMIT)) {
      session.setHibernateFlushMode(FlushMode.AUTO);
      return flushMode;
     }
    }
    // No FlushMode change needed...
    return null;
}

在JpaDialect中,我们可以看到JpaDialect使用只读选项准备刷新模式。当 readOnly = true 时, JpaDialect 禁止刷新。此外,您还可以看到,在准备刷新模式后,session.setDefaultReadOnly(true)将session的readOnly属性设置为true。

/**
 * Change the default for entities and proxies loaded into this session
 * from modifiable to read-only mode, or from modifiable to read-only mode.
 *
 * Read-only entities are not dirty-checked and snapshots of persistent
 * state are not maintained. Read-only entities can be modified, but
 * changes are not persisted.
 *
 * When a proxy is initialized, the loaded entity will have the same
 * read-only/modifiable setting as the uninitialized
 * proxy has, regardless of the session's current setting.
 *
 * To change the read-only/modifiable setting for a particular entity
 * or proxy that is already in this session:
 * @see Session#setReadOnly(Object,boolean)
 *
 * To override this session's read-only/modifiable setting for entities
 * and proxies loaded by a Query:
 * @see Query#setReadOnly(boolean)
 *
 * @param readOnly true, the default for loaded entities/proxies is read-only;
 *                 false, the default for loaded entities/proxies is modifiable
 */
void setDefaultReadOnly(boolean readOnly);

在Session接口中,通过将readOnly属性设置为true,将不会对只读实体进行脏检查,也不会维护持久状态的快照。此外,只读实体的更改也不会持久化。

总而言之,这些是在 Hibernate 中使用@Transactional(readOnly = true)所得到的结果

  • 性能改进:只读实体不进行脏检查
  • 节省内存:不维护持久状态的快照
  • 数据一致性:只读实体的更改不会持久化
  • 当我们使用主从或读写副本集(或集群)时,@Transactional(readOnly = true)使我们能够连接到只读数据库

2、当我们使用 JPA 时,是否应该总是将@Transactional(readOnly = true)添加到服务层的只读方法?有什么取舍吗?

我看到,当使用@Transactional(readOnly = true)时,我们可以有很多优势。但是,将@Transactional(readOnly = true)添加到服务层的只读方法是否合适?以下是我担心的事情

  1. 无限制地使用事务可能会导致数据库死锁、性能和吞吐量下降。
  2. 由于一个事务占用一个DB连接,所以@Transactional(readOnly = true)添加到Service层的方法可能会导致DB连接饥饿。

推荐一个开源免费的 Spring Boot 实战项目:

https://github.com/javastacks/spring-boot-best-practice

第一个问题很难重现,所以我做了一些测试来检查第二个问题。

@Transactional(readOnly = true)
public List<UserDto> transactionalReadOnlyOnService(){
    List<UserDto> userDtos = userRepository.findAll().stream()
            .map(userMapper::toDto)
            .toList();
    timeSleepAndPrintConnection();
    return userDtos;
}

public List<UserDto> transactionalReadOnlyOnRepository(){
    List<UserDto> userDtos = userRepository.findAll().stream()
            .map(userMapper::toDto)
            .toList();
    timeSleepAndPrintConnection();
    return userDtos;
}

我在服务层测试了两个方法,一个是@Transactional(readOnly = true),另一个是存储库层中的@Transactional (readOnly = true)(在 SimpleJpaRepository 中,它是 Jpa Respitory 的默认实现,在类的顶部有@Transformational(ready Only),因此 findAll()方法在默认情况下有@transactional(read only = True))。

我从DB中获取userInfo并保持线程5秒钟,然后检查该方法何时释放连接。

结果如下:

对于服务层方法中的@Transactional(readOnly = true)

activeConnections:0, IdleConnections:10, TotalConnections:10
start transactionalReadOnlyOnService!!
Hibernate: 
    select
        u1_0.id,
        u1_0.email,
        u1_0.name,
        u1_0.profile_file_name 
    from
        users u1_0
activeConnections:1, IdleConnections:9, TotalConnections:10
activeConnections:1, IdleConnections:9, TotalConnections:10
activeConnections:1, IdleConnections:9, TotalConnections:10
activeConnections:1, IdleConnections:9, TotalConnections:10
activeConnections:1, IdleConnections:9, TotalConnections:10
end transactionalReadOnlyOnService!!
activeConnections:0, IdleConnections:10, TotalConnections:10

对于存储库层方法中的@Transactional(readOnly = true)

activeConnections:0, IdleConnections:10, TotalConnections:10
start transactionalReadOnlyOnRepository!!
Hibernate: 
    select
        u1_0.id,
        u1_0.email,
        u1_0.name,
        u1_0.profile_file_name 
    from
        users u1_0
activeConnections:0, IdleConnections:10, TotalConnections:10
activeConnections:0, IdleConnections:10, TotalConnections:10
activeConnections:0, IdleConnections:10, TotalConnections:10
activeConnections:0, IdleConnections:10, TotalConnections:10
activeConnections:0, IdleConnections:10, TotalConnections:10
end transactionalReadOnlyOnRepository!!
activeConnections:0, IdleConnections:10, TotalConnections:10

正如您所看到的,@Transactional(readOnly = true)一旦查询结果到达,存储库层就会释放连接。

然而,@Transactional(readOnly = true)在服务层的方法中直到服务层的方法结束才释放连接。

因此,当服务层的方法有需要大量时间的逻辑时要小心,因为它可以长时间持有数据库连接,这可能会导致数据库连接匮乏。

3、回顾

很明显,@Transactional(readOnly = true)有很多优点。

  • 性能改进:只读实体不进行脏检查
  • 节省内存:不维护持久状态的快照
  • 数据一致性:只读实体的更改不会持久化
  • 当我们使用主从或读写副本集(或集群)时,@Transactional(readOnly = true)使我们能够连接到只读数据库

但是,您还应该记住,@Transactional(readOnly = true)在服务层的方法中可能会导致数据库死锁、性能低下和数据库连接匮乏!

当您需要将只读查询仅仅作为一个事务执行时,请毫不犹豫选择的在服务层的方法中使用@Transactional(readOnly = true),如果你的服务层的方法中有大量其他逻辑方法时,就要做取舍了!

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!文章来源地址https://www.toymoban.com/news/detail-746702.html

到了这里,关于面试官:@Transactional(readOnly=true) 有什么用?还有谁不会?!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Nginx + Spring Boot 轻松实现负载均衡,还有谁不会??

    来源:虚无境的博客 地址:www.cnblogs.com/xuwujing/p/11953697.html 在介绍Nginx的负载均衡实现之前,先简单的说下负载均衡的分类,主要分为 硬件负载均衡和软件负载均衡 ,硬件负载均衡是使用专门的软件和硬件相结合的设备,设备商会提供完整成熟的解决方案,比如F5,在数据的

    2024年02月08日
    浏览(27)
  • 深入浅出 Java 中的神锁:ReentrantLock,还有谁不会?

    来源:jiannan.blog.csdn.net/article/details/121331360 话不多说,扶我起来,我还可以继续撸。 在学习 ReentrantLock 源码之前,先来回顾一下链表、队列数据结构的基本概念~~ 小学一、二年级的时候,学校组织户外活动,老师们一般都要求同学之间小手牵着小手。这个场景就很类似一

    2024年02月08日
    浏览(35)
  • ChatGPT拒绝做这5件事,还有20件它也不会做!

    导读 :自从ChatGPT和其他生成式AI应用程序出现以来,我们一直在探索我们可以用它们做什么。看起来ChatGPT可以做任何事情。但在本文中,我们将探讨ChatGPT不会或无法做的事情。 本文字数:1800,阅读时长大约:10分钟 自从ChatGPT和其他生成式AI应用程序出现以来,我们一直在

    2024年02月09日
    浏览(27)
  • Spring Boot 调用外部接口的 3 种方式,还有谁不会?!

    SpringBoot不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。 在Spring-Boot项目开发中,存在着本模块的代码需要访问外面模块接口,或外部url链接的需求, 比如在apaas开发过程中需要封装接口在接口中调用apaas提供的接口(

    2024年02月09日
    浏览(49)
  • 【MySQL数据库】看完还有谁学不会 MySQL 中的视图?

    在MySQL中,视图是一种虚拟表,它是由一个或多个基本表的行或列组成的。视图并不实际存储数据,而是根据定义的查询语句动态生成结果集。视图可以简化复杂的查询操作,提高查询效率,同时也可以保护数据的安全性,隐藏敏感数据。 视图是一种虚拟表,它是由一个或多

    2024年02月10日
    浏览(40)
  • Maven 打包跳过测试的 5 种方法,应有尽有,还有谁不会!?

    平时开发时的工作的话之主要负责写代码就行了,什么发布项目啊,好吧不是我们干的事。在我们的了解中打包发布项目应该不是一个困难的问题。 对,最简单的方法就行使用直接使用maven插件打包,甚至我们都不需要知道他是怎么实现的,插件能帮我们将项目打包为一个

    2023年04月18日
    浏览(36)
  • Civitai中国镜像AI模型免费网站——只为造福不会魔法的炼丹师,还有五个免费模型网址整合介绍

    Civitai 是一个完全免费开放的AI绘画视频模型平台,所有用户都可以免费注册、登录、上传、下载、评论、评分、收藏、关注等等。 不会对任何人收取任何费用。 Civitai的目标是为AI艺术爱好者提供一个便利友好的资源共享和发现平台,并建立一个活跃支持的AI绘画视频模型艺

    2024年02月04日
    浏览(28)
  • Spring 声明式事务 @Transactional(详解)【面试重点,小林出品】

            关于 @Transactional 注解的基本使用,推荐看Spring 声明式事务 @Transactional(基本使用)         本篇博客主要学习 @Transactional 注解当中的三个常⻅属性:         1. rollbackFor:异常回滚属性.指定能够触发事务回滚的异常类型.可以指定多个异常类型         2. Iso

    2024年01月25日
    浏览(36)
  • Linux shell脚本set -e原理(set -o errexit 命令出错退出)(|| true屏蔽前面子命令返回值)(用if判断即使命令返回值不为零也不会退出)

    set -e 是 Linux shell 中的一个选项,它的作用是在脚本执行过程中,一旦出现任何命令执行失败(返回非零的退出状态码),脚本就会立即退出。 具体原理如下: 当设置了 set -e 选项后,shell 会在每个命令执行后检查其退出状态码。 如果命令的退出状态码为非零,表示命令执行

    2024年02月22日
    浏览(35)
  • vue本地运行开发,为什么要配置changeOrigin: true

    changeOrigin: true 是在配置反向代理时常见的一个选项,通常用于解决跨域请求的问题。Vue本地运行时,可能会涉及到前端应用与后端服务不在同一个域的情况,这就会触发浏览器的同源策略,阻止跨域请求。 使用Vue CLI(或其他类似的工具)在本地运行时,可以通过配置代理来

    2024年01月18日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包