1.事务
事务:事务是一组操作的执行单元,相对于数据库操作来讲,事务管理是一组SQL指令.
且,事务的一致性要求这个事务内的操作必须全部执行成功,如果在此过程出现异常,如其中的一条SQL执行失败,那么这组事务(这组操作)就会回滚.
事务特性:
Atomic(原子性):事务是不可再分的一组操作.(要么都成功,要么都失败)
COnsistent(一致性):数据不应被破坏
Isolate(隔离性):用户与用户间的操作是互不影响的
Durable(持久性):事务执行成功后,数据持久化
2.Spring声明式事务管理
控制事务的方式:方式1:编程式事务管理
JDBC:
Connection con;//对象
con.setAutoCommite(false);//设置手动提交事务
Hibernate:
session.beginTransaction();//开启事务
session.getTransaction.commit()//提交事务
编程式事务的特点:
1.优点:可以精确地控制事务
2.缺点:每次都要手写事务代码,代码冗余
方式2:声明式事务控制
Spring框架提供声明式事务管理控制.
Spring提供事务的切面类,只需通过事务配置,就可以利用Aop功能,给指定的方法添加事务.
事务切面类:(Spring提供)
Jdbc事务控制:DataSourceTransactionManager
Hibernate事务控制:HibernateTransactionManager
特点:
1.Spring声明式事务管理,一定要用AOP编程
2.Aop连接的是方法(而不是方法中的某几行代码)
3.粗粒度的事务控制(事务控制不精确)
优点:
1.事务控制只需配置即可,对程序的结构没有影响
2.Spring的解耦体现
让事务与业务代码脱离
3.提高开发效率(无需手写事务代码)
*******案例**********
*****1.Jdbc声明式事务管理,set方法注入对象********
// 各接口略
//1.dao类
public class UserDao implements IUserDao{//dao类
// 注入JdbcTemplate
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void save() {
jdbcTemplate.update("insert into t_user(name)values('test')");
}
}
//2.service类
public class UserService implements IUserService {//service类
// 注入Dao; 注意:如果当期加入容器的service是代理对象,这里一定要用接口!
private IUserDao userDao;
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
//... 开启事务
@Override
public void save() {
userDao.save();
int i=1/0;
userDao.save();
}
}
<!-- 配置 -->
<!-- 1. 创建DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///day33"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="6"></property>
</bean>
<!-- 2. 创建JdbcTemplate, 注入DataDataSource -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3. 创建dao实例,注入JdbcTemplate -->
<bean id="userDao" class="cn.itcast.a_tx_jdbc.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 4. 创建Service实例, 注入dao -->
<bean id="userService" class="cn.itcast.a_tx_jdbc.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
<!--
5. Spring声明式事务管理配置
-->
<!-- 5.1 配置事务管理器类 (切面类) -->
<!-- (里面实现了事务的重复代码!) -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5.2 配置事务通知, 指定事务管理器类-->
<!-- 拦截到方法后,对不同名称的方法,如何进行事务控制,如只读、读写? -->
<!-- save* 以save开头的方法,进行的事务管理的方式,如读写 -->
<!-- * 放到最后; 上面所有的方法都不满足的时候,应用的事务管理方式,如只读!-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" read-only="false" />
<tx:method name="update*" read-only="false"/>
<tx:method name="delete*" read-only="false"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 5.3 Aop配置 = 切入点表达式 + 应用通知 -->
<!-- 通过切入点表达式,对指定的方法生成代理对象且进行拦截; 拦截到后应用事务通知;在由事务通知应用事务管理器事务的实现! -->
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.a_tx_jdbc.*Service.*(..))" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
*结果/现象
/*service类中的save()方法包含:
userDao.save();
int i=1/0;
userDao.save();
第一条语句执行成功,但数据并未持久化;
第二条语句执行失败,程序异常,因为是在事务中发生的异常,所以所有事务内的操作全部回滚.即第一条语句的执行结果无效.
第三条语句不执行.
*/
*****2.Jdbc声明式事务管理,注解注入对象********
// 接口略
@Repository // 对象加入ioc容器
public class UserDao implements IUserDao{
// 自动从容器找名称是"jdbcTemplate"对应的bean!
@Resource
private JdbcTemplate jdbcTemplate;
public void save() {
jdbcTemplate.update("insert into t_user(name)values('test')");
}
}
// 模拟插入日志的操作
@Service
public class LogService {
@Resource
private JdbcTemplate jdbcTemplate;
// REQUIRES_NEW 表示无论执行当前save方法有没有事务环境,都会开启一个独立的事务!
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void save(){
jdbcTemplate.update("insert into t_log values('提示:正在执行保存!')");
}
}
@Service
public class UserService implements IUserService {
@Resource
private IUserDao userDao;
@Resource
private LogService logService;
// 给当前方法应用是事务!
@Transactional(
readOnly=false, // 读写的事务; 对数据有修改的时候必须使用这个属性!
isolation=Isolation.DEFAULT, // 数据库默认的隔离级别!
//noRollbackFor=ArithmeticException.class, // 遇到指定的异常不回滚!
timeout=-1, // 事务的超时时间(S), -1 表示事务不设置超时时间!
propagation=Propagation.REQUIRED // 默认值,表示当前方法必须在事务环境运行!
)
public void save() {
// 先插入日志
logService.save();
int i =1/0;
// 执行业务处理
userDao.save();
}
}
<!-- 1. 创建DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///day33"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="6"></property>
</bean>
<!-- 2. 创建JdbcTemplate, 注入DataDataSource -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3. 开启注解扫描 -->
<context:component-scan base-package="cn.itcast.b_tx_jdbc_anno"></context:component-scan>
<!-- 4. Spring声明式事务管理, 注解方式实现 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务的注解扫描 -->
<tx:annotation-driven transaction-manager="txManager"/>
/* //测试,结果/现象
public void save() {
// 先插入日志
logService.save();//次条语句是在一个事务内
int i =1/0;//程序异常
// 执行业务处理
userDao.save();//次条语句在另一事务内
//所以,第一条语句的执行结果不会回滚;而第二条语句不执行.
}
*/
3. 事务细节
a. 引用事务名称空间org.springframework.jdbc.datasource.DataSourceTransactionManager
org.springframework.orm.hibernate3.HibernateTransactionManager
b.使用@Transactional注解
@Transactional定义到方法上,表示当前执行的方法应用事务!
@Transactional定义到类上,表示当前类的所有方法应用事务!
@Transactional定义到父上,表示执行父类的方法时候应用事务!
c.事务属性:
propagation属性:
Never 当前业务方法不能再事务内执行!否则报错!
即不应用事务!
SUPPORTS 当前执行方法有事务则支持!没有事务也可以运行!
适合查询操作!
REQUIRED 当前方法必须在事务环境运行!
如果当前方法已经在事务环境运行,那么当前方法就不开启一个新的事务!
如果当前方法没有再事务环境中运行,那么会开启一个新的事务!
REQUIRESNEW 当前方法必须在事务环境运行!
无论当前方法是否在事务环境中运行,当前方法都会开启一个新的事务!
readOnly=false, // 读写的事务; 对数据有修改的时候必须使用这个属性!
isolation=Isolation.DEFAULT, // 数据库默认的隔离级别!
//noRollbackFor=ArithmeticException.class, // 遇到指定的异常不回滚!
timeout=-1,// 事务的超时时间(S), -1 表示事务不设置超时时间!
*若有不足或错误,请多多指教,谢谢!*