Spring学习03:事务控制TransactionManager
使用Spring配置事务控制
项目准备
-
在pom.xml中导入依赖的坐标
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.2.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies>
-
在resources目录下创建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: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/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"> </beans>
-
创建实体类
public class Account { private int accountId; private String name; private double money; public int getAccountId() { return accountId; } public void setAccountId(int accountId) { this.accountId = accountId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } @Override public String toString() { return "Account{" + "accountId=" + accountId + ", name='" + name + '\'' + ", money=" + money + '}'; } }
-
创建service层接口及其实现类
public interface AccountService { /** * 查询所有账户 * @return */ List<Account> findAll(); /** * 转账 * @param sourceName 转出账户 * @param targetName 转入账户 * @param money 转账金额 */ void transfer(String sourceName,String targetName,double money); }
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public List<Account> findAll() { return accountDao.findAll(); } public void transfer(String sourceName, String targetName, double money) { // 1查询转出账户 Account source = accountDao.findAccountByName(sourceName); // 2查询转入账户 Account target = accountDao.findAccountByName(targetName); // 3转出账户减钱 source.setMoney(source.getMoney() - money); // 4转入账户加钱 target.setMoney(target.getMoney() + money); // 5更新转出账户 accountDao.updateAccount(source); // 6更新转入账户 accountDao.updateAccount(target); } }
-
创建dao层接口及其实现类
public interface AccountDao { /** * 查询所有账户 * @return */ List<Account> findAll(); /** * 更新账户 * @param account */ void updateAccount(Account account); /** * 根据名称查询账户 * @param accountName * @return */ Account findAccountByName(String accountName); }
public class AccountDaoImpl implements AccountDao { private JdbcTemplate jdbcTemplate; public List<Account> findAll() { return jdbcTemplate.query("select * from account",new BeanPropertyRowMapper<Account>(Account.class)); } public void updateAccount(Account account) { jdbcTemplate.update("update account set name=?,money=? where accountId=?",account.getName(),account.getMoney(),account.getAccountId()); } public Account findAccountByName(String accountName) { List<Account> accounts = jdbcTemplate.query("select * from account where name=?",new BeanPropertyRowMapper<Account>(Account.class),accountName); if(accounts.isEmpty()){ return null; } if(accounts.size() > 1){ throw new RuntimeException("结果集不唯一"); } return accounts.get(0); } }
-
在bean.xml中添加配置
<context:component-scan base-package="com.chen"></context:component-scan> <!--配置service--> <bean id="accountService" class="com.chen.service.impl.AccountServiceImpl"> <!--注入Dao--> <property name="accountDao" ref="accountDao"></property> </bean> <!--配置dao--> <bean id="accountDao" class="com.chen.dao.impl.AccountDaoImpl"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置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://localhost:3306/account1?characterEncoding=utf-8"></property> <property name="user" value="root"></property> <property name="password" value="991105"></property> </bean>
基于XML的Spring事务控制的配置
-
配置事务管理器并注入数据源
<!--配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
-
配置事务的通知
<!--配置事务的通知--> <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:method> </tx:attributes> </tx:advice>
其中的属性如下:
- id:事务通知的唯一标识
- transaction-manager:引入事务管理器
<tx:method>
标签内的属性:- name:匹配的方法,可以使用通配符
*
- isolation:事务的隔离级别,Spring默认使用数据库的事务隔离级别
- propagation:事务的传播行为,默认为REQUIRED,增删改方法应该用REQUIRED,查询方法可以使用SUPPORTS
- read-only:事务是否为只读事务,默认为false,增删改方法应该用false,查询方法可以使用true
- timeout:指定超时时间,默认值为-1,表示永不超时
- rollback-for:指定一个异常,当发生该异常时,事务回滚;发生其他异常时,事务不回滚;无默认值,表示发生任何异常都回滚
- no-rollback-for:指定一个异常,当发生该异常时,事务不回滚;发生其他异常时,事务回滚.;默认值,表示发生任何异常都回滚
- name:匹配的方法,可以使用通配符
-
配置AOP
<!--配置AOP--> <aop:config> <!--配置切入点表达式--> <aop:pointcut id="pt" expression="execution(* com.chen.service.impl.*.*(..))"/> <!--为事务通知指定切入点表达式--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor> </aop:config>
基于注解的Spring事务控制的配置
使用的注解
@EnableTransactionManagement:标识开启事务控制
-
创建事务控制配置类
public class TransactionConfiguration { @Bean(name="transactionManager") public PlatformTransactionManager createTransactionManager(DataSource dataSource){ return new DataSourceTransactionManager(dataSource); } }
-
创建JDBCConfig配置类
public class JdbcConfiguration { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.user}") private String user; @Value("${jdbc.password}") private String password; @Bean(name = "jdbcTemplate") public JdbcTemplate createJdbcTemplate(DataSource dataSource){ return new JdbcTemplate(dataSource); } @Bean(name = "dataSource") public DataSource createDataSource(){ DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(user); ds.setPassword(password); return ds; } }
-
创建SpringConfiguration主配置类
@Configuration @ComponentScan("com.chen") @Import({JdbcConfiguration.class,TransactionConfiguration.class}) @PropertySource("classpath:jdbcConfig.properties") @EnableTransactionManagement public class SpringConfiguration { }
-
改写业务层实现类
@Service("accountService") @Transactional(propagation = Propagation.SUPPORTS,readOnly = true) public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; public List<Account> findAll() { return accountDao.findAll(); } @Transactional(propagation = Propagation.REQUIRED,readOnly = false) public void updateAccount(Account account) { accountDao.updateAccount(account); } @Transactional(propagation = Propagation.REQUIRED,readOnly = false) public void transfer(String sourceName, String targetName, double money) { // 1查询转出账户 Account source = accountDao.findAccountByName(sourceName); // 2查询转入账户 Account target = accountDao.findAccountByName(targetName); // 3转出账户减钱 source.setMoney(source.getMoney() - money); // 4转入账户加钱 target.setMoney(target.getMoney() + money); // 5更新转出账户 accountDao.updateAccount(source); // 6更新转入账户 accountDao.updateAccount(target); } }