一、Spring的事务控制
- 分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。
- Spring提供了一组事务控制的接口。这组接口是在
spring-tx-4.2.4.RELEASE.jar
中。 - Spring的事务都是基于AOP的,既可以使用配置的方法实现,也可以使用注解的方法实现。
二、Spring事务控制的API介绍
-
PlatformTransactionManager
这个接口是spring的事务管理器,它里面提供了常用的操作事务的方法。- 获取事务状态信息
TransactionStatus getTransaction(TransactionDefinition definition)
- 提交事务
void commit(TransactionStatus status)
- 回滚事务
void rollback(TransactionStatus status)
- 一般都会用它的实现类
使用SpringJDBC或iBatis进行持久化数据时使用
DataSourceTransactionManage
- 获取事务状态信息
-
TransactionDefinition
事务的定义信息对象,方法如下:- 获取事务对象名称
Spring getName()
- 获取事务隔离级
int getIsolationLevel()
- 获取事务传播行为
int getPropagationBehavior()
- 获取事务超时时间
int getTimeout()
- 获取事务是否只读
boolean isReadOnly()
- 事务的传播行为
REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW
新建事务,如果当前在事务中,把当前事务挂起。
NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER
以非事务方式运行,如果当前存在事务,抛出异常
NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED
类似的操作。 - 超时时间
默认值是-1,没有超时限制。如果有,以秒为单位进行设置 - 是否是只读事务
建议查询时设置为只读
- 获取事务对象名称
-
TransactionStatus
这个接口提供的是事务具体的运行状态 -
刷新事务
void flush()
-
获取是否存在存储点
boolean hasSavepoint()
-
获取事务是否为新的事务
boolean isNewTransaction()
-
获取事务是否回滚
boolean isRollbackOnly()
-
设置事务回滚
void setRollbackOnly()
三、XML的声明式事务控制配置方式
1.所需jar包
jar包2.数据库
account数据库并创建对应的实体类
3.编写业务层接口和实现类
IStuService.java
package com.edu.service;
/**
* 业务层接口
*/
public interface IStuService {
/**
* 转账
* @param sourceName
* @param targetName
* @param money
*/
void transfer(String sourceName,String targetName,Float money);
}
StuServiceImpl.java
package com.edu.service.impl;
import com.edu.dao.IStuDao;
import com.edu.domain.Account;
import com.edu.service.IStuService;
/**
* 业务层实现类
*/
public class StuServiceImpl implements IStuService {
private IStuDao stuDao;
public void setStuDao(IStuDao stuDao) {
this.stuDao = stuDao;
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
//1.根据名称查询账户信息
Account account = stuDao.findAccountByName(sourceName);
Account target = stuDao.findAccountByName(targetName);
//2.转出账户减钱,转入账户加钱
account.setMoney(account.getMoney()-money);
target.setMoney(target.getMoney()+money);
//3.更新账户信息
stuDao.updateAccount(account);
int i=1/0;//异常,检验事务的作用
stuDao.updateAccount(target);
}
}
4.编写dao接口和实现类
package com.edu.dao;
import com.edu.domain.Account;
/**
* 持久层接口
*/
public interface IStuDao {
/**
* 根据名称查询账户信息
* @param targetName
* @return
*/
Account findAccountByName(String targetName);
/**
* 更新账户信息
* @param account
*/
void updateAccount(Account account);
}
package com.edu.dao.Impl;
import com.edu.dao.IStuDao;
import com.edu.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import java.util.List;
/**
* 持久层实现类
*/
public class StuImpl extends JdbcDaoSupport implements IStuDao {
@Override
public Account findAccountByName(String targetName) {
List<Account> accountList = getJdbcTemplate().query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), targetName);
if (accountList.isEmpty()) {
return null; //没有名称的账户
}
if (accountList.size() > 1) {
//结果集不唯一,不要
throw new RuntimeException("结果集不唯一,不要");
}
return accountList.get(0);
}
@Override
public void updateAccount(Account account) {
getJdbcTemplate().update("update account set name = ?,money = ? where cid = ?",account.getName(),account.getMoney(),account.getCid());
}
}
5.配置配置文件
bean.xml
<?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: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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置service -->
<bean id="stuService" class="com.edu.service.impl.StuServiceImpl">
<property name="stuDao" ref="stuDao"></property>
</bean>
<!-- 配置dao -->
<bean id="stuDao" class="com.edu.dao.Impl.StuImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置Spring内置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/goods"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- Spring基于XML的声明式事务控制 -->
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 3.配置AOP
配置切入点表达式
和
通知和切入点表达式的关联
-->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut id="pt1" expression="execution(* com.edu.service.impl.*.*(..))"/>
<!-- 配置事务通知和切入点表达式的关联 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
</beans>
6.测试类
package com.edu.springTemplate;
import com.edu.service.IStuService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IStuService stuService = (IStuService) ac.getBean("stuService");
/*Stu stu = stuService.findStuById(1);
System.out.println(stu);*/
stuService.transfer("aaa","bbb",100f);
}
}
如果没有事务,若异常,账户信息无法回滚,也就是会造成一个账户的钱转了出去,但另一个账户却没有受到。但是有了事务,若有异常就会回滚,账户操作就会回到之前。
四、基于XML和注解组合使用的声明式事务配置
在上述案例基础上只需要修改
1.业务层实现类
package com.edu.service.impl;
import com.edu.dao.IStuDao;
import com.edu.domain.Account;
import com.edu.service.IStuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("stuService")
@Transactional
public class StuServiceImpl implements IStuService {
@Autowired
private IStuDao stuDao;
@Override
public void transfer(String sourceName, String targetName, Float money) {
//1.根据名称查询账户信息
Account account = stuDao.findAccountByName(sourceName);
Account target = stuDao.findAccountByName(targetName);
//2.转出账户减钱,转入账户加钱
account.setMoney(account.getMoney()-money);
target.setMoney(target.getMoney()+money);
//3.更新账户信息
stuDao.updateAccount(account);
int i=1/0;
stuDao.updateAccount(target);
}
}
2.Dao层实现类
package com.edu.dao.Impl;
import com.edu.dao.IStuDao;
import com.edu.domain.Account;
import com.edu.domain.Stu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("stuDao")
public class StuImpl implements IStuDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Stu findStuById(Integer sid) {
List<Stu> list = jdbcTemplate.query("select * from stu where sid = ?", new BeanPropertyRowMapper<Stu>(Stu.class), 1);
return list.isEmpty() ? null : list.get(0);
}
@Override
public Account findAccountByName(String targetName) {
List<Account> accountList = jdbcTemplate.query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), targetName);
if (accountList.isEmpty()) {
return null; //没有名称的账户
}
if (accountList.size() > 1) {
//结果集不唯一,不要
throw new RuntimeException("结果集不唯一,不要");
}
return accountList.get(0);
}
@Override
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name = ?,money = ? where cid = ?",account.getName(),account.getMoney(),account.getCid());
}
}
3.配置配置类
bean.xml
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.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">
<!-- 配置Spring创建容器时要扫描的包 -->
<context:component-scan base-package="com.edu"></context:component-scan>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置Spring内置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/goods"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- Spring基于XML和注解组合的声明式事务控制 -->
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.配置spring开启注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!-- 3.在需要使用事务的地方使用@Transactional
这个注解可以写在接口,类上和方法上
写在接口上,表明接口的所有实现类都有事务
写在类上,表示该类的所有方法都有事务
写在方法上,表示该方法有事务
优先级:就近原则
-->
</beans>
测试类如上
效果也如上
五、基于纯注解的声明式事务配置
在基于XML和注解组合使用的声明式事务配置的基础上将xml文件种的配置转为注解
1.使用注解配置数据源
jdbcConfig.java
package com.edu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
/**
* 使用注解
* 配置JdbcTemplate
* 配置数据源
*/
public class JdbcConfig {
//配置JdbcTemplate
@Bean(name = "jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
//配置数据源
@Bean(name = "dataSource")
public DataSource createDataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/goods");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
2.配置事务管理器
TransactionManager.java
package com.edu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
/**
* 事务控制配置类
*/
public class TransactionManager {
@Bean(name = "transactionManager")
public PlatformTransactionManager createTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
3.创建一个类用于加载spring的配置并指定要扫描的包
SpringConfig.java
package com.edu.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Spring的配置类,作用就是当bean.xml用
*/
@Configuration
@ComponentScan("com.edu")
@Import({JdbcConfig.class,TransactionManager.class})
@EnableTransactionManagement
public class SpringConfig {
}
4.测试类
package com.edu.springTemplate;
import com.edu.config.SpringConfig;
import com.edu.service.IStuService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
IStuService stuService = (IStuService) ac.getBean("stuService");
stuService.transfer("aaa","bbb",100f);
}
}
网友评论