一.使用spring的注解来声明事务
1.1 说明
此案例要和上一篇文章,基于xml实现事务的配置,要做对比,实现的功能是一样的,但是xml方式和基于注解方式的写法配置都是有区别的。
1.2 主要流程说明
spring中基于注解的声明式事务控制的控制步骤:
1配置事务管理器
2.开启spring的注解事务的支持
3.在需要事务支持的地方使用@Transaction注解
二 项目案例
2.1 工程结构
2.2 配置pom文件
<!-- log4j的日志-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- spring的事务管理-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- mysql的驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- spring 切面包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
2.3 dao层
1.接口
package com.ljf.spring.tx.anno.dao;
import com.ljf.spring.tx.anno.domain.Account;
public interface AccountDao {
/**
* 根据名称查询账户
* @param accountName
* @return
*/
Account findAccountByName(String accountName);
/**
* 更新账户
* @param account
*/
void updateAccount(Account account);
}
2.实现层
注解的方式可以和上篇文章xml的方式进行对别
package com.ljf.spring.tx.anno.dao.impl;
import com.ljf.spring.tx.anno.dao.AccountDao;
import com.ljf.spring.tx.anno.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @ClassName: AccountDaoImpl
* @Description: TODO
* @Author: liujianfu
* @Date: 2021/02/22 18:02:28
* @Version: V1.0
**/
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account findAccountByName(String accountName) {
List<Account> accounts = jdbcTemplate.query("select id id ,account_name accountName,money money from tb_account where account_name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
if(accounts.isEmpty()){
return null;
}
if(accounts.size()>1){
throw new RuntimeException("结果集不唯一");
}
return accounts.get(0);
}
@Override
public void updateAccount(Account account) {
jdbcTemplate.update("update tb_account set account_name=?,money=? where id=?",account.getAccountName(),account.getMoney(),account.getId());
}
}
2.4 service层
1.接口
package com.ljf.spring.tx.anno.service;
public interface AccountService {
/**
* 转账
* @param sourceName 转成账户名称
* @param targetName 转入账户名称
* @param money 转账金额
*/
public void transferMoney(String sourceName, String targetName, Double money);
}
2.实现层
package com.ljf.spring.tx.anno.service.impl;
import com.ljf.spring.tx.anno.dao.AccountDao;
import com.ljf.spring.tx.anno.domain.Account;
import com.ljf.spring.tx.anno.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* @ClassName: AccountServiceImpl
* @Description: TODO
* @Author: liujianfu
* @Date: 2021/02/22 17:52:56
* @Version: V1.0
**/
@Service("accountService")
@Transactional(propagation= Propagation.SUPPORTS,readOnly=true)//只读型事务的配置
public class AccountServiceImpl implements AccountService {
//通过注解
@Autowired
private AccountDao actDao;
/** 北京,上海的钱均为500 ,现在实现北京向上海进行转账50,正确的结果为北京450,上海550,
* 但是现在没有进行事务管理,在北京向上海进行转账,实现北京进行了减款操作,但是在上海进行加钱操作之前,出现了报错,后面代码无法执行,
* 造成北京450,上海还是500
* 转账
* @param sourceName 转成账户名称
* @param targetName 转入账户名称
* @param money 转账金额
*
* 1.@Transactional 等价于
* <!-- 配置aop-->
* <aop:config>
* <!-- 配置切入点表达式-->
* <aop:pointcut id="pt1" expression="execution(* com.ljf.spring.tx.xml.service.impl.*.*(..))"></aop:pointcut>
* <!--建立切入点表达式和事务通知的对应关系 -->
* <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
*
* </aop:config>
* 2.@Transactional(propagation= Propagation.REQUIRED,readOnly=false) 等价于
* <!-- 配置事务的通知-->
* <tx:advice id="txAdvice" transaction-manager="transactionManager">
* <tx:attributes>
* <tx:method name="*" propagation="REQUIRED" read-only="false" rollback-for="*"/>
* <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
* </tx:attributes>
* </tx:advice>
*
* <!-- 配置aop-->
* <aop:config>
* <!-- 配置切入点表达式-->
* <aop:pointcut id="pt1" expression="execution(* com.ljf.spring.tx.xml.service.impl.*.*(..))"></aop:pointcut>
* <!--建立切入点表达式和事务通知的对应关系 -->
* <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
*/
@Override
//需要的是读写型事务配置
@Transactional(propagation= Propagation.REQUIRED,readOnly=false)
public void transferMoney(String sourceName, String targetName, Double money) {
System.out.println("正在实现转账功能 transfer....");
//2.1根据名称查询转出账户
Account source = actDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = actDao.findAccountByName(targetName);
//2.3转出账户减钱
System.out.println(":"+(source.getMoney()-money));
source.setMoney(source.getMoney()-money);
//2.4转入账户加钱
target.setMoney(target.getMoney()+money);
//2.5更新转出账户
actDao.updateAccount(source);
try{
int i=1/0;
}
catch (Exception e){
e.printStackTrace();
System.out.println("进行了事务的回滚");
throw new RuntimeException();
}
//2.6更新转入账户
actDao.updateAccount(target);
}
}
2.5 resouce的配置文件
2.5.1 jdbc的配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/nongda
jdbc.user=root
jdbc.password=
2.5.2 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: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">
<context:property-placeholder location="classpath:spring-jdbc.properties"></context:property-placeholder>
<!-- 1.配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 2.配置spring创建容器时要扫描的包-->
<context:component-scan base-package="com.ljf.spring.tx"></context:component-scan>
<!-- 3.配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- spring中基于注解 的声明式事务控制配置步骤
1、配置事务管理器
2、开启spring对注解事务的支持
3、在需要事务支持的地方使用@Transactional注解
-->
<!-- 4.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5.开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
2.6 调用
package com.ljf.spring.tx.anno;
import com.ljf.spring.tx.anno.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据id获取Bean对象
AccountService as = ac.getBean(AccountService.class);
as.transferMoney("北京","上海",50.0);
System.out.println(as);
}
}
多次执行,北京、上海的钱数不变,回滚进行了操作,起到了作用!