事务的特性(ACID):
A: Atomic 原子性
表示组成一个事务的多个对数据库的操作为一个不可分割的单元, 只有所有的操作都成功才算成功, 整个事务才会提交, 其中任何一个操作失败了都会导致整个操作失败, 事务则会回滚
C: Consistency 一致性
事务操作成功后, 数据库所处的状态和业务规则是一致(不变)的, 如果A账户给B账户汇100, 则A账户要减去100, B账户要加上100 两个账户的总额是不变的
I: Isolation 隔离性
在多个对数据库操作相同的数据并发时, 不同的事务有自己的数据空间, 事务与事务之间不受干扰(不是绝对的), 干扰程度受数据库或者操作事务的隔离级别来决定, 隔离级别越高, 干扰就越低, 数据的一致性就越好, 而并发性则越差.
D: Durability 持久性
一旦事务提交成功, 数据就被持久化到数据库, 不可以回滚, 重启/关机都不会丢失数据了.
原子性由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: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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<!-- 配置属性文件的位置 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<!-- 采用${xxx}的形式读取属性文件中对应key的内容 -->
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${uname}"></property>
<property name="password" value="${pword}"></property>
<!-- 初始化连接数 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 最大连接数 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 最小空闲连接数 -->
<property name="minIdle" value="${minIdle}"></property>
</bean>
<bean id="orderDao" class="com.rl.spring.dao.impl.OrderDaoImpl">
<property name="ds" ref="dataSource"></property>
</bean>
<bean id="detailDao" class="com.rl.spring.dao.impl.DetailDaoImpl">
<property name="ds" ref="dataSource"></property>
</bean>
<bean id="orderService" class="com.rl.spring.service.impl.OrderServiceImpl">
<property name="orderDao" ref="orderDao"></property>
</bean>
<bean id="detailService" class="com.rl.spring.service.impl.DetailServiceImpl">
<property name="orderDao" ref="orderDao"></property>
<property name="detailDao" ref="detailDao"></property>
</bean>
<!-- 定义事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理的注解驱动 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
下面只粘贴实现类的代码
OrderDaoImpl:
package com.rl.spring.dao.impl;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import com.rl.spring.dao.OrderDao;
import com.rl.spring.model.Order;
public class OrderDaoImpl implements OrderDao {
private DataSource ds;
private JdbcTemplate jt;
public void setDs(DataSource ds) {
this.ds = ds;
this.jt = new JdbcTemplate(ds);
}
@Override
public int saveOrder(Order order) {
String sql = "insert into t_order values(null, ?)";
jt.update(sql, new Object[] {order.getTotalPrice()});
return jt.queryForInt("SELECT LAST_INSERT_ID()");//该行代码的作用是让数据库返回主键
}
}
DetailDaoImpl
package com.rl.spring.dao.impl;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import com.rl.spring.dao.DetailDao;
import com.rl.spring.model.Detail;
import com.rl.spring.model.Order;
public class DetailDaoImpl implements DetailDao {
private DataSource ds;
private JdbcTemplate jt;
public void setDs(DataSource ds) {
this.ds = ds;
this.jt = new JdbcTemplate(ds);
}
@Override
public void saveDetail(Detail detail) {
String sql = "insert into t_detail values(null, ?, ?, ?)";
jt.update(sql, new Object[] {detail.getItemName(), detail.getQuantity(), detail.getOrderId()});
}
}
OrderServiceImpl(较简单, 省略粘贴上来)
DetailServiceImpl:
package com.rl.spring.service.impl;
import org.springframework.transaction.annotation.Transactional;
import com.rl.spring.dao.DetailDao;
import com.rl.spring.dao.OrderDao;
import com.rl.spring.model.Detail;
import com.rl.spring.model.Order;
import com.rl.spring.service.DetailService;
public class DetailServiceImpl implements DetailService {
DetailDao detailDao;
OrderDao orderDao;
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void setDetailDao(DetailDao detailDao) {
this.detailDao = detailDao;
}
@Transactional//事务管理注解
@Override
public void saveOrderAndDetail(Order order, Detail detail) {
Integer orderId = orderDao.saveOrder(order);
detail.setOrderId(orderId);
int i = 1/0;//设置运行期异常, 由于配置了Transactional注解, 所以事务会全部回滚
detailDao.saveDetail(detail);
}
}
测试代码:
package com.rl.spring.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.rl.spring.model.Detail;
import com.rl.spring.model.Order;
import com.rl.spring.service.DetailService;
import com.rl.spring.service.OrderService;
@RunWith(value=SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:ApplicationContext.xml"})
public class TestSpring {
@Autowired
OrderService orderService;
@Autowired
DetailService detailService;
@Test
public void test() {
Order order = new Order();
order.setTotalPrice(100);
Detail detail = new Detail();
detail.setItemName("HUAWEI");
detail.setQuantity(1);
detailService.saveOrderAndDetail(order, detail);
}
}
@Transactional注解的本质是事务的传播特性, 下面详讲事务的传播特性
Spring的传播特性
Required传播特性(80%以上使用该传播特性)
业务方法需要在一个事务中运行, 如果一个方法已经处在一个事务中那么就加入到这个事务中, 否则就会创建一个新事务
如下图:
传播特性可配置:
Never传播特性(极少使用):
指定的业务方法绝对不能在事务中运行, 如果在事务中运行了, 则会抛异常, 只有业务方法没有事务才会正常执行.
MANDATORY传播特性:
与Never相反, 只能在一个已经存在的事务中执行, 不能自己发起事务, 如果业务方法没有事务的情况下, 则抛异常
SUPPORTS传播特性:
如果业务方法已经在某个事务中被调用, 则业务方法就成为事务的一部分, 如果业务方法没有在某个事务中被调用, SUPPORTS也支持该业务方法的执行(一句话, 开事务我就用事务, 不开事务我就不用事务, 但都可以执行)
NOT_SUPPORTED传播特性:
永远不支持事务(在有事务中该事务会被挂起)
REQUIRES_NEW传播特性:
永远使用自己创建的事务(如果已经存在事务则挂起它, 自己新创建), 假如自己new出来的这个事务回滚了, 是不会影响到另一个被挂起的事务的.
NESTED传播特性:
主要区分于内外部事务, 如果内部事务做回滚, 是不会影响到外部事务的(因为NESTED会设置一个事务保存点, 回滚到该保存点后继续执行外部事务); 而如果是外部事务做回滚, 该内外部事务全部回滚.
这点跟REQUIRES_NEW有区别, REQUIRES_NEW是不管是否内外部事务都互不影响.
====================================================
Spring系列暂时写到这里, 后续抽时间更一下事务的隔离级别, 接下来会更Hibernate系列.