我们可以使用 @Transactional(readOnly = true) 来设置只读事务

在将事务设置成只读后,当前只读事务就不能进行写的操作,否则报错。如下

Cause: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed;

需不需要在只有查询的方法上加上@Transactional注解?

需要分为两种情况来看

  • 若一个事务里只发出一条select语句,则没有必要启用事务支持,数据库默认支持sql执行期间的读一致性。此时可以不加@Transactional注解

  • 若一个事务里先后发出了多条select语句。 @Transactional注解表明被申明方法是一个整体事务。在mysql的RR隔离级别下,普通的无锁select是镜像读,多次查询结果不会改变,所以能保证读一致性(可重复读);相反若不加 @Transactional注解,则多条select都是独立的事务在前条select之后,后条select之前,数据被其他事务改变,则该次整体的查询将会出现读数据不一致的现象。此时需要添加@Transactional注解

需不需要在只有查询的方法上使用@Transactional(readOnly = true)申明为一个只读事务?

需要,因为存在可能的优化

我们可以看一下@Transactional注解的源码,其中的readOnly 属性的解释

    /*** 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;

这段文字的说明如下:

  • 一个布尔标志,如果事务设置为只读,允许在运行时进行相应的优化。默认为false;
  • 这只是对实际事务子系统的提示,它不一定会导致写入访问尝试失败。无法解析只读提示的事务管理器也不会引发异常而是默默地忽略这个属性。

对读一致性的验证

如下单元测试代码中testSelect()方法先后查询2次(没有加 @Transactional注解),testInsert()方法在2次查询前插入数据

package com.springboot.study.demo1;
import com.springboot.study.demo1.entity.User;
import com.springboot.study.demo1.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;/***@program: springboot_study*@description:*@author: yinkai*@create: 2020-02-29 12:03*/
@RunWith(SpringRunner.class)
@SpringBootTest
@Rollback(value=false)//事务提交而不是回滚
public class TestTran {@Resourceprivate UserMapper mapper;@Testpublic void testSelect() throws InterruptedException {List<User> list1 = mapper.selectList(null);System.out.println(list1.size());//睡眠5秒给insert事务提供时间System.out.println("=================睡眠20s==============");TimeUnit.SECONDS.sleep(20);//再次查询List<User> list2 = mapper.selectList(null);System.out.println(list2.size());}@Test@Rollback(value=false)//事务提交而不是回滚@Transactionalpublic void testInsert(){User user = new User(11L,"YINKAI",25,"YINKAI@ALIYUN.COM",213);mapper.insert(user);}}

运行测试方法testSelect(),该方法在第一次查询之后睡眠20秒(查询记录为18条)。然后执行testInsert()方法,插入了一条数据后testSelect()睡眠时间到继续执行,再次查询记录为19条。两次查询结果不一致,这也是一种幻读现象

进行对比试验,在testSelect()方法上加上@Transactional(readOnly = true)注解

@Test@Transactional(readOnly = true)public void testSelect() throws InterruptedException {List<User> list1 = mapper.selectList(null);System.out.println(list1.size());//睡眠5秒给insert事务提供时间System.out.println("=================睡眠20s==============");TimeUnit.SECONDS.sleep(20);//再次查询List<User> list2 = mapper.selectList(null);System.out.println(list2.size());}

再次运行testSelect()和testInsert()方法,发现两次结果相同。而且只发出了一条sql语句(这个优化并不是readOnly = true所带来的,事实上只要加上@Transactional注解就会有这种优化)

再次进行对比试验,在testSelect()方法上加上@Transactional注解(不包含readOnly 属性)

    @Test@Transactionalpublic void testSelect() throws InterruptedException {List<User> list1 = mapper.selectList(null);System.out.println(list1.size());//睡眠5秒给insert事务提供时间System.out.println("=================睡眠20s==============");TimeUnit.SECONDS.sleep(20);//再次查询List<User> list2 = mapper.selectList(null);System.out.println(list2.size());}

使用@Transactional注解不加readOnly=true 也只是发出一条sql,这就是一种优化。但是此优化并非readOnly=true所带来的;两次查询结果相同,说明单单使用@Transactional注解就能够带来 读一致性(可重复读)

大总结

在开发中我们需要在只发出查询语句的方法上添加@Transactional(readOnly = true)注解,将之申明为只读事务。

  • 多条查询下要使用该注解,能够防止多次查询到的数据不一致(维持可重复读)。而且有一定的优化,比如上面的两次的查询只发出一条sql;
  • 尽管在单条查询下不会出现数据不一致现象,但是使用@Transactional(readOnly = true)注解能够优化查询,源码中提到readOnly = true也存在着可能的优化

加上@Transactional(readOnly = true)可以保证读一致性查询优化以及一些可能的优化,即使数据库和驱动底层不支持readOnly 属性,那也不会报错。我们何乐而不为呢?

如何证明readOnly = true带来了优化?

事实上readOnly 和spring没有任何关系,spring事务仅仅是一层封装,最后都要调用底层驱动的setReadonly方法来开启。所以readOnly 是否能到来优化还是要看 数据库类型和驱动类型;所以readonly语义不是所有的数据库驱动都支持的

比如Oracle对于只读事务,不启动回滚段,不记录回滚log。又比如在spring+hibernate的条件下,readonly有一些优化

spring doc 有如下描述

Read-only status: A read-only transaction can be used when your code
reads but does not modify data. Read-only transactions can be a useful
optimization in some cases, such as when you are using Hibernate.

这句话解释如下:
只读状态:当代码读取但不修改数据时,可以使用只读事务。在某些情况下,只读事务可能是一种有用的优化,例如当您使用hibernate时

spring 事务管理之只读事务@Transactional(readOnly = true)相关推荐

  1. 关于只读事务:@Transactional(readOnly = true)

    一:只读事务是否做无用功?文章链接:只读事务是否只做无用功 @Transactional(ReadOnly=true) mysql支持只读事务的. 只读事务内,不能增加.修改.删除内容,否则报Cann ...

  2. 带你理解 只读事务(@Transactional(readOnly = true)

    概念:从这一点设置的时间点开始(时间点a)到这个事务结束的过程中,其他事务所提交的数据,该事务将看不见!(查询中不会出现别人在时间点a之后提交的数据) 应用场合: 如果你一次执行单条查询语句,则没有必 ...

  3. spring事务@Transactional(readOnly = true)及隔离级别实验

    先写结论: 第一:@Transactional(readOnly = true) 1.那么方法里面,必须是读的操作,当有写的操作的时候会报错提示. 2.读到的数据,从进入方法开始,以后即使其他客户端修 ...

  4. spring框架学习 - Data Access之 事务管理 - 声明式事务管理

    接上一篇博客:https://blog.csdn.net/qq_43605444/article/details/122085016?spm=1001.2014.3001.5502 4.声明式事务管理 ...

  5. Java开发【Spring之AOP详解(xml--注解->方法增强、事务管理(声明事务的实现))】

    文章目录 引入 一.AOP概述 1.什么是AOP 2.AOP的优势及使用场景 3.AOP实现原理 二.代理模式 1.代理模式概念 2.代理模式分类 3.静态代理演示 定义HouseAgencyComp ...

  6. 说说@Transactional(readOnly = true),和mysql事务隔离级别;

    1.首先说说注解@Transactional(readOnly = true) (1)这里使用readOnly=true后,直观会给我们的体现就是,在该事务中,我们只能做查询,不能做更改等操作,否则报 ...

  7. java 数据库 事务 只读_不使用事务和使用只读事务的区别

    转转转,,还需要具体验证( 决定把readonly类型的事务完全去掉,以提高性能,这里有讨论:http://stackoverflow.com/questions/2562865/spring-tra ...

  8. @Transactional(readOnly = true)

    概念:从这一点设置的时间点开始(时间点a)到这个事务结束的过程中,其他事务所提交的数据,该事务将看不见!(查询中不会出现别人在时间点a之后提交的数据)   应用场合: 如果你一次执行单条查询语句,则没 ...

  9. spring配置mysql事务管理_Spring 数据库事务管理机制

    要点1 Spring事务管理方法编程式事务(TransactionTemplate.PlatformTransactionManager) 声明式事务(配置式.注解式) 2 Spring 注解 @Tr ...

最新文章

  1. 这个人工智能强到让人感到害怕
  2. Thief in a Shop
  3. RubyMine 1.0加入Ruby IDE大家庭
  4. Centos7 下载、安装、配置、启动部署
  5. 哪些年给我们留下记忆的IT站点
  6. Jupyter Lab的学习笔记
  7. R 保存包含中文的 eps 图片--showtext
  8. 巨头林立的音频赛道,喜马拉雅如何讲好资本故事?
  9. 1 分析计算机上网时断时续的原因,网络丢包现象分析与解决方案
  10. 这些音乐MV制作堪称大片,看过真是值回票价
  11. 【实用工具】让文件资源管理器像浏览器一样实现多标签化——QTTabBar
  12. 53页PPT | 湖仓一体大数据平台解决方案
  13. Datawhale组队学习NLP之transformer Task03 BERT
  14. 反对将AI用于“自主武器”公开信:Stephen Hawking、Elon Musk,以及多位人工智能专家署名...
  15. html5 健身房模板,健身房瘦身HTML模板
  16. 数据结构 (C++)笔记6 (有序列表 排序器)
  17. 第1周笔记5-Dijkstra最短路径问题
  18. 如何用vscode调试ts代码
  19. 为什么这几年电脑病毒不见了?
  20. 微信小程序从本地开发环境到线上测试环境和生产环境的配置

热门文章

  1. Java核心技术卷I基础知识1.2.7 可移植性
  2. C#开发微信门户及应用(27)-公众号模板消息管理
  3. Apache + Tomcat集群配置详解(1)
  4. 如何在MySQL随机选择记录
  5. SQL Servr 2008空间数据应用系列一:空间信息基础
  6. 解决无法使用locate命令的方法
  7. Java 工程师成神之路 | 2019正式版
  8. Spring(15)——基于注解的配置(二)
  9. 系统调用被信号打断的例子
  10. 分布式缓存技术memcached学习系列(五)—— memcached java客户端的使用