使用spring框架实现数据库事务处理方式
使用spring框架实现数据库事务处理
事务对于数据库来说是,是对sql语句的一系列操作,这些操作被组织成为一个事务。事务具有原子性的,要么全部执行,要么不执行。若事务中sql语句发生错误,事务需要对已经执行的sql进行回滚操作,撤销先前对数据库的操作,防止数据库出现错误状态。
jdbc对数据库事务处理的支持
jdbc本身提供了对数据库处理的支持,使用java.sql.connection对象完成事务的提交。使用connection提交数据库事务处理如下:
发现里面有一个方法叫setautocommit(),设置为true时,就是自动提交sql,如果设置为false时,sql语句的提交由程序负责,应用程序必须调用commit方法,同时在执行sql语句异常处理块中调用rollback方法,对异常发生前进行的数据库进行回滚。
在企业级中,事务一般都是并发执行的,当事务并发执行时,就会发生数据库同步的问题,具体问题可分为下面四种类型:
- 脏读:
- 不可重复读:
- 丢失更新:
- 幻读:
为了解决上面的四种问题,解决事务并发的问题
jdbc定义了五种事务隔离级别来解决这些并发导致的问题
-
transaction_read_uncommitted
:数据级别最低,俗称脏读,在没有提交数据时,就已经读取到已经更新的数据。 -
transaction_read_committed
:当一个事务进行查询时,允许读取提交前的数据,数据提交后,当前查询就可以读取到数据,update数据的时候并不锁住表。(最长使用) -
transaction_repeatable_read
:当一个事务进行查询时,不允许读取其他事务更新的数据,可以读取其他事务新增的数据。 -
transaction_serializable
:当一个事务进行查询时,不允许任何对这个事务进行查询,就是把并行改成了串行。不提倡使用。
在spring框架中调用一个数据库事务处理分三步走:
- 第一步就是配置数据源就是配置数据库的名称和密码,地址等。
- 第二步就是声明事务管理transactionmanagerspring框架提供了platformtransactionmanager作为事务管理类的顶层接口,声明了初始化事务,提交事务,回滚事务等接口。接口实现由具体的数据库驱动类实现。具体包括datatransactionmanager等实现类。但是主要还是使用的是datatransactionmanager类。
- 定义可以执行事务的dao类dao提供了应用程序访问数据源必要的接口和方法。
spring 事务实现方式有哪些
编程式事务管理,
在代码中调用 commit()、rollback()等事务管理相关的方法
maven pom.xml文件
<dependency> <groupid>org.springframework</groupid> <artifactid>spring-beans</artifactid> <version>4.2.4.release</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> <version>4.2.4.release</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-aop</artifactid> <version>4.2.4.release</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-tx</artifactid> <version>4.2.4.release</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-jdbc</artifactid> <version>4.2.4.release</version> </dependency> <!-- mysql驱动 --> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> <version>5.1.18</version> </dependency>
编程式事务管理,可以通过 java.sql.connection 控制事务。spring 配置文件
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="driver" class="com.mysql.jdbc.driver"></bean> <bean id="datasource" class="org.springframework.jdbc.datasource.simpledriverdatasource"> <constructor-arg index="0" name="driver" ref="driver" /> <constructor-arg index="1"> <value>jdbc:mysql://localhost:3306/test</value> </constructor-arg> <constructor-arg index="2"> <value>root</value> </constructor-arg> <constructor-arg index="3"> <value>root</value> </constructor-arg> </bean> </beans>
测试代码
package constxiong.interview.transaction; import java.sql.connection; import java.sql.resultset; import java.sql.sqlexception; import java.sql.statement; import javax.sql.datasource; import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; public class transactiontest { public static void main(string[] args) throws exception { testmanualtransaction();//测试函数式控制事务 } private static void testmanualtransaction() throws sqlexception { applicationcontext context = new classpathxmlapplicationcontext("spring_transaction.xml"); datasource ds = (datasource)context.getbean("datasource"); connection conn = ds.getconnection(); try { inittable(conn);//初始化表 conn.setautocommit(false);//设置不自动提交事务 queryusers(conn);//查询打印用户表 deleteuser(conn);//删除 id=1 用户 conn.rollback();//回滚 queryusers(conn);//查询打印用户表 } finally { conn.close(); } } private static void inittable(connection conn) throws sqlexception { conn.createstatement().execute("drop table if exists user"); conn.createstatement().execute("create table user(id int, username varchar(60)) engine=innodb default charset=utf8 ");//是否支持事务与数据库引擎有关,此处删除 engine=innodb default charset=utf8 可能不支持事务 conn.createstatement().execute("insert into user values(1, 'user1')"); conn.createstatement().execute("insert into user values(2, 'user2')"); } private static void deleteuser(connection conn) throws sqlexception { conn.createstatement().execute("delete from user where id = 1"); } private static void queryusers(connection conn) throws sqlexception { statement st = conn.createstatement(); st.execute("select * from user"); resultset rs = st.getresultset(); while (rs.next()) { system.out.print(rs.getstring("id")); system.out.print(" "); system.out.print(rs.getstring("username")); system.out.println(); } } }
删除用户语句回滚,打印出两个用户
1 user1
2 user2
1 user1
2 user2
transactionproxyfactorybean 的声明式事务管理
新增 userdao 接口
package constxiong.interview.transaction; import java.util.list; import java.util.map; public interface userdao { /** * 查询用户 * @return */ public list<map<string, object>> getusers(); /** * 删除用户 * @param id * @return */ public int deleteuser(int id); }
新增 userdao 实现
package constxiong.interview.transaction; import java.util.list; import java.util.map; import org.springframework.jdbc.core.support.jdbcdaosupport; public class userdaoimpl extends jdbcdaosupport implements userdao { /** * 查询用户 * @return */ public list<map<string, object>> getusers() { string sql = "select * from user"; return this.getjdbctemplate().queryforlist(sql); } /** * 删除用户 * @param id * @return */ public int deleteuser(int id){ string sql = "delete from user where id = " + id; int result = this.getjdbctemplate().update(sql); if (id == 1) { throw new runtimeexception(); } return result; } }
修改 spring 配置文件,添加事务管理器 datasourcetransactionmanager 和事务代理类 transactionproxyfactorybean
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="driver" class="com.mysql.jdbc.driver"></bean> <bean id="datasource" class="org.springframework.jdbc.datasource.simpledriverdatasource"> <constructor-arg index="0" name="driver" ref="driver" /> <constructor-arg index="1"> <value>jdbc:mysql://localhost:3306/test</value> </constructor-arg> <constructor-arg index="2"> <value>root</value> </constructor-arg> <constructor-arg index="3"> <value>root</value> </constructor-arg> </bean> <bean id="userdao" class="constxiong.interview.transaction.userdaoimpl"> <property name="datasource" ref="datasource"></property> </bean> <!-- 事务管理器 --> <bean id="tracnsactionmanager" class="org.springframework.jdbc.datasource.datasourcetransactionmanager"> <property name="datasource" ref="datasource"></property> </bean> <bean id="userproxy" class="org.springframework.transaction.interceptor.transactionproxyfactorybean"> <property name="transactionmanager" ref="tracnsactionmanager"></property> <property name="target" ref="userdao"></property> <property name="transactionattributes"> <props> <!-- 主要 key 是方法 isolation_default 事务的隔离级别 propagation_required 传播行为 --> <!-- -exception 表示发生指定异常回滚,+exception 表示发生指定异常提交 --> <prop key="deleteuser">-java.lang.runtimeexception</prop> </props> </property> </bean> </beans>
测试代码
package constxiong.interview.transaction; import java.util.map; import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; public class transactiontest { static applicationcontext context = new classpathxmlapplicationcontext("spring_transaction.xml"); public static void main(string[] args) throws exception { testusetransactionproxy(); //测试使用 spring transactionproxyfactorybean } private static void testusetransactionproxy() { final userdao userdao = (userdao)context.getbean("userproxy"); printusers(userdao);//打印用户 userdao.deleteuser(1);//删除 id=1 用户 } private static void printusers(userdao userdao) { for (map<string, object> user : userdao.getusers()) { system.out.println(user); } } }
结果输出
{id=1, username=user1}
{id=2, username=user2}
exception in thread "main" java.lang.runtimeexception
at constxiong.interview.transaction.userdaoimpl.deleteuser(userdaoimpl.java:28)
at sun.reflect.nativemethodaccessorimpl.invoke0(native method)
at sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62)
at sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43)
at java.lang.reflect.method.invoke(method.java:498)
at org.springframework.aop.support.aoputils.invokejoinpointusingreflection(aoputils.java:302)
at org.springframework.aop.framework.reflectivemethodinvocation.invokejoinpoint(reflectivemethodinvocation.java:190)
at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:157)
at org.springframework.transaction.interceptor.transactioninterceptor$1.proceedwithinvocation(transactioninterceptor.java:99)
at org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:281)
at org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:96)
at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179)
at org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:208)
at com.sun.proxy.$proxy3.deleteuser(unknown source)
at constxiong.interview.transaction.transactiontest.testusetransactionproxy(transactiontest.java:32)
at constxiong.interview.transaction.transactiontest.main(transactiontest.java:13)
注解 @transactional 的声明式事务管理
userdaoimpl 删除用户方法添加注解 @transactional(rollbackfor=runtimeexception.class) 出现 runtimeexception 回滚
package constxiong.interview.transaction; import java.util.list; import java.util.map; import org.springframework.jdbc.core.support.jdbcdaosupport; import org.springframework.transaction.annotation.transactional; public class userdaoimpl extends jdbcdaosupport implements userdao { /** * 查询用户 * @return */ public list<map<string, object>> getusers() { string sql = "select * from user"; return this.getjdbctemplate().queryforlist(sql); } /** * 删除用户 * @param id * @return */ @transactional(rollbackfor=runtimeexception.class) public int deleteuser(int id){ string sql = "delete from user where id = " + id; int result = this.getjdbctemplate().update(sql); if (id == 1) { throw new runtimeexception(); } return result; } }
修改 spring 配置文件,开启 spring 的事务注解能力
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemalocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="driver" class="com.mysql.jdbc.driver"></bean> <bean id="datasource" class="org.springframework.jdbc.datasource.simpledriverdatasource"> <constructor-arg index="0" name="driver" ref="driver" /> <constructor-arg index="1"> <value>jdbc:mysql://localhost:3306/test</value> </constructor-arg> <constructor-arg index="2"> <value>root</value> </constructor-arg> <constructor-arg index="3"> <value>root</value> </constructor-arg> </bean> <bean id="userdao" class="constxiong.interview.transaction.userdaoimpl"> <property name="datasource" ref="datasource"></property> </bean> <!-- 事务管理器 --> <bean id="tracnsactionmanager" class="org.springframework.jdbc.datasource.datasourcetransactionmanager"> <property name="datasource" ref="datasource"></property> </bean> <!-- 启用事务注解 --> <tx:annotation-driven transaction-manager="tracnsactionmanager"/> </beans>
测试代码
package constxiong.interview.transaction; import java.util.map; import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; public class transactiontest { static applicationcontext context = new classpathxmlapplicationcontext("spring_transaction.xml"); public static void main(string[] args) throws exception { testannotationtransaction(); } private static void testannotationtransaction() { userdao userdao = (userdao)context.getbean("userdao"); printusers(userdao); userdao.deleteuser(1); } private static void printusers(userdao userdao) { for (map<string, object> user : userdao.getusers()) { system.out.println(user); } } }
输出结果
{id=1, username=user1}
{id=2, username=user2}
exception in thread "main" java.lang.runtimeexception
at constxiong.interview.transaction.userdaoimpl.deleteuser(userdaoimpl.java:30)
at sun.reflect.nativemethodaccessorimpl.invoke0(native method)
at sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62)
at sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43)
at java.lang.reflect.method.invoke(method.java:498)
at org.springframework.aop.support.aoputils.invokejoinpointusingreflection(aoputils.java:302)
at org.springframework.aop.framework.reflectivemethodinvocation.invokejoinpoint(reflectivemethodinvocation.java:190)
at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:157)
at org.springframework.transaction.interceptor.transactioninterceptor$1.proceedwithinvocation(transactioninterceptor.java:99)
at org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:281)
at org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:96)
at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179)
at org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:208)
at com.sun.proxy.$proxy5.deleteuser(unknown source)
at constxiong.interview.transaction.transactiontest.testannotationtransaction(transactiontest.java:20)
at constxiong.interview.transaction.transactiontest.main(transactiontest.java:13)
aspectj aop 配置(注解)事务
maven pom.xml 添加 aspectj 的支持
<dependency> <groupid>org.aspectj</groupid> <artifactid>aspectjweaver</artifactid> <version>1.8.13</version> </dependency>
去除 userdaoimpl 注解@transactional(rollbackfor=runtimeexception.class)
package constxiong.interview.transaction; import java.util.list; import java.util.map; import org.springframework.jdbc.core.support.jdbcdaosupport; public class userdaoimpl extends jdbcdaosupport implements userdao { /** * 查询用户 * @return */ public list<map<string, object>> getusers() { string sql = "select * from user"; return this.getjdbctemplate().queryforlist(sql); } /** * 删除用户 * @param id * @return */ public int deleteuser(int id){ string sql = "delete from user where id = " + id; int result = this.getjdbctemplate().update(sql); if (id == 1) { throw new runtimeexception(); } return result; } }
修改 spring 配置文件,织入切面
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemalocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="driver" class="com.mysql.jdbc.driver"></bean> <bean id="datasource" class="org.springframework.jdbc.datasource.simpledriverdatasource"> <constructor-arg index="0" name="driver" ref="driver" /> <constructor-arg index="1"> <value>jdbc:mysql://localhost:3306/test</value> </constructor-arg> <constructor-arg index="2"> <value>root</value> </constructor-arg> <constructor-arg index="3"> <value>root</value> </constructor-arg> </bean> <bean id="userdao" class="constxiong.interview.transaction.userdaoimpl"> <property name="datasource" ref="datasource"></property> </bean> <!-- 事务管理器 --> <bean id="tracnsactionmanager" class="org.springframework.jdbc.datasource.datasourcetransactionmanager"> <property name="datasource" ref="datasource"></property> </bean> <tx:advice id="txadvice" transaction-manager="tracnsactionmanager"> <tx:attributes> <!-- 为连接点指定事务属性 --> <tx:method name="deleteuser" rollback-for="java.lang.runtimeexception"/> </tx:attributes> </tx:advice> <aop:config> <!-- 切入点配置 --> <aop:pointcut id="point" expression="execution(* *constxiong.interview.transaction.userdao.deleteuser(..))" /> <aop:advisor advice-ref="txadvice" pointcut-ref="point"/> </aop:config> </beans>
测试代码
package constxiong.interview.transaction; import java.util.map; import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; public class transactiontest { static applicationcontext context = new classpathxmlapplicationcontext("spring_transaction.xml"); public static void main(string[] args) throws exception { testaspectjtransaction(); } private static void testaspectjtransaction() { userdao userdao = (userdao)context.getbean("userdao"); printusers(userdao); userdao.deleteuser(1); } private static void printusers(userdao userdao) { for (map<string, object> user : userdao.getusers()) { system.out.println(user); } } }
输出结果
{id=1, username=user1}
{id=2, username=user2}
exception in thread "main" java.lang.runtimeexception
at constxiong.interview.transaction.userdaoimpl.deleteuser(userdaoimpl.java:28)
at sun.reflect.nativemethodaccessorimpl.invoke0(native method)
at sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62)
at sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43)
at java.lang.reflect.method.invoke(method.java:498)
at org.springframework.aop.support.aoputils.invokejoinpointusingreflection(aoputils.java:302)
at org.springframework.aop.framework.reflectivemethodinvocation.invokejoinpoint(reflectivemethodinvocation.java:190)
at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:157)
at org.springframework.transaction.interceptor.transactioninterceptor$1.proceedwithinvocation(transactioninterceptor.java:99)
at org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:281)
at org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:96)
at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179)
at org.springframework.aop.interceptor.exposeinvocationinterceptor.invoke(exposeinvocationinterceptor.java:92)
at org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179)
at org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:208)
at com.sun.proxy.$proxy2.deleteuser(unknown source)
at constxiong.interview.transaction.transactiontest.testannotationtransaction(transactiontest.java:20)
at constxiong.interview.transaction.transactiontest.main(transactiontest.java:13)
ps:
这篇仅用事务回滚为例,了解 spring 事务控制,还需要关注数据库的acid四种特性、事务传播特性、事务的隔离级别(脏读、不可重复读、幻读)。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
上一篇: 安居客怎么绑定微信?安居客绑定微信教程
推荐阅读
-
使用Python的Django框架实现事务交易管理的教程
-
Laravel框架使用monolog_mysql实现将系统日志信息保存到mysql数据库的方法
-
使用Java实现数据库编程—05 事务、视图、索引、备份和恢复
-
Spring实战之使用TransactionProxyFactoryBean实现声明式事务操作示例
-
使用spring框架实现数据库事务处理方式
-
Spring数据库事务配置方式 Spring配置管理JDBCSQL框架
-
Spring数据库事务配置方式 Spring配置管理JDBCSQL框架
-
使用Spring框架实现RESTful
-
nodejs使用Sequelize框架操作数据库的实现
-
使用cglib实现数据库框架的级联查询