1.配置步骤:135
●第一步:配置事务管理器
●第二步:配置通知
●第三步:配置切面
记得添加aspectj的依赖:
<!--依赖aspectj--><dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.0.0-M2</version></dependency>
Spring配置文件如下:
记得添加aop命名空间。
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.powernode</groupId> <artifactId>course31</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <!--仓库--> <repositories> <!--仓库里程碑版spring--> <repository> <id>repository.spring.milestone</id> <name>Spring Milestone Repository</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> <!--依赖--> <dependencies> <!--spring context--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.0-M2</version> </dependency> <!--spring 依赖aspects 依赖AspectJ--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.0.0-M2</version> </dependency> <!--spring jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>6.0.0-M2</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <!--德鲁伊连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.13</version> </dependency> <!--@Resource注释--> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties></project>
spring.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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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"> <!--组件扫描--> <context:component-scan base-package="com.powernode.bank"/> <!--配置数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/spring6> <property name="username" value="root"/> <property name="password" value="lzl"/> </bean> <!--Jdbctemplatet配置--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事务管理器--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置通知,具体增强代码。--配置通知,具体增强代码。135--> <!--注:事务管理器应在通知中关联。--> <tx:advice id="txAdvice" transaction-manager="txManager"><!-- 配置相关通知属性--> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED" rollback-for="java.lang.Throwable"/><!-- 上面的transfer把名字写死了,有时候不方便,以下是模糊匹配的例子--> <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> <tx:method name="query*" read-only="true"/> <tx:method name="select*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="get*" read-only="true"/> </tx:attributes> </tx:advice><!-- 配置切面--> <aop:config><!-- 切点--> <aop:pointcut id="txPointcut" expression="execution(* com.powernode.bank.service..*(..))"/> <!--切面 = 通知 + 切点--> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config></bean>
pojo类Account
package com.powernode.bank.pojo;/** * 银行账户类 119 **/public class Account { private String actno; private Double balance; @Override public String toString() { return "Account{" + "actno='" + actno + '\'' + ", balance=" + balance + '}'; } public Account() { } public Account(String actno, Double balance) { this.actno = actno; this.balance = balance; } public String getActno() { return actno; } public void setActno(String actno) { this.actno = actno; } public Double getBalance() { return balance; } public void setBalance(Double balance) { this.balance = balance; }}
Accoundao接口
package com.powernode.bank.dao;import com.powernode.bank.pojo.Account;/** * CRUD操作专门负责账户信息。 119 * SQL语句只在DAO中执行,没有任何业务逻辑。 * 也就是说,DAO不与业务挂钩。 */public interface AccountDao { /** * 根据账户查询账户信息查询账户信息 * @param actno * @return */ Account selectByActno(String actno); /** * 更新帐户信息。 * @param act * @return */ int update(Account act);}
实现AccountDaoImplaodao接口
package com.powernode.bank.dao.impl;import com.powernode.bank.dao.AccountDao;import com.powernode.bank.pojo.Account;import jakarta.annotation.Resource;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;/** * Accountdao接口实现 119 **/@Repository("accountDao")public class AccountDaoImpl implements AccountDao { @Resource(name = "jdbcTemplate") private JdbcTemplate jdbcTemplate; @Override public Account selectByActno(String actno) { String sql = "select actno, balance from t_act where actno = ?"; Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), actno); return account; } @Override public int update(Account act) { String sql = "update t_act set balance = ? where actno = ? where actno = ?"; int count = jdbcTemplate.update(sql, act.getBalance(), act.getActno()); return count; }}
Accountservicent业务层接口
package com.powernode.bank.service;import com.powernode.bank.pojo.Account;/** * 业务接口 119 **/public interface AccountService { /** * 转账业务方法 * @param fromActno 从这个账户转出 * @param toActno 转到这个账户 * @param money 转账金额 */ void transfer(String fromActno, String toActno, double money);}
AccountServiceimple实现业务层接口
package com.powernode.bank.service.impl;import com.powernode.bank.dao.AccountDao;import com.powernode.bank.pojo.Account;import com.powernode.bank.service.AccountService;import jakarta.annotation.Resource;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Isolation;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;/** * 业务层实现类 119 **/@Service("accountService")public class AccountServiceImpl implements AccountService { @Resource(name = "accountDao") private AccountDao accountDao; // 控制事务,因为所有的转账业务都应该以这种方式完成。 @Override public void transfer(String fromActno, String toActno, double money) { // 第一步:开始事务 // 第二步:执行核心业务逻辑: // 查询转账账户余额是否充足 Account fromAct = accountDao.selectByActno(fromActno); if (fromAct.getBalance() < money) { throw new RuntimeException(”余额不足!!!!"); } // 余额充足 Account toAct = accountDao.selectByActno(toActno); // 先修改内存中两个对象的余额。 fromAct.setBalance(fromAct.getBalance() - money); toAct.setBalance(toAct.getBalance() + money); // 数据库更新 int count = accountDao.update(fromAct); count += accountDao.update(toAct); if(count != 2) { throw new RuntimeException(转账失败,联系银行!= 2) { throw new RuntimeException(转账失败,联系银行!); } // 第三步:如果在执行业务流程中没有异常。提交事务 // 第四步:如果在执行业务流程中出现异常,则回滚事务。 }}
测试
package com.powernode.bank.test;import com.powernode.bank.service.AccountService;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * XML声明式事务的实现方式 * 测试 135 **/public class BankTxTest { @Test public void testNoAnnotation(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); AccountService accountService = applicationContext.getBean("accountService", AccountService.class); try { accountService.transfer("act-001", "act-002", 10000.0); System.out.println(转账成功); } catch (Exception e) { e.printStackTrace(); } }}
在Accountserviceimpl的transfer模拟异常
// 模拟异常 String s = null; s.toString();
钱没有消失,事务生效了