14.1spring事务管理
14.1.1 事务定义
在程序中,尤其是在操作数据库的程序中,指的是访问并且可能更新数据库中数据项的一个执行单元(unit),
这个执行单元由事务开始(begin transaction)和事务结束(end transaction)之间执行的全部操作组成
14.1.2 事务ACID原则
一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做
事务必须是使得数据库状态从一个一致性状态,转变到另外一个一致性状态。
也就是说在事务前,和事务后,被操作的目标资源状态一致。
比如银行转账案例中,转账前和转账后,总账不变。
一个事务的执行不能被其他事务的影响。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,
多个并发事务之间不能相互干扰
一个事务一旦提交,它对数据库中数据的改变会永久存储起来其他操作不会对它产生影响。
14.2 spring声明式事务管理
14.2.1 项目目录(xml配置)
14.2.2 编写实体类
package cn.guardwhy.domain;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
private int id;
private String name;
private Float money;
}
14.2.3 结果集映射类
package cn.guardwhy.rowmapper;
import cn.guardwhy.domain.Account;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
public class AccountRowMapper implements RowMapper<Account>{
public Account mapRow(ResultSet rs, int index) throws SQLException {
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getFloat("money"));
return account;
}
}
14.2.4 持久层(Dao)
package cn.guardwhy.dao;
import cn.guardwhy.domain.Account;
public interface AccountDao {
Account findAccountById(Integer accountId);
Account findAccountByName(String accountName);
void updateAccount(Account account);
}
package cn.guardwhy.dao.impl;
import cn.guardwhy.dao.AccountDao;
import cn.guardwhy.domain.Account;
import cn.guardwhy.rowmapper.AccountRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class AccountDaoImpl implements AccountDao{
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Account findAccountById(Integer accountId) {
List<Account> list = jdbcTemplate.query("select * from account where id=?",
new AccountRowMapper(), accountId);
return list.isEmpty()?null:list.get(0);
}
public Account findAccountByName(String accountName) {
List<Account> list = jdbcTemplate.query("select * from account where name=?",
new AccountRowMapper(), accountName);
if(list.isEmpty()){
return null;
}
if(list.size()>1){
throw new RuntimeException("账户不唯一,请检查!");
}
return list.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set money=?
where id=?",account.getMoney(),account.getId());
}
}
14.2.5 业务层(Service)
package cn.guardwhy.service;
import cn.guardwhy.domain.Account;
public interface AccountService {
Account findAccountById(Integer accountId);
void transfer(String sourceName,String destName,Float money);
}
package cn.guardwhy.service.impl;
import cn.guardwhy.dao.AccountDao;
import cn.guardwhy.domain.Account;
import cn.guardwhy.service.AccountService;
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void transfer(String sourceName, String destName, Float money) {
Account sourceAccount = accountDao.findAccountByName(sourceName);
Account destAccount = accountDao.findAccountByName(destName);
sourceAccount.setMoney(sourceAccount.getMoney()-money);
destAccount.setMoney(destAccount.getMoney()+money);
accountDao.updateAccount(sourceAccount);
accountDao.updateAccount(destAccount);
}
}
14.3 声明式事务配置
14.3.1配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
14.3.2 配置aop和切入点表达式
<aop:config>
<aop:pointcut id="pt1" expression="execution(* cn.guardwhy.service..*.*(..))"> </aop:pointcut>
</aop:config>
14.3.3 建立通知与切入点表达式关系
<aop:config>
<aop:pointcut id="pt1" expression="execution(* cn.guardwhy.service..*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
14.3.4 配置通知
<tx:advice id="txAdvice" transaction-manager="transactionManager">
</tx:advice>
14.3.5 配置事务属性
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
14.3.6 配置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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="accountService" class="cn.guardwhy.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<bean id="accountDao" class="cn.guardwhy.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="initialSize" value="6" />
<property name="minIdle" value="3" />
<property name="maxActive" value="50" />
<property name="maxWait" value="60000" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* cn.guardwhy.service..*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED" read-only="false" />
</tx:attributes>
</tx:advice>
</beans>
14.3.7 表现层(Controller)
package cn.guardwhy.controller;
import cn.guardwhy.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AccountController {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
AccountService accountService = (AccountService)context.getBean("accountService");
accountService.transfer("curry","james",200f);
}
}
14.4 xml和注解声明式事务配置
14.4.1项目目录
14.4.2 持久层(Dao)
package cn.guardwhy.dao.impl;
import cn.guardwhy.dao.AccountDao;
import cn.guardwhy.domain.Account;
import cn.guardwhy.rowmapper.AccountRowMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao{
@Autowired
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Account findAccountById(Integer accountId) {
List<Account> list = jdbcTemplate.query("select * from account where id=?", new AccountRowMapper(), accountId);
return list.isEmpty()?null:list.get(0);
}
public Account findAccountByName(String accountName) {
List<Account> list = jdbcTemplate.query("select * from account where name=?", new AccountRowMapper(), accountName);
if(list.isEmpty()){
return null;
}
if(list.size()>1){
throw new RuntimeException("账户不唯一,请检查!");
}
return list.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set money=? where id=?",account.getMoney(),account.getId());
}
}
14.4.3 业务层(Service)
package cn.guardwhy.service.impl;
import cn.guardwhy.dao.AccountDao;
import cn.guardwhy.domain.Account;
import cn.guardwhy.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("accountService")
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
public void transfer(String sourceName, String destName, Float money) {
Account sourceAccount = accountDao.findAccountByName(sourceName);
Account destAccount = accountDao.findAccountByName(destName);
sourceAccount.setMoney(sourceAccount.getMoney()-money);
destAccount.setMoney(destAccount.getMoney()+money);
accountDao.updateAccount(sourceAccount);
accountDao.updateAccount(destAccount);
}
}
14.4.4 配置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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.guardwhy"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="initialSize" value="6" />
<property name="minIdle" value="3" />
<property name="maxActive" value="50" />
<property name="maxWait" value="60000" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven/>
</beans>
14.4.5 表现层(Controller)
package cn.guardwhy.controller;
import cn.guardwhy.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AccountController {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
AccountService accountService = (AccountService)context.getBean("accountService");
accountService.transfer("curry","james",200f);
}
}
14.4.6 执行结果
14.5 注解声明式事务配置
14.5.1 项目目录
14.5.2 持久层配置类
package cn.guardwhy.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
public class DaoConfiguration {
@Bean("jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean("dataSource")
public DataSource createDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setInitialSize(6);
dataSource.setMinIdle(3);
dataSource.setMaxActive(50);
dataSource.setMaxWait(60000);
dataSource.setTimeBetweenEvictionRunsMillis(60000);
return dataSource;
}
}
14.5.2 事务配置类
package cn.guardwhy.config;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
public class TransactionConfiguration {
@Bean("transactionManager")
public DataSourceTransactionManager getTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
14.5.3 主配置类
package cn.guardwhy.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;
@Configuration
@ComponentScan(value="cn.guardwhy")
@Import(value={
DaoConfiguration.class,TransactionConfiguration.class})
@EnableTransactionManagement
public class SpringConfiguration {
}
14.5.4 表现层
package cn.guardwhy.controller;
import cn.guardwhy.config.SpringConfiguration;
import cn.guardwhy.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AccountController {
public static void main(String[] args) {
ApplicationContext context = new
AnnotationConfigApplicationContext(SpringConfiguration.class);
AccountService accountService = (AccountService)context.getBean("accountService");
accountService.transfer("curry","james",200f);
}
}