首页 > 其他分享 >面试官:@Transactional(readOnly=true) 有什么用?还有谁不会?!

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

时间:2023-11-20 10:22:36浏览次数:32  
标签:10 面试官 Transactional readOnly TotalConnections only 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开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

标签:10,面试官,Transactional,readOnly,TotalConnections,only,true
From: https://www.cnblogs.com/javastack/p/17843364.html

相关文章

  • Readonly只针对input(text/password)和textarea有效,而disabled对于所有的表单元素有效
    Readonly只针对input(text/password)和textarea有效,而disabled对于所有的表单元素有效,包括select,radio,checkbox,button等Readonly和Disabled是用在表单中的两个属性,它们都能够做到使用户不能够更改表单域中的内容。但是它们之间有着微小的差别,总结如下:Readonly只针对input(tex......
  • 面试官:听说你很了解Java8特性,给我优化一下这段代码吧?
    @[toc]前言在之前的一次面试过程中,我被问到了一道代码优化题:对于下面的代码,你有什么优化的思路呢?booleanhandleStrList(StringstrList){ for(Strings:strList){ if(s.length()%2==0){ returntrue; } }booleanhandleStrList(StringstrList){for(Strings:s......
  • 面试官:你会如何设计QQ中的网络协议?
    引言在设计QQ这道面试题时,我们需要避免进入面试误区。这意味着我们不应该盲目地开展头脑风暴,提出一些不切实际的想法,因为这些想法可能无法经受面试官的深入追问。因此,我们需要站在前人的基础上,思考如何解决这类面试题。我们可以设计一个实际可行的QQ系统,而不是离题太远。设计细......
  • Spring Boot - @Transactional 标注的方法如何使用 synchronized?
    这篇文章中有说到@Transactional标注的方法也有锁的情况下会出现一些问题,具体请看SpringBoot锁。而且Idea也会标一个黄色波浪线提示你。我是这样做的,仅供参考。file:[DiscussionService.java]@ServicepublicclassDiscussionServiceimplementsIDiscussionService{......
  • @Transactional:声明式事务管理,保证数据一致性
    一、介绍通过使用@Transactional注解,我们可以更加方便地管理事务,保障数据的一致性和可靠性。在实际项目中,合理使用@Transactional注解可以提高「开发效率」和代码「可维护性」。二、用法@Transactional(rollbackFor=Exception.class)publicResponseDTO<String>update(No......
  • 这个面试官真烦,问完合并又问拆分。
    你好呀,我是歪歪。这次来盘个小伙伴分享给我的一个面试题,他说面试的过程中面试官的问了一个比较开放的问题:请谈谈你对于请求合并和分治的看法。他觉得自己没有答的特别好,主要是没找到合适的角度来答题,跑来问我怎么看。我能怎么看?我也不知道面试官想问啥角度啊。但是这种开放......
  • 拿到offer后,面试官:你需要了解公司的哪方面?
      面试入职流程中面试官都会问到:你需要了解公司的哪方面?部分小伙伴包括之前的我就会非常局促的随便问问,工作团队,内容,是否双休等等匆匆而过,但是这一部分其实非常重要,问得足够充分影响正式入职后的职场是否能适应,及早做判断,并有一定的心理准备。  以下问题为个人入职后总结入......
  • Spring源码解析——@Transactional注解的声明式事物介绍
    正文面的几个章节已经分析了spring基于@AspectJ的源码,那么接下来我们分析一下Aop的另一个重要功能,事物管理。最全面的Java面试网站事务的介绍1.数据库事物特性原子性多个数据库操作是不可分割的,只有所有的操作都执行成功,事物才能被提交;只要有一个操作执行失败,那么所有的操作......
  • 谁面对面试官紧张了?谁?
    本文首发自公粽hao「林行学长」,欢迎来撩,免费领取20个求职工具资源包。了解校招、分享校招知识的学长来了!10月中旬了,想必大家都有了一定的面试经历。面试是许多人在职业发展中必经的一道关口,但是对许多求职者来说,面试过程却充满了紧张和压力。其中一个主要原因是对面试官的紧张。01......
  • 面试官:什么是单点登录?如何实现?
     一、是什么单点登录(SingleSignOn),简称为SSO,是目前比较流行的企业业务整合的解决方案之一SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统SSO一般都需要一个独立的认证中心(passport),子系统的登录均得通过passport,子系统本身将不参与登录......