事务是正确执行一系列的操作(或动作),使数据库从一种状态转换为另一种状态,且保证操作全部成功,或者全部失败。
事务必须遵循ACID原则:
传播行为、隔离规则、回滚规则、事务超时、是否只读、传播行为
public interface TransactionDefinition {
// 返回数据库的传播行为
int getPropgationBehavior();
// 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
int getIsolationLevel();
// 返回事务必须在多少秒内完成
int getTimeout();
// 判断事务是否可读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
boolean isReadOnly();
}
隔离级别定义了一个事务可能受其他并发事务影响的程度。
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读
保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
当事务方法被另一个事务方法调用时,必须制定事务如何传播。
在TransactionDefinition接口中定义了七个事务传播行为。
如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
总是非事务地执行,并挂起任何存在的事务。
总是非事务地执行,如果存在一个活动事务,则抛出异常
如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待结束。
设计事务时注意点:
为了使应用程式很好的运行,事务不能运行太长的时间。因为事务可能涉及到对后端数据库的锁定,所以长时间的食物会不必要的占用数据库资源。
默认情况下,事务只有遇到运行期异常时才不会回滚,而在遇到检查型异常时不会回滚。
通过事务管理器获取TransactionStatus实例。
控制事务在回滚提交的时候需要应用相应的状态。
Spring事务接口
// Spring事务窗台接口
// 通过调用PlatfotmTransationManager的getTransaction()
// 获取事务状态实例
public interface TransationStatus {
boolean isNewTransaction(); // 是否是新的事务
boolean hasSavepoint(); // 是否恢复点
void setRollbackOnly(); // 设置为只回滚
boolean isRollbackOnly(); // 是否为只回滚
boolean isCompleted(); // 是否已完成
}
第一步:创建相关数据表
CREATE TABLE `books` (
`isbn` varchar(18) NOT NULL,
`name` varchar(64) NOT NULL,
`price` float(10,2) NOT NULL,
`pubdate` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
第二步:创建JavaWeb项目(Maven)
第三步:配置Maven依赖
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-expressionartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.9.1version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.1version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>4.3.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-dbcp2artifactId>
<version>2.1.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.19version>
第四步:创建数据库操作类
src/com/mooc/utils/TemplateUtils.java
/**
* Spring数据库操作工具类
*/
public class TemplateUtils {
private final static String dbDriver = "com.mysql.jdbc.Driver" ;
private final static String dbUrl = "jdbc:mysql://127.0.0.1:3306/test" ;
private final static String dbUser = "root";
private final static String dbPwd = "root";
private static BasicDataSource dataSource ;
//静态初识:创建连接数据源
static {
//创建DBCP简单数据源并初始化相关数据源属性
//private void createSimpleDataSource(){
dataSource = new BasicDataSource() ;
dataSource.setDriverClassName(dbDriver);
dataSource.setUrl(dbUrl);
dataSource.setUsername(dbUser);
dataSource.setPassword(dbPwd);
//指定数据库连接池初始连接数
dataSource.setInitialSize(10);
//设定同时向数据库申请的最大连接数
dataSource.setMaxTotal(50);
//设置连接池中保持的最少连接数量
dataSource.setMinIdle(5);
//}
}
public static TransactionTemplate getTransactionTemplate() {
PlatformTransactionManager txManager = new DataSourceTransactionManager(
dataSource);
return new TransactionTemplate(txManager);
}
public static JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(dataSource);
}
public static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return new NamedParameterJdbcTemplate(dataSource);
}
public static SimpleJdbcInsert getSimpleJdbcTemplate() {
return new SimpleJdbcInsert(dataSource);
}
/**
* //获取事务管理器:TransactionManager
* 根据需要,可以是如JDBC、Hibernate,这里定义JDBC事务管理其
* @return DataSourceTransactionManager
*/
public static DataSourceTransactionManager getDataSourceTransactionManager(){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
// 设置数据源:此事务数据源须和正式事务管理器的数据源一致
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
第五步:创建事务类
src/com/mooc/springtransactions/TransManagerExample.java
public class TransManagerExample {
public static void main(String[] args) {
// 第一步:获取JDBC事务管理器
DataSourceTransactionManager dtm = TemplateUtils.getDataSourceTransactionManager();
// 第二步:创建事务管理器属性对象
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
// 根据需要,设置事务管理器的相关属性
transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
// 第三步:获得事务状态对象
TransactionStatus ts = dtm.getTransaction(transDef);
// 第四步:基于当前事务管理器,获取操作数据库的JDBC模板对象
JdbcTemplate jt = new JdbcTemplate(dtm.getDataSource());
try {
// 第五步:执行业务方法
jt.update("update books set price=112.5,name='炎黄传奇' where isbn='128-166-890-China' ");
// 第六步:提交业务
//其它数据操作如增删
dtm.commit(ts); //如果不commit,则更新无效果
} catch (Exception e) {
//回滚操作
dtm.rollback(ts);
e.printStackTrace();
}
}
}
基于AOP模式机制,对方法前后进行拦截。
独立代理,共享代理,拦截器,tx拦截器,全注释。(前三种不推荐使用)
配置文件方式,注解方式
第一步:
创建目标服务类:
src/main/java/com/company/service/DefaultFooService.java
package com.company.service;
import com.company.beans.Foo;
public class DefaultFooService implements FooService {
@Override
public Foo getFoo(String name) {
Foo f = new Foo();
f.setName(name);
f.setLevel(8);
f.setBarName("默认吧名:Q吧");
return f;
// throw new UnsupportedOperationException();
}
@Override
public Foo getFoo(String name, String barname) {
throw new UnsupportedOperationException();
}
@Override
public void insertFoo(Foo foo) {
throw new UnsupportedOperationException();
}
@Override
public void updateFoo(Foo foo) {
throw new UnsupportedOperationException();
}
}
第二步,配置连接源
src/main/resources/database.properties
#DBCP数据库连接池配置属性详细内容可参考官网描述:
#http://commons.apache.org/proper/commons-dbcp/configuration.html
#dsName = defaultDataSource
#连接设置
username=root
password=root
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
#connectionProperties,跟进需要,可参看官方说明进行详细配置:
#
initialSize=10
#
maxIdle=20
#
minIdle=5
#最大连接数量
maxActive=50
#是否在自动回收超时连接的时候打印连接的超时错误
logAbandoned=true
#是否自动回收超时连接
removeAbandoned=true
#超时时间(以秒数为单位)
#设置超时时间有一个要注意的地方,超时时间=现在的时间-程序中创建Connection的时间,如果maxActive比较大,比如超过100,那么removeAbandonedTimeout可以设置长一点比如180,也就是三分钟无响应的连接进行回收,当然应用的不同设置长度也不同。
removeAbandonedTimeout=180
#
#maxWait代表当Connection用尽了,多久之后进行回收丢失连接
maxWait=1000
第三步,配置Spring声明式事务
src/main/resources/springContextExample.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:database.properties" />
bean>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<bean id="fooService" class="com.company.service.DefaultFooService"/>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* com.company.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
aop:config>
beans>
第六步,进行测试
src/main/java/com/company/examples/Main.java
public class Main {
public static void main(final String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("/springContextExample.xml");
System.out.println(ctx);
FooService fooService = (FooService) ctx.getBean("fooService");
System.out.println(fooService.getFoo("123"));
}
}
第一步:定义目标服务类
com/company/service/XbeanServiceImpl.java
@Transactional
public class XbeanServiceImpl implements XbeanService{
@Override
public Xbean getXbean(int id) {
Xbean xb = new Xbean() ;
xb.setName("业务Bean的ID="+id);
xb.setName("Bean默认名称");
return xb ;
}
@Override
public void insertXbean(Xbean xb) {
throw new UnsupportedOperationException();
}
}
第二步:添加注解声明式事务的支持
src/main/resources/springContextExample.xml
<tx:annotation-driven transaction-manager="txManager" />
第三步,定义目标类的bean
<bean id="xbeanService" class="com.company.service.XbeanServiceImpl"/>
第四步,编写测试方法
public static void main(final String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("/springContextExample.xml");
System.out.println(ctx);
XbeanService xbeanService = (XbeanService)ctx.getBean("xbeanService");
out.println( xbeanService.getXbean(123));
}
Spring将事务管理分为了两类:
编程式事务管理
声明式事务管理:
基于TransactionProxyFactoryBean的方式(很少使用)
基于AspectJ的方式(常使用)