1.XML配置虽然比较复杂但是更加灵活,而注解配置虽然比价简单,但是灵活性不够,在实际的开发项目中我们更加倾向于是用注解和XML配置相结合的方式进行对项目的配置。之前笔者分别是用纯XML进行配置Spring的事务使用Spring的事务管理器配置数据库的事务和Spring使用纯注解配置事务管理并实现简单的增删查改以及模拟转账功能,这两种方式都能够实现对Spring事务的控制,不过两者均存在着一定的优缺点,所以两者结合起来更加简单灵活。
2.搭建项目。
2.1创建数据库以及数据表Account。
create database if not exists springdemo;
use springdemo;
create table if not exists Account(id int(4) auto_increment primary key,name varchar(16) not null,password varchar(16) not null,age int(3) ,createtime datetime default now(),money int(8));
2.2在idea中建立maven工程。
2.3在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>org.example</groupId>
<artifactId>springdemo_tx_annotationAndXML</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.8.RELEASE</spring.version>
</properties>
<dependencies>
<!-- 引入Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!-- 引入MySQL依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- 引入c3p0依赖-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- 引入测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2.4创建数据库连接配置文件db.properties
jdbc.url=jdbc:mysql://localhost:3306/springdemo?serverTimezone=UTC
jdbc.name=root
jdbc.password=root
jdbc.driver=com.mysql.cj.jdbc.Driver
3.编写entity,dao,service包下的代码
3.1编写实体类
package entity;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
import java.util.Date;
/**
* Classname:Account
*
* @description:
* @author: 陌意随影
* @Date: 2020-08-01 17:33
* @Version: 1.0
**/
@Repository("account")
@Scope("prototype")
public class Account {
//用户主键ID
private int id;
//用户名
private String name;
//用户密码
private String password;
//用户年龄
private int age;
//用户创建时间
private Date createTime ;
//用户的余额
private int money;
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", age=" + age +
", createTime=" + createTime +
", monney=" + money +
'}';
}
}
实体类Account是多例对象,所以要把@Scope("prototype")声明出来,如果不声明,默认是单例对象。
3.2编写持久层dao的接口以及实现类
package dao;
import entity.Account;
import java.util.List;
/**
* Classname:springdemo3
*
* @description:{description}
* @author: 陌意随影
* @Date: 2020-08-01 17:32
*/
public interface AccountDao {
/**
* @Description :保存用户
* @Date 11:51 2020/8/9 0009
* @Param * @param account :
* @return boolean
**/
public boolean saveAccount(Account account);
/**
* @Description :更新用户
* @Date 11:51 2020/8/9 0009
* @Param * @param newAccount :
* @return boolean
**/
public boolean updateAccount(Account newAccount);
/**
* @Description :通过ID删除用户
* @Date 11:52 2020/8/9 0009
* @Param * @param id :
* @return boolean
**/
public boolean deleteAccountById(int id);
/**
* @Description :通过ID查询用户
* @Date 11:52 2020/8/9 0009
* @Param * @param id :
* @return entity.Account
**/
public Account findAccountById(int id);
/**
* @Description :查找所有用户
* @Date 11:52 2020/8/9 0009
* @Param * @param :
* @return java.util.List<entity.Account>
**/
public List<Account> findAll();
}
package dao;
import entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* Classname:AccountDaoImpl
*
* @description:AccountDao的实现类
* @author: 陌意随影
* @Date: 2020-08-01 17:32
* @Version: 1.0
**/
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
//用于进行MySQL增删查改,自动注入JdbcTemplate
@Autowired
JdbcTemplate jdbcTemplate = null;
/**
* @Description :保存用户
* @Date 12:17 2020/8/9 0009
* @Param * @param newAccount :
* @return boolean
**/
public boolean saveAccount(Account newAccount) {
return jdbcTemplate.update("insert into account(name,password,age,createTIme,money) values(?,?,?,?,?)",
newAccount.getName(),newAccount.getPassword(),
newAccount.getAge(),newAccount.getCreateTime(),newAccount.getMoney())== 1;
}
/**
* @Description :更新用户
* @Date 12:18 2020/8/9 0009
* @Param * @param newAccount :
* @return boolean
**/
public boolean updateAccount(Account newAccount) {
return jdbcTemplate.update("update account set name=?,password=?,age=?,createTime=?,money=? where id=?",
newAccount.getName(),newAccount.getPassword(),
newAccount.getAge(),newAccount.getCreateTime(),newAccount.getMoney(),newAccount.getId())==1;
}
/**
* @Description :通过ID删除用户
* @Date 12:18 2020/8/9 0009
* @Param * @param id :
* @return boolean
**/
public boolean deleteAccountById(int id) {
return jdbcTemplate.update("delete from account where id=?",id) == 1;
}
/**
* @Description :通过ID查询用户
* @Date 11:02 2020/8/9 0009
* @Param * @param id :
* @return entity.Account
**/
public Account findAccountById(int id) {
List<Account> list = jdbcTemplate.query("select* from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), id);
if (list.isEmpty()){
return null;
}
if (list.size()==1){
return list.get(0);
}
return null;
}
/**
* @Description :查询所有哟用户
* @Date 11:01 2020/8/9 0009
* @Param * @param :
* @return java.util.List<entity.Account>
**/
public List<Account> findAll() {
return jdbcTemplate.query("select* from account",new BeanPropertyRowMapper<Account>(Account.class));
}
}
3.3编写业务逻辑的接口以及实现类
package service;
import entity.Account;
import java.util.List;
/**
* Classname:springdemo3
*
* @description:{description}
* @author: 陌意随影
* @Date: 2020-08-01 17:35
*/
public interface AccountService {
/**
* @Description :保存用户
* @Date 11:51 2020/8/9 0009
* @Param * @param account :
* @return boolean
**/
public boolean saveAccount(Account account);
/**
* @Description :更新用户
* @Date 11:51 2020/8/9 0009
* @Param * @param newAccount :
* @return boolean
**/
public boolean updateAccount(Account newAccount);
/**
* @Description :通过ID删除用户
* @Date 11:52 2020/8/9 0009
* @Param * @param id :
* @return boolean
**/
public boolean deleteAccountById(int id);
/**
* @Description :通过ID查询用户
* @Date 11:52 2020/8/9 0009
* @Param * @param id :
* @return entity.Account
**/
public Account findAccountById(int id);
/**
* @Description :查找所有用户
* @Date 11:52 2020/8/9 0009
* @Param * @param :
* @return java.util.List<entity.Account>
**/
public List<Account> findAll();
/**
* @Description :从用户ID为sourceId的用户向用户ID为targetId的用户转账money
* @Date 11:55 2020/8/9 0009
* @Param * @param sourceId
* @param targetId
* @param money :
* @return boolean
**/
public boolean tranferMoney(int sourceId,int targetId,int money);
}
package service;
import dao.AccountDao;
import entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Classname:AccountServiceImpl
* @description:
* @author: 陌意随影
* @Date: 2020-08-01 17:35
* @Version: 1.0
**/
@Service("accountService")
public class AccountServiceImpl implements AccountService {
//用户数据可访问的Dao接口,自动注入AccountDao
@Autowired
private AccountDao accountDao;
/**
* @Description :方便在配置文件中注入AccountDao的实现了AccountDaoImpl
* @Date 12:21 2020/8/9 0009
* @Param * @param accountDao :
* @return void
**/
public boolean saveAccount(Account account) {
return accountDao.saveAccount(account);
}
public boolean updateAccount(Account newAccount) {
return accountDao.updateAccount(newAccount);
}
public boolean deleteAccountById(int id) {
return accountDao.deleteAccountById(id);
}
public Account findAccountById(int id) {
return accountDao.findAccountById(id);
}
public List<Account> findAll() {
return accountDao.findAll();
}
/**
* @Description :从用户ID为sourceId的用户向用户ID为targetId的用户转账money
* @Date 11:55 2020/8/9 0009
* @Param * @param sourceId
* @param targetId
* @param money :
* @return boolean
**/
public boolean tranferMoney(int sourceId, int targetId, int money) {
//获取sourceId对应的用户
Account sourceAccount = this.findAccountById(sourceId);
//获取targetId对应的用户
Account targetAccount = this.findAccountById(targetId);
//转账失败
if (sourceAccount == null || targetAccount == null) {
return false;
}
//转账者扣去转账的金额
sourceAccount.setMoney(sourceAccount.getMoney() -money);
//目标对象加上获取的转账金额
targetAccount.setMoney(targetAccount.getMoney()+money);
//更新转账者的账户
boolean b = this.updateAccount(sourceAccount);
//模拟异常
int i = 1/0;
//更新转账目标者的账户
boolean b1 = this.updateAccount(targetAccount);
return b == true && b1 == true;
}
}
4.编写测试代码
package test;
import entity.Account;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.AccountService;
import java.util.Date;
import java.util.List;
/**
* Classname:AccountTest
*
* @description:
* @author: 陌意随影
* @Date: 2020-08-01 17:39
* @Version: 1.0
**/
public class AccountTest {
private ApplicationContext applicationContext;
private AccountService accountService;
@BeforeEach
public void init(){
applicationContext = new ClassPathXmlApplicationContext("bean.xml");
System.out.println("启动");
}
@AfterEach
public void destroy(){
}
@Test
public void testSave(){
Account account = applicationContext.getBean("account", Account.class);
account.setAge(434);
account.setName("李四");
account.setPassword("打个卡的撒老顾客");
account.setCreateTime(new Date());
account.setMoney(1000);
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
boolean fla = accountService.saveAccount(account);
System.out.println("插入用户:"+fla);
}
@Test
public void testUpdate(){
Account account = applicationContext.getBean("account", Account.class);
account.setAge(77);
account.setName("站干啥");
account.setPassword("54545");
account.setCreateTime(new Date());
account.setId(3);
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
boolean fla = accountService.updateAccount(account);
System.out.println("更新用户:"+fla);
}
@Test
public void testDelete(){
Account account = applicationContext.getBean("account", Account.class);
account.setId(5);
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
boolean fla = accountService.deleteAccountById(account.getId());
System.out.println("删除用户:"+fla);
}
@Test
public void testFindOne(){
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
Account account1 = accountService.findAccountById(6);
System.out.println("查找单个用户"+account1);
}
@Test
public void testFindAll(){
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
List<Account> accountList = accountService.findAll();
for (Account a :accountList) {
System.out.println(a);
}
}
@Test
public void testTran(){
//获取没有经过事务管理的普通AccountServiceImpl
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
accountService.tranferMoney(3,6,100);
}
}
5.配置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" 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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描包下的类-->
<context:component-scan base-package="dao"></context:component-scan>
<context:component-scan base-package="service"></context:component-scan>
<context:component-scan base-package="entity"></context:component-scan>
<!-- 引入资源文件-->
<context:property-placeholder location="db.properties"></context:property-placeholder>
<!-- 配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置c3p0数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 用户名-->
<property name="user" value="${jdbc.name}"></property>
<!-- 数据库驱动-->
<property name="driverClass" value="${jdbc.driver}"></property>
<!-- 密码-->
<property name="password" value="${jdbc.password}"></property>
<!-- URL-->
<property name="jdbcUrl" value="${jdbc.url}"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"> </property>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<!-- 配置事务的属性 -->
<tx:attributes>
<!-- 配置方法的属性,其中“*”表示任意,比如说save*表示以save开头的所有方法,比如saveAccount,saveUser等。。-->
<tx:method name="tranferMoney" isolation="DEFAULT" read-only="false" />
<tx:method name="save*" isolation="DEFAULT" read-only="false"></tx:method>
<tx:method name="update*" isolation="DEFAULT" read-only="false"></tx:method>
<tx:method name="delete*" isolation="DEFAULT" read-only="false"></tx:method>
<tx:method name="find*" isolation="DEFAULT" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 配置aop-->
<aop:config>
<!-- 配置切入点表达式-->
<aop:pointcut id="pt" expression="execution(* *service.AccountServiceImpl.*(..))"/>
<!-- 建立切入点表达式和事务通知的对应关系-->
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="pt"></aop:advisor>
</aop:config>
</beans>
6.配置完成,可以直接进行测试了。
本次测试源代码已经上传到个人博客的服务器,如有需要请自行移步下载:http://moyisuiying.com/wp-content/uploads/2020/08/springdemo_tx_annotationAndXML.rar