专栏首页Albert陈凯2018-11-17 面试必问问题TransactionalJava事务之一——Java事务的基本问题

2018-11-17 面试必问问题TransactionalJava事务之一——Java事务的基本问题

Java事务之一——Java事务的基本问题

是一个系列文章,里面很多链接访问到相关的知识,适合仔细阅读入门

关于加@Transactional注解的方法之间调用,事务是否生效的问题 https://blog.csdn.net/blacktal/article/details/79345902

之前面试被问过这个问题,回答基本靠猜,在此记录一下事务方法调用的问题。

  1. 不同类之间的方法调用,如类A的方法a()调用类B的方法b(),这种情况事务是正常起作用的。只要方法a()或b()配置了事务,运行中就会开启事务,产生代理。

若两个方法都配置了事务,两个事务具体以何种方式传播,取决于设置的事务传播特性。

  1. 同一个类内方法调用:重点来了,同一个类内的方法调用就没那么简单了,假定类A的方法a()调用方法b()

同一类内方法调用,无论被调用的b()方法是否配置了事务,此事务在被调用时都将不生效。 看一个例子:

@Service
public class UserServiceImpl implements UserService {
 
    @Autowired
    private UserDao userDao;
    
    public int insertUser(User user) {
        userDao.insertUser(user);
        // 调用同类方法
        this.selectUser(user.getId());
        return 1;
        
    }
    @Transactional
    public String selectUser(int id) {
        throw new RuntimeException();
        //return userDao.selectUser(id);
    }
 
    public int updateUser(User user) {
        return userDao.updateUser(0, user);
    }
 
}

service层中,insertUser()方法没有配置事务,selectUser()配置了事务,在insertUser()中调用selectUser()时,查看日志如下:

[DEBUG][2018-02-22 11:00:32] org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:104) drop table if exists user 
    Hibernate: drop table if exists user
[DEBUG][2018-02-22 11:00:32] org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:104) create table user (id integer not null auto_increment, createdDate datetime, email varchar(255), name varchar(20) not null, password varchar(255), phone varchar(255), updatedDate datetime, primary key (id)) 
    Hibernate: create table user (id integer not null auto_increment, createdDate datetime, email varchar(255), name varchar(20) not null, password varchar(255), phone varchar(255), updatedDate datetime, primary key (id))
[INFO][2018-02-22 11:00:32] org.hibernate.tool.hbm2ddl.SchemaExport.execute(SchemaExport.java:406) HHH000230: Schema export complete 
    [DEBUG][2018-02-22 11:00:32] org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:1073) Checking 0 named HQL queries 
    [DEBUG][2018-02-22 11:00:32] org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:1096) Checking 0 named SQL queries 
    [DEBUG][2018-02-22 11:00:32] org.hibernate.stat.internal.StatisticsInitiator.initiateServiceInternal(StatisticsInitiator.java:110) Statistics initialized [enabled=false] 
    [INFO][2018-02-22 11:00:33] org.springframework.orm.hibernate4.HibernateTransactionManager.afterPropertiesSet(HibernateTransactionManager.java:360) Using DataSource [com.mchange.v2.c3p0.ComboPooledDataSource[ identityToken -> 1hge15x9t1g0fr98s704x1|72d1ad2e, dataSourceName -> 1hge15x9t1g0fr98s704x1|72d1ad2e ]] of Hibernate SessionFactory for HibernateTransactionManager 
    [DEBUG][2018-02-22 11:00:33] org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:228) Executing identity-insert immediately 
    [DEBUG][2018-02-22 11:00:33] org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:104) insert into user (createdDate, email, name, password, phone, updatedDate) values (?, ?, ?, ?, ?, ?) 
    Hibernate: insert into user (createdDate, email, name, password, phone, updatedDate) values (?, ?, ?, ?, ?, ?)
[DEBUG][2018-02-22 11:00:33] org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:212) Obtaining JDBC connection 
    [DEBUG][2018-02-22 11:00:33] org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:218) Obtained JDBC connection 
    [DEBUG][2018-02-22 11:00:33] org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity(IdentifierGeneratorHelper.java:93) Natively generated identity: 1 
    [DEBUG][2018-02-22 11:00:33] org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:267) parse() - HQL: select name from com.wcl.pojo.User u where u.id = ? 
    [DEBUG][2018-02-22 11:00:33] org.hibernate.hql.internal.ast.QueryTranslatorImpl.showHqlAst(QueryTranslatorImpl.java:285) --- HQL 
AST ---

可见没有开启事务,因此selectUser()的事务配置没有生效,抛异常后也不会回滚。

另一个例子:方法a()配置了事务,此时b()的事务虽然不生效,但a()的事务生效,对于b()中抛出的异常也会回滚。

[DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:104) drop table if exists user 
    Hibernate: drop table if exists user
[DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:104) create table user (id integer not null auto_increment, createdDate datetime, email varchar(255), name varchar(20) not null, password varchar(255), phone varchar(255), updatedDate datetime, primary key (id)) 
    Hibernate: create table user (id integer not null auto_increment, createdDate datetime, email varchar(255), name varchar(20) not null, password varchar(255), phone varchar(255), updatedDate datetime, primary key (id))
[INFO][2018-02-22 11:08:50] org.hibernate.tool.hbm2ddl.SchemaExport.execute(SchemaExport.java:406) HHH000230: Schema export complete 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:1073) Checking 0 named HQL queries 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:1096) Checking 0 named SQL queries 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.stat.internal.StatisticsInitiator.initiateServiceInternal(StatisticsInitiator.java:110) Statistics initialized [enabled=false] 
    [INFO][2018-02-22 11:08:50] org.springframework.orm.hibernate4.HibernateTransactionManager.afterPropertiesSet(HibernateTransactionManager.java:360) Using DataSource [com.mchange.v2.c3p0.ComboPooledDataSource[ identityToken -> 1hge15x9t1g0qf4c3bcmx8|399f45b1, dataSourceName -> 1hge15x9t1g0qf4c3bcmx8|399f45b1 ]] of Hibernate SessionFactory for HibernateTransactionManager 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:212) Obtaining JDBC connection 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.inter<a href="http://blog.csdn.net/JIESA/article/details/53438342" rel="nofollow" target="_blank">nal.LogicalCo</a>nnectionImpl.obtainConnection(LogicalConnectionImpl.java:218) Obtained JDBC connection 
    <span style="color:#ff0000;">[DEBUG][2018-02-22 11:08:50] org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:158) begin 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:69) initial autocommit status: true 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:71) disabling autocommit </span>
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:228) Executing identity-insert immediately 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:104) insert into user (createdDate, email, name, password, phone, updatedDate) values (?, ?, ?, ?, ?, ?) 
    Hibernate: insert into user (createdDate, email, name, password, phone, updatedDate) values (?, ?, ?, ?, ?, ?)
[DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:212) Obtaining JDBC connection 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:218) Obtained JDBC connection 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity(IdentifierGeneratorHelper.java:93) Natively generated identity: 1 
    <span style="color:#ff0000;">[DEBUG][2018-02-22 11:08:50] org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:203) rolling back 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doRollback(JdbcTransaction.java:164) rolled JDBC Connection </span>
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.releaseManagedConnection(JdbcTransaction.java:126) re-enabling autocommit 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.releaseConnection(LogicalConnectionImpl.java:232) Releasing JDBC connection 
    [DEBUG][2018-02-22 11:08:50] org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.releaseConnection(LogicalConnectionImpl.java:250) Released JDBC connection 
    Exception in thread "main" java.lang.RuntimeException
    at com.wcl.service.UserServiceImpl.selectUser(UserServiceImpl.java:26)
    at com.wcl.service.UserServiceImpl.insertUser(UserServiceImpl.java:20)
  1. 不生效的原因?

有几篇文章探究了事务这个特性的原因,spring声明式事务 同一类内方法调用事务失效

个人理解,当从类外调用方法a()时,从spring容器获取到的serviceImpl对象实际是包装好的proxy对象,因此调用a()方法的对象是动态代理对象。而在类内部a()调用b()的过程中,实质执行的代码是this.b(),此处this对象是实际的serviceImpl对象而不是本该生成的代理对象,因此直接调用了b()方法。

aop原理跟事务一样,往大里说是动态代理,往小里说是反射机制。我又测试了两个方法,分别加上aop增强通知,类内调用的效果跟事务是一样的。这里最好研究一下spring aop和事务的源码,应该能搞得更清楚。


作者:blacktal 来源:CSDN 原文:https://blog.csdn.net/blacktal/article/details/79345902 版权声明:本文为博主原创文章,转载请附上博文链接!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 2018-10-11 Java 分布式应用追踪系统 skywalking (APM(应用性能监控)系统)Document

    SkyWalking: 针对分布式系统的APM(应用性能监控)系统,特别针对微服务、cloud native和容器化(Docker, Kubernetes, M...

    Albert陈凯
  • XX公司大数据笔试题(A)

    XX公司大数据笔试题(A) 大数据基础(HDFS/Hbase/Hive/Spark〉 1.1. 对出Hadoop集群典型的配置文件名称,并说明各配置文件的用途...

    Albert陈凯
  • 大数据领域的性能测试Benchmark介绍

    一、Benchmark简介 Benchmark是一个评价方式,在整个计算机领域有着长期的应用。正如维基百科上的解释“As computer architect...

    Albert陈凯
  • 关于加@Transactional注解的方法之间调用,事务是否生效的问题

    1. 不同类之间的方法调用,如类A的方法a()调用类B的方法b(),这种情况事务是正常起作用的。只要方法a()或b()配置了事务,运行中就会开启事务,产生代理。

    AlbertZhang
  • VR技术的进步推动工业机器人革命

    这项成果象征着业界正朝着让机器人了解视觉世界的未来更迈进了一步。今天,人类专家通常以一种相对较缓慢的两步骤过程训练厂房中的机器人重复几个动作,但这有时还需要人类...

    企鹅号小编
  • CentOS5/CentOS6/CentOS7 安装KDE、Xfce图形界面

    注意:执行第一或第二部分就可以了(不要都执行),第三部分是扩展补充(可以不执行) 一、 CentOS5/CentOS6/CentOS7(通用)安装KDE图形界面...

    我爱你的一诺
  • 致程序猿:专业课老师没教的,都在这8本书里了

    然后,朋友圈里一会儿是“00后开始学习人工智能”,一会儿又是“35岁的程序员没人要”……数据叔觉得,焦虑往往来源于对未知(以及自己的无知)的恐惧,所以数据叔也在...

    华章科技
  • Windows10系统下安装配置MongoDB数据库

    1、下载 地址:http://www.mongodb.org/downloads(32位还是64位自行选择)。

    似水的流年
  • Windows10系统下安装配置MongoDB数据库

    1、下载 地址:http://www.mongodb.org/downloads(32位还是64位自行选择)。 2、解压 把mongodb-win...

    似水的流年
  • Windows10系统下安装配置MongoDB数据库

    1、下载 地址:http://www.mongodb.org/downloads(32位还是64位自行选择)。

    似水的流年

扫码关注云+社区

领取腾讯云代金券