最原始的事务控制
// 注册驱动 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // Class.forName("com.mysql.jdbc.Driver"); // 获取连接 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring5?useSSL=false", "root", "admin123"); // Connection对象可以设置事务提交方式:分为自动提交和手动提交;默认是自动提交 connection.setAutoCommit(false); // 获取预处理对象 PreparedStatement preparedStatement = connection.prepareStatement("insert into account (name,money)values (?,?)"); preparedStatement.setString(1,"ccc"); preparedStatement.setFloat(2,1555f); int i = preparedStatement.executeUpdate(); connection.commit(); System.out.println(i); preparedStatement.close(); connection.close();
Spring事务模板使用
spring配置文件内容:
<!-- 配置事务管理器--> <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="comboPooledDataSource"></property> </bean> <!-- 配置spring事务模板--> <bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate"> <property name="transactionManager" ref="TransactionManager"></property> </bean> <bean id="accountServiceImpl" class="service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> <property name="transactionTemplate" ref="transactionTemplate"></property> </bean> <!-- 创建数据源的bean:使用ComboPooledDataSource数据源需要导入外部jar包:c3p0--> <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring5"></property> <property name="user" value="root"></property> <property name="password" value="admin123"></property> </bean>
业务逻辑层实现类:
//账户业务层实现类 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } // 声明事务控制模板 private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public void transfer(String sourseName, String targetName, Float money) { transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus transactionStatus) { // 根据账户名查询对应的账户 Account sourceAccount = accountDao.findAccountByName(sourseName); Account targetAccount = accountDao.findAccountByName(targetName); // 转出账户减钱,转入账户加钱 sourceAccount.setMoney(sourceAccount.getMoney() - money); targetAccount.setMoney(targetAccount.getMoney() + money); // 更新两个账户 accountDao.updateAccount(sourceAccount); // 模拟异常 int i = 1 / 0; accountDao.updateAccount(targetAccount); return null; } }); } @Override public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } }
测试类代码:
public class Client { public static void main(String[] args) { // 获取容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); AccountServiceImpl accountServiceImpl = (AccountServiceImpl) context.getBean("accountServiceImpl"); accountServiceImpl.transfer("aaa","bbb",100f); context.close(); }
Spring基于XML的声明式事务控制(配置方式)重点(开发中首选)
需要事务控制的代码:
//账户业务层实现类 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String sourseName, String targetName, Float money) { // 根据账户名查询对应的账户 Account sourceAccount = accountDao.findAccountByName(sourseName); Account targetAccount = accountDao.findAccountByName(targetName); // 转出账户减钱,转入账户加钱 sourceAccount.setMoney(sourceAccount.getMoney() - money); targetAccount.setMoney(targetAccount.getMoney() + money); // 更新两个账户 accountDao.updateAccount(sourceAccount); // 模拟异常 int i = 1 / 0; accountDao.updateAccount(targetAccount); } @Override public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } }
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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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"> <!-- 将jdbc模板交给spring管理--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="comboPooledDataSource"></property> </bean> <!-- 创建数据源的bean:使用ComboPooledDataSource数据源需要导入外部jar包:c3p0--> <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring5"></property> <property name="user" value="root"></property> <property name="password" value="admin123"></property> </bean> <bean id="accountServiceImpl" class="service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> <property name="transactionTemplate" ref="transactionTemplate"></property> </bean> <bean id="accountDao" class="dao.impl.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> <property name="dataSource" ref="comboPooledDataSource"></property> </bean> <!-- 配置事务管理器--> <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="comboPooledDataSource"></property> </bean> <!-- 使用tx名称空间下面的标签配置事务的通知--> <tx:advice id="txAdvice" transaction-manager="TransactionManager"> <!-- 配置事务属性 isolation=用于指定事务的隔离级别,默认值是"DEFAULT",表示使用数据库的隔离级别 timeout=用于指定事务的过期时间,默认值"-1",永不过期,如果设置了该值,以秒为单位 read-only=用于指定事务是否只读,默认值"false",不只读.只有查询方法才可以设置为true propagation=用于指定事务的传播行为,默认值是"REQUIRED",表示一定有事务,一般增删改设置为REQUIRED;查询设置为SUPPORT,表示,你有事务我就要有,你没有,我也没有; rollback-for=用于指定用一个异常,当产生该异常时,事务回滚。产生其他异常时,事务不回滚。没有默认值,表示任何异常都回滚。 no-rollback-for=用于指定一个异常,当产生该异常时,事务不回滚。产生其他异常时,事务回滚。没有默认值,表示任何异常都回滚。 --> <tx:attributes> <tx:method name="*" read-only="false"/><!--*表示对将来使用通知的所有方法(增删改)使用此配置--> <tx:method name="find*" read-only="true"/><!--加入将来我们所有的查询方法都以fing开头,那么find*表示对查询方法使用此配置--> </tx:attributes> </tx:advice> <!-- 配置AOP--> <aop:config> <!-- 配置切入点表达式--> <aop:pointcut id="pt" expression="execution(* service..*.*(..))"/> <!--建立切入点表达式和事务通知之间的关系--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor> </aop:config> </beans>
事务属性补充图片:
测试代码:
public class Client { public static void main(String[] args) { // 获取容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); AccountService accountService = (AccountService) context.getBean("accountServiceImpl"); accountService.transfer("aaa","bbb",100f); context.close(); } }
Spring基于XML和注解组合事务控制(配置方式)重点
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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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"> <!-- 配置Spring扫描注解的包--> <context:component-scan base-package="service"></context:component-scan> <context:component-scan base-package="dao"></context:component-scan> <!-- 将jdbc模板交给spring管理--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="comboPooledDataSource"></property> </bean> <!-- 创建数据源的bean:使用ComboPooledDataSource数据源需要导入外部jar包:c3p0--> <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring5"></property> <property name="user" value="root"></property> <property name="password" value="admin123"></property> </bean> <!-- 配置事务管理器--> <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="comboPooledDataSource"></property> </bean> <!-- 开启Spring对注解事务的支持,并引用事务管理器--> <tx:annotation-driven transaction-manager="TransactionManager"></tx:annotation-driven> </beans>
需要事务控制的类需要添加@Transactional注解:
//账户业务层实现类 @Service("accountService") /** * @Transactional可以出现在接口上,类上和方法 * 接口上:表示该接口中的所有方法,实现类中重写的方法都有事务 * 类上:表示该类中所有的方法都有事务 * 方法上:表示当前方法有事务 * 优先级:就近原则。 * 方法>类>接口 */ @Transactional(readOnly = false) public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Override public void transfer(String sourseName, String targetName, Float money) { // 根据账户名查询对应的账户 Account sourceAccount = accountDao.findAccountByName(sourseName); Account targetAccount = accountDao.findAccountByName(targetName); // 转出账户减钱,转入账户加钱 sourceAccount.setMoney(sourceAccount.getMoney() - money); targetAccount.setMoney(targetAccount.getMoney() + money); // 更新两个账户 accountDao.updateAccount(sourceAccount); // 模拟异常 int i = 1 / 0; accountDao.updateAccount(targetAccount); } @Override public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } }
测试类代码:
public class Client { public static void main(String[] args) { // 获取容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); AccountService accountService = (AccountService) context.getBean("accountService"); accountService.transfer("aaa","bbb",100f); Account account = accountService.findAccountById(1); System.out.println(account); context.close(); } }
基于纯注解的声明式事务控制(配置方式)重点
创建一个类用于加载spring的配置并指定要扫描的包
/** * 用于初始化spring容器的配置类 */ @Configuration @ComponentScan(basePackages="com.itheima") public class SpringConfiguration { }
创建业务层接口和实现类并使用注解让spring管理
/** * 账户的业务层实现类 */ @Service("accountService") public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; @Override public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } @Override public void transfer(String sourceName, String targeName, Float money) { //1.根据名称查询两个账户 Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targeName); //2.修改两个账户的金额 source.setMoney(source.getMoney()-money);//转出账户减钱 target.setMoney(target.getMoney()+money);//转入账户加钱 //3.更新两个账户 accountDao.updateAccount(source); int i=1/0; accountDao.updateAccount(target); } }
创建Dao接口和实现类并使用注解让spring管理
Dao层接口和AccountRowMapper与基于xml配置的时候相同。略 @Repository("accountDao") public class AccountDaoImpl implements IAccountDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public Account findAccountById(Integer id) { List<Account> list = jdbcTemplate.query("select * from account where id = ? ",new AccountRowMapper(),id); return list.isEmpty()?null:list.get(0); } @Override public Account findAccountByName(String name) { List<Account> list = jdbcTemplate.query("select * from account where name = ? ",new AccountRowMapper(),name); if(list.isEmpty()){ return null; } if(list.size()>1){ throw new RuntimeException("结果集不唯一,不是只有一个账户对象"); } return list.get(0); } @Override public void updateAccount(Account account) { jdbcTemplate.update("update account set money = ? where id = ? ",account.getMoney(),account.getId()); } }
使用@Bean注解配置数据源
@Bean(name = "dataSource") public DataSource createDS() throws Exception { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setUsername("root"); dataSource.setPassword("123"); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://spring3_day04"); return dataSource; }
使用@Bean注解配置配置事务管理器
@Bean public PlatformTransactionManager createTransactionManager(@Qualifier("dataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
使用@Bean注解配置JdbcTemplate
@Bean public JdbcTemplate createTemplate(@Qualifier("dataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); }
在需要控制事务的业务层实现类上使用@Transactional注解
@Service("accountService") @Transactional(readOnly = true, propagation = Propagation.SUPPORTS) public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; @Override public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } @Override @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void transfer(String sourceName, String targeName, Float money) { //1.根据名称查询两个账户 Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targeName); //2.修改两个账户的金额 source.setMoney(source.getMoney() - money);//转出账户减钱 target.setMoney(target.getMoney() + money);//转入账户加钱 //3.更新两个账户 accountDao.updateAccount(source); //int i=1/0; accountDao.updateAccount(target); } }
该注解的属性和xml中的属性含义一致。该注解可以出现在接口上,类上和方法上。
出现接口上,表示该接口的所有实现类都有事务支持。
出现在类上,表示类中所有方法有事务支持
出现在方法上,表示方法有事务支持。
以上三个位置的优先级:方法>类>接口。
使用@EnableTransactionManagement开启spring对注解事务的的支持
@Configuration @EnableTransactionManagement public class SpringTxConfiguration { //里面配置数据源,配置JdbcTemplate,配置事务管理器。在之前的步骤已经写过了。 }