益阳网站建设公司/郑州网络推广厂家

益阳网站建设公司,郑州网络推广厂家,wordpress getresults,北京易点云是什么公司来道题CREATE TABLE goods (id bigint(20) NOT NULL AUTO_INCREMENT,good_id varchar(20) DEFAULT NULL,num int(11) DEFAULT NULL,PRIMARY KEY (id),KEY goods_good_id_index (good_id)) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ciClass.forName("c…

来道题

CREATE TABLE `goods` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`good_id` varchar(20) DEFAULT NULL,

`num` int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `goods_good_id_index` (`good_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

Class.forName("com.mysql.jdbc.Driver");

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","");

//part1

conn.setAutoCommit(false);

Statement statement = conn.createStatement();

statement.execute("INSERT INTO test.goods ( good_id, num) VALUES ( 'sku123', 0);");

conn.commit();

//part2

statement = conn.createStatement();

statement.execute("INSERT INTO test.goods ( good_id, num) VALUES ( 'sku123', 0);");

conn.setAutoCommit(true);

//part3

try {

statement = conn.createStatement();

statement.execute("INSERT INTO test.goods ( good_id, num) VALUES ( 'sku123', 0);");

int i = 1/0;

}catch (Exception ex){

System.out.println("there is an error");

}

conn.setAutoCommit(true);

//part4

conn.setAutoCommit(false);

try {

statement = conn.createStatement();

statement.execute("INSERT INTO test.goods ( good_id, num) VALUES ( 'sku123', 0);");

int i = 1/0;

}catch (Exception ex){

System.out.println("there is an error");

}

conn.setAutoCommit(true);

你举得这4段代码都提交了吗,为什么?

如果你知道这个知识点,那么本文对于你来说很容易理解。

一个知识点

首先,上面4段代码都会提交成功。

主要的知识点是, autocommit 的状态切换时,会对自动提交之前执行的内容。

看下这个方法的注释就知道了。

db72aa1ace0b9eee407068a4bd414b64.png

image.png

他这边说,如果事务执行过程中,如果 autocommit 状态改变了,会提交之前的事务。

额,这有个逻辑上的问题,如果autocommit本身就是true,我们的语句不是直接就提交了么,那这个描述应该改成从false改成true的时候。

其实这段注释还有前半段。

53c1f10b4cf60886350035ae7da4e7e1.png

image.png

针对DML和DDL语句,autocommit=true的情况下,statement是立刻提交的。

而对于select语句,要等到关联的result set被关闭,对于存储过程....

而这个知识点太偏了,懂的朋友了解下,告诉我是啥..

所以我们这边的知识点严谨点来说就是: 对于DDL和DML语句,当autocommit从false切换为true时,事务会自动提交。

spring事务中的aftercommit

afterCommit是Spring事务机制中的一个回调钩子,用于在事务提交后做一些操作。

我们可以这么使用它

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){

public void afterCommit() {

//...执行一些操作

}

});

也可以通过@TransactionalEventListener间接使用它,它的底层原理就是上面这段代码

@TransactionalEventListener

public void handleEvent(Event event){

//...执行一些操作

}

重点是在事务提交后执行一些操作,也就是我题目中conn.commit()之后再执行一些操作。

这个时候存在一个问题,如果这个操作是数据库相关的操作,会不会被提交。

根据我文章开篇的代码,你肯定就知道答案就是会提交,但是是autocommit的切换导致的提交。

额,其实并不是,对比2个常用db框架,Mybatis和JPA(Hibernate),Mybatis会提交,而Hibernate会丢失。

57c1f61159a4ab063895e392d514f93b.png

image.png

在afercomit的注释中,他也警告我们了,在aftercommit中做数据库操作可能不会被提交。如果你要做数据库操作,你需要在一个新的事务中,可以使用PROPAGATION_REQUIRES_NEW隔离级别。

源码中的NOTE要仔细的看!!很重点

在不创建新事务的前提下,为什么对于Mybatis和JPA在aftercommit中执行操作,一个提交,一个不提交?开始我们的源码解析。

源码解析

Spring Transaction的核心逻辑封装在TransactionAspectSupport的invokeWithinTransaction方法中,而核心流程中重要的三个操作,获取/提交/回滚事务,由PlatformTransactionManager来实现。

public interface PlatformTransactionManager extends TransactionManager {

TransactionStatus getTransaction(@Nullable TransactionDefinition definition)

throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;

}

PlatformTransactionManager使用了策略模式和模板方法模式,它的子类AbstractPlatformTransactionManager又对上面三个方法做了抽象,暴露了一一系列钩子方法让子类实现。

最常用的子类就是DataSourceTransactionManager和HibernateTransactionManager,分别对应Mybatis和JPA框架。

本文讲解的aftercommit同步钩子在AbstractPlatformTransactionManager的processCommit中被触发。

回顾我们上面展示的场景,我们在一个事务里,注册了一个aftercommit钩子,并且aftercommit里面,也会再次操作数据库,执行dml操作。

private void processCommit(DefaultTransactionStatus status) throws TransactionException {

try {

//...

else if (status.isNewTransaction()) {

if (status.isDebug()) {

logger.debug("Initiating transaction commit");

}

unexpectedRollback = status.isGlobalRollbackOnly();

//...假设事务提交成功

doCommit(status);

}

try {

triggerAfterCommit(status);

}

finally {

triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);

}

}

finally {

cleanupAfterCompletion(status);

}

在第一个事务doCommit成功,他会通过triggerAfterCommit触发它的aftercommit钩子逻辑,进行下一次事务操作,但是此时的Transaction还没有释放,并且它也不是newTransaction了。

为什么不是newTransaction,见以下代码

private TransactionStatus handleExistingTransaction(

TransactionDefinition definition, Object transaction, boolean debugEnabled)

throws TransactionException {

//...

return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);

}

因为 status.isNewTransaction() 不成立,所以 doCommit(status); 不会执行。

doCommit中会进行什么操作?

对于DataSourceTransactionManager,就是调用了Connection的commit方法,对事务进行提交。

protected void doCommit(DefaultTransactionStatus status) {

DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();

Connection con = txObject.getConnectionHolder().getConnection();

if (status.isDebug()) {

logger.debug("Committing JDBC transaction on Connection [" + con + "]");

}

try {

con.commit();

}

catch (SQLException ex) {

throw new TransactionSystemException("Could not commit JDBC transaction", ex);

}

}

虽然错失了doCommit这个机会,但是在cleanupAfterCompletion(status);方法

private void cleanupAfterCompletion(DefaultTransactionStatus status) {

status.setCompleted();

if (status.isNewSynchronization()) {

TransactionSynchronizationManager.clear();

}

if (status.isNewTransaction()) {

doCleanupAfterCompletion(status.getTransaction());

}

if (status.getSuspendedResources() != null) {

if (status.isDebug()) {

logger.debug("Resuming suspended transaction after completion of inner transaction");

}

Object transaction = (status.hasTransaction() ? status.getTransaction() : null);

resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());

}

}

在doCleanupAfterCompletion的逻辑中,注意doCleanupAfterCompletion也是一个钩子,这个逻辑也由DataSourceTransactionManager实现

protected void doCleanupAfterCompletion(Object transaction) {

DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

// Remove the connection holder from the thread, if exposed.

if (txObject.isNewConnectionHolder()) {

TransactionSynchronizationManager.unbindResource(obtainDataSource());

}

// Reset connection.

Connection con = txObject.getConnectionHolder().getConnection();

try {

if (txObject.isMustRestoreAutoCommit()) {

con.setAutoCommit(true);

}

DataSourceUtils.resetConnectionAfterTransaction(

con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());

}

catch (Throwable ex) {

logger.debug("Could not reset JDBC Connection after transaction", ex);

}

if (txObject.isNewConnectionHolder()) {

if (logger.isDebugEnabled()) {

logger.debug("Releasing JDBC Connection [" + con + "] after transaction");

}

DataSourceUtils.releaseConnection(con, this.dataSource);

}

txObject.getConnectionHolder().clear();

}

调用到了 con.setAutoCommit(true);间接了提交了事务

然后我们再来看看HibernateTransactionManager对这个两个方法的实现

HibernateTransactionManager#doCommit

protected void doCommit(DefaultTransactionStatus status) {

HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();

Transaction hibTx = txObject.getSessionHolder().getTransaction();

Assert.state(hibTx != null, "No Hibernate transaction");

if (status.isDebug()) {

logger.debug("Committing Hibernate transaction on Session [" +

txObject.getSessionHolder().getSession() + "]");

}

try {

//看这里

hibTx.commit();

}

catch (org.hibernate.TransactionException ex) {

// assumably from commit call to the underlying JDBC connection

throw new TransactionSystemException("Could not commit Hibernate transaction", ex);

}

catch (HibernateException ex) {

// assumably failed to flush changes to database

throw convertHibernateAccessException(ex);

}

catch (PersistenceException ex) {

if (ex.getCause() instanceof HibernateException) {

throw convertHibernateAccessException((HibernateException) ex.getCause());

}

throw ex;

}

}

HibernateTransactionManager#doCleanupAfterCompletion

protected void doCleanupAfterCompletion(Object transaction) {

HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;

// Remove the session holder from the thread.

if (txObject.isNewSessionHolder()) {

TransactionSynchronizationManager.unbindResource(obtainSessionFactory());

}

// Remove the JDBC connection holder from the thread, if exposed.

if (getDataSource() != null) {

TransactionSynchronizationManager.unbindResource(getDataSource());

}

Session session = txObject.getSessionHolder().getSession();

if (this.prepareConnection && isPhysicallyConnected(session)) {

// We're running with connection release mode "on_close": We're able to reset

// the isolation level and/or read-only flag of the JDBC Connection here.

// Else, we need to rely on the connection pool to perform proper cleanup.

try {

Connection con = ((SessionImplementor) session).connection();

Integer previousHoldability = txObject.getPreviousHoldability();

if (previousHoldability != null) {

con.setHoldability(previousHoldability);

}

DataSourceUtils.resetConnectionAfterTransaction(

con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());

}

catch (HibernateException ex) {

logger.debug("Could not access JDBC Connection of Hibernate Session", ex);

}

catch (Throwable ex) {

logger.debug("Could not reset JDBC Connection after transaction", ex);

}

}

if (txObject.isNewSession()) {

if (logger.isDebugEnabled()) {

logger.debug("Closing Hibernate Session [" + session + "] after transaction");

}

SessionFactoryUtils.closeSession(session);

}

else {

if (logger.isDebugEnabled()) {

logger.debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction");

}

if (txObject.getSessionHolder().getPreviousFlushMode() != null) {

session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode());

}

if (!this.allowResultAccessAfterCompletion && !this.hibernateManagedSession) {

disconnectOnCompletion(session);

}

}

txObject.getSessionHolder().clear();

}

docommit里的逻辑还是用到了底层connection的commit,而在doCleanupAfterCompletion中,没有见到设置autocommit的身影。

所以在JPA中你在aftercommit中进行dml操作是会丢失的。

另外一个点是,如果你在aftercommit进行了事务操作,但是中间发生了异常,比如2条insert语句后,发生了异常,这两条insert会不会回滚?

答案是不会

回顾processCommit方法

private void processCommit(DefaultTransactionStatus status) throws TransactionException {

try {

//...

else if (status.isNewTransaction()) {

if (status.isDebug()) {

logger.debug("Initiating transaction commit");

}

unexpectedRollback = status.isGlobalRollbackOnly();

//...假设事务提交成功

doCommit(status);

}

try {

triggerAfterCommit(status);

}

finally {

triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);

}

}

finally {

cleanupAfterCompletion(status);

}

}

我们的aftercommit在triggerAfterCommit执行,这个方法里面抛出了异常,因为没有catch,异常会往上传递,在cleanupAfterCompletion里也没有处理异常,但是对于mybatis来讲,它改变了autocommit状态,所以更改被提交了。这是一个你想不到的坑。

最佳实践

aftercommit或者说是transactionlistener,最好不要有dml操作

一但aftercommit中有事务操作,存在的风险是,一致性得不到保证,异常不会让这部分的事务回滚

demo

写了一个工程,用于测试mybatis和jpa中对于aftercommit中执行dml操作是否会提交

地址如下

参考资料

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338905.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

序列化和反序列化的概念_序列化的概念

序列化和反序列化的概念讨论了为什么Optional不可序列化以及如何处理(即将推出)之后,让我们仔细看看序列化。 总览 这篇文章介绍了序列化的一些关键概念。 它尝试精简地执行此操作,而不会涉及太多细节,包括将建议降至…

C语言必学的12个排序算法:基数排序

# 基本思想基数排序(radix sort),同样时一种非比较的内部排序算法,主要基于多关键字排序的思想进行排序,它将单个关键字按照基数分成“多个关键字”进行排序。例如整数789是一个关键字,可以按照十进制位划分多关键字(十…

有没有code能改xml内容_Spring源码解析-applicationContext.xml加载和bean的注册

applicationContext文件加载和bean注册流程​ Spring对于从事Java开发的boy来说,再熟悉不过了,对于我们这个牛逼的框架的介绍就不在这里复述了,Spring这个大杂烩,怎么去使用怎么去配置,各种百度谷歌都能查到很多大牛教…

C语言数据类型转换

首先变量的数据类型是可以转换的。转换的方法有两种,一种是自动转换,另一种是强制转换。自动转换即当不同类型的数据进行混合运算时,编译系统将按照一定的规则自动完成。而强制类型转换是由程序员通过编程强制转换数据的类型。自动转换的规则…

java 类持久化_Java 持久化之 -- IO 全面整理(看了绝不后悔)

目录:一、java io 概述什么是IO?IO包括输入流和输出流,输入流指的是将数据以字符或者字节形式读取到内存 分为字符输入流和字符输入流输入流指的是从内存读取到外界 ,分为字符输入流和字节输出流Java IO即Java 输入输出系统。不管…

idea 找不到或无法加载主类_解决IDEA中Groovy项目no Groovy library is defined的问题

实验环境IDEA2019.1.2Groovy-2.5.8错误重现新建了一个Groovy工程,指定了groovy版本,如图新建了一个简单的Groovy Class,运行,出现如下错误错误的尝试(以下是试错过程,并不能解决问题,读者可以不要跟着操作&…

C语言标识符、关键字和注释

这一节主要讲解C语言中的几个基本概念。标识符定义变量时,我们使用了诸如“a”“abc”“mn123”这样的名字,它们都是程序员自己起的,一般能够表达出变量的作用,这叫做标识符(Identifier)。标识符就是程序员…

java 类.class_面试官:Java反射是什么?我回答不上来!

一.概念反射就是把Java的各种成分映射成相应的Java类。Class类的构造方法是private,由JVM创建。反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和…

巧解C语言运算符的优先级和结合性

本篇文章我们从一个例子入手讲解,请看下面的代码:#include int main(){ int a 16, b 4, c 2; int d a b * c; int e a / b * c; printf( "d%d, e%d\n", d, e); return 0; }运行结果:d24, e81) 对于表达式a b * c&#xff0…

jms mdb_MDB!= JMS,反之亦然

jms mdb基本 消息驱动Bean(又称为MDB)只是另一个EJB,例如无状态,有状态或单例。 使用MessageDriven批注指定。 MDB用于异步消息处理 它们与无状态EJB 相似 ,因为它们都是由EJB容器池化的 但是,它们与无状…

C语言中的三目运算符是啥?有何用处?

一般来说,C语言中的三目运算符为a?b:c即有三个参与运算的量。由条件运算符组成条件表达式的一般形式为:表达式1? 表达式2:表达式3求值规则为:如果表达式1的值为真,则以表达式2 的值作为条件表达式的值,否…

status_code想要得到302却得到200_中考200天倒计时!教你高效规划!抓紧抢报预留座位!...

教育点击右上方蓝字关注金石教育金石教育青岛站推送青岛教育资讯,关注孩子成长关注教育主讲人——段莲1、金石教育首席学习规划师:在中考规划行业,段莲老师已经深入研究多年,了解最新的中考数据,并且能够把握住每个数据…

gui jfr_Java飞行记录器(JFR)

gui jfrJFR是一个Java分析器,它使您可以研究代码的运行时特征。 通常,您将使用探查器来确定代码的哪些部分导致大量内存分配或导致消耗过多的CPU。 有很多产品在那里。 过去,我使用过YourKit,OptimizeIt,JProfiler&am…

java 命名内部类_如何把java SWT程序中的匿名内部类改写成命名内部类?

展开全部//请看最后一个e69da5e6ba9062616964757a686964616f31333363386134private class ,其实命名内部类跟普通的java程序一样写,只要知道你自己的监听器要继承于哪个类,实现什么方法public class Level4Class extends ViewPart {private TableViewer …

C语言 | 递增运算符

如何灵活使用C语言递增运算符。 解题思路:自增运算符 使操作数的值加1,其操作数必须为可变左值(可简单地理解为变量)。对于自增就是加1这一点,读者应该不会有什么疑问。难点在于: 可以置于操作数前面&#…

数据结构实验之图论四:迷宫探索_迷宫搜索类的双向bfs问题(例题详解)

前言文章若有疏忽还请指正!更多精彩还请关注公众号:bigsai头条号:一直码农一直爽在搜索问题中,以迷宫问题最具有代表性,无论是八皇后的回溯问题,还是dfs找出口,bfs找最短次数等等题目的问题。在…

ngrok服务器搭建_利用暴露在外的API,无法检测的Linux恶意软件将矛头指向Docker服务器...

K8s已经成为一线大厂分布式平台的标配技术。你是不是还在惆怅怎么掌握它?来这里,大型互联网公司一线工程师亲授,不来虚的,直接上手实战,3天时间带你搭建K8s平台,快速学会K8s,点击下方图片可了解…

干货 | C语言系列3——常量,运算符,常用数学函数......

符号常量和const常量1.符号常量符号常量通俗来讲就是“替换”,又称为宏定义。格式如下:#define 标识符 常量宏定义可以定义单个变量为常量,也可以定义某个语句或片段。宏定义有一点需要特别注意,它只是“机械”替换,并…

javafx 浏览器_浏览器中的JavaFX

javafx 浏览器浏览器中的JavaFX屏幕截图 最近,Carl Dea和我启动了一个新项目,将JavaFX 8引入浏览器。 今天,我想介绍我们创建的前两个概念验证,以查看该想法是否完全可行。 对于不耐烦的人,这里是到PoC的链接。 但请注…

javafx 示例_JavaFX列表示例

javafx 示例这是使用JavaFX构建的示例列表应用程序。 该应用程序是待办事项列表。 该应用程序具有添加,更新和删除列表中项目的功能。 列表数据存储在HSQLDB关系数据库中。 该应用程序使用JDBC(Java数据库连接)API访问数据库。 该应用程序打包…