Spring框架开发的三种模式
第一节 IoC的综合案例 - 纯xml开发
1.1综合案例描述
案例的需求
实现账户表的增删改查操作
案例的要求
选用基于XML的Spring和Mybatis整合配置实现。
- 数据库表结构介绍
CREATE TABLE `account` (
`id` int(11) NOT NULL ,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`money` double NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT
1.2 案例的实现
1.2.1 创建工程导入坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
1.2.2 编写基础代码
/**
* 账户的实体类
*/
public class Account {
private Integer id;
private String name;
private Double money;
//省略set,get,toString等方法
}
/**
* 账户的业务层接口
*/
public interface AccountService {
/**
* 保存
*/
void save(Account account);
/**
* 根据id删除
*/
void delete(Integer id);
/**
* 更新账户
*/
void update(Account account);
/**
* 根据id查询
*/
Account findById(Integer id);
/**
* 根据名称查询账户
*/
Account findByName(String name);
/**
* 查询所有
*/
List<Account> findAll();
}
/**
* 账户业务接口实现类
*/
public class AccountServiceImpl implements AccountService {
// 依赖注入 通过依赖注入可以实现service调用dao的增删改查方法 通过方法进行增删改查的调用
// 调用dao的时候要有接口所对应的SQL文件
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void save(Account account) {
accountDao.save(account);
}
@Override
public void delete(Integer id) {
accountDao.delete(id);
}
@Override
public void update(Account account) {
accountDao.update(account);
}
@Override
public Account findById(Integer id) {
return accountDao.findById(id);
}
@Override
public Account findByName(String name) {
return accountDao.findByName(name);
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
}
/**
* 账户持久层接口
*/
public interface AccountDao {
/**
* 保存
*/
void save(Account account);
/**
* 根据id删除
*/
void delete(Integer id);
/**
* 更新账户
*/
void update(Account account);
/**
* 根据id查询
*/
Account findById(Integer id);
/**
* 根据名称查询账户
*/
Account findByName(String name);
/**
* 查询所有
*/
List<Account> findAll();
}
1.2.3 编写mybatis的映射配置
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.AccountDao">
<!--保存-->
<insert id="save" parameterType="account">
insert into account values(#{id},#{name},#{money})
</insert>
<!--根据id删除-->
<delete id="delete" parameterType="int" >
delete from account where id=#{id}
</delete>
<!--更新账户-->
<update id="update" parameterType="account">
update account set name=#{name},money=#{money} where id=#{id}
</update>
<!--根据id查询-->
<select id="findById" parameterType="int" resultType="account">
select * from account where id=#{id}
</select>
<!--根据名称查询账户-->
<select id="findByName" parameterType="string" resultType="account">
select * from account where name=#{name}
</select>
<!--查询所有-->
<select id="findAll" resultType="account">
select * from account
</select>
</mapper>
1.2.4 连接数据库
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
# 连接数据库
1.2.5 编写spring和mybatis整合配置
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring配置文件并导入约束 -->
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- SpringContext spring核心配置文件 -->
<!-- dao层内容 -->
<!-- 1.连接数据库的配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 2.配置Druid数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 3.配置SqlSessionFactory的对象 是为了让spring完成mybatis的对象创建的时候,可以知道通过SqlSessionFactoryBean就能帮助找到mybatis所用到的sqlsession -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="druidDataSource"/>
<!-- pojo的别名映射-->
<property name="typeAliasesPackage" value="com.zhuxu.pojo"/>
</bean>
<!-- 4.配置dao层代码动态代理对象生成的扫描器 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定扫描的dao层的路径 -->
<property name="basePackage" value="com.zhuxu.dao"/>
</bean>
<!-- 5.service层内容 spring的配置 -->
<bean id="accountService" class="com.zhuxu.service.impl.AccountServiceImpl">
<!-- 依赖注入 -->
<property name="accountDao" ref="accountDao"/>
</bean>
</beans>
1.2.6 测试
/*
测试类
*/
public class CRUDTest {
@Test
public void findByName(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
AccountService accountService = applicationContext.getBean(AccountService.class);
Account account = accountService.findByName("迪丽热巴");
System.out.println("account = " + account);
}
@Test
public void findAll(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
AccountService accountService = applicationContext.getBean(AccountService.class);
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println("account = " + account);
}
}
}
第二节 注解开发
2.1 bean标签和注解的对应
-
bean标签对应注解@Component对应的是把xml配置文件中impl文件替换掉 替换如下的代码
-
如:
@Component("accountDao") public class AccountDaoImpl implements AccountDao { @Override public void saveAccount() { System.out.println("模拟保存账户"); } }
就不用在xml中写以下代码
<!--配置accountService对象--> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
但是beans.xml配置没有指定接口实现类对象所以就需要开启springioc的注解扫描,扫描包中类的注解
<context:component-scan base-package="com.itheima"></context:component-scan>
-
- 注解属性value:bean标签的id属性
- 不指定value属性,默认就是类名,首字母小写
- 该注解衍生出了三个注解,@Controller(在servlet层),@Service(service层),@Repository(dao层 ),用法和@Componet一致,为了更加清晰的提现层的概念。
-
bean标签属性scope对应注解@Scope
- 注解属性value:singleton,prototype
-
bean标签属性init-method对应注解@PostConstruct 构造方法执行完毕后执行
-
bean标签属性destroy-method对应注解@PreDestroy 对象销毁前执行的方法
-
service层
public interface AccountService { //模拟保存账户 void save(); }
//@Component("accountService") @Service("accountService") @Scope("prototype") public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void saveAccount() { accountDao.saveAccount(); } }
-
dao层
public interface AccountDao { //模拟保存账户 void save(); }
//@Component("accountDao") @Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Override public void saveAccount() { System.out.println("保存了账户"); } private void init() { System.out.println("AccountDao对象初始化"); } private void destroy() { System.out.println("AccountDao对象 销毁了"); } }
-
添加applicationContext配置文件命名空间
<?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" 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" > <!-- 开启spring的注解扫描,扫描包中类的注解--> <context:component-scan base-package="com.itheima"></context:component-scan> </beans>
-
测试注解
@Test public void testIOC(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); AccountService accountService = context.getBean(AccountService.class); AccountDao accountDao = context.getBean(AccountDao.class); System.out.println(accountService); System.out.println(accountDao); context.close(); }
2.2 依赖注入注解
之前是私有化一对象,通过get或者set的方法进行依赖注入
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
-
@Autowired注解(Spring框架提供)通过类型自动注入
@Autowired private AccountDao accountDao;
-
当接口的实现类不止一个时,无法通过类型自动注入了
按照类型注入,如果无法确定唯一类型(接口有多个实现类),需要配合注解@Qualifier的使用
-
@Qualifier("id") 注解(Spring框架提供)
- 按照id注入
-
@Resource注解(JDK提供)
- 注解属性name:配置类的id
@Resource(name="accountDao")
private AccountDao accountDao;
代码演示
- 业务层
@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements AccountService {
@Autowired
//@Qualifier("accountDao2")
//@Resource(name = "accountDao2")
private AccountDao accountDao;
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
}
- dao层
package com.itheima.dao.impl;
/**
* 账户dao实现类
*/
@Component("accountDao")
//@Repository("accountDao")
@Scope("singleton")
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount(Account account) {
System.out.println("模拟转账");
}
@PostConstruct()
public void init(){
System.out.println("AccountDaoImpl 初始化...");
}
@PreDestroy
public void destroy(){
System.out.println("AccountDaoImpl 销毁...");
}
}
2.3 Spring对Junit的支持
- junit运行的时候底层使用了Runner对象,有一个默认使用的Runner对象。
- Spring对junit的支持,其实是自己实现了一个Runner对象(按照junit runner的要求实现)
- Spring对junit的支持的体现
- 好处一:配置完之后,不需要我们手动的启动Spring
- 好处二:可以在junit测试类中使用@AutoWired等方式注入对象,直接对其进行调用测试
- 使用步骤
- 引入spring-test.jar包
- 配置测试类
//Spring框架中的Runner对象, 替换Junit中的Runner对象 (更换启动器 或者叫更换执行对象)
@RunWith(SpringJUnit4ClassRunner.class)
//框架启动入口, xml配置文件启动(2选1)加载xml启动执行配置文件 可以加载多个配置文件
@ContextConfiguration(locations = "classpath:beans.xml")
//框架启动入口, 注解方式配置文件启动(2选1) 纯注解开发推荐使用
//@ContextConfiguration(classes = SpringConfig.class)
-
代码实现
- pom.xml
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.9.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.9.RELEASE</version> </dependency> </dependencies>
- 测试类
package com.itheima; //Spring框架中的Runner对象, 替换Junit中的Runner对象 @RunWith(SpringJUnit4ClassRunner.class) //框架启动入口, 注解方式配置文件启动 //@ContextConfiguration(classes = SpringConfig.class); //框架启动入口, xml配置文件启动 @ContextConfiguration(locations = "classpath:ApplicationContext.xml") public class AccountTest { //注入业务层接口 @Autowired private AccountService service; @Test public void testIOC(){ System.out.println("service = " + service); Account account = new Account(); account.setName("小米"); account.setMoney(888.0F); service.saveAccount(account); } }
第三节 IoC的综合案例(CRUD) - 半注解半xml开发
企业主流的开发方式
注意:往往第三方jar中的对象我们使用xml配置(比如druid数据库连接池、Mybatis的SQLSessionFactory),类似于service层和dao层的实现类,这属于我们自己写的代码,往往会使用注解,这就是半xml半注解的模式。
-
applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--配置dao--> <!--配置properties文件的位置--> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!--配置数据源--> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!--配置mybatis的SqlSessionFactory工厂--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="druidDataSource"></property> <property name="typeAliasesPackage" value="com.itheima.pojo"></property> </bean> <!--配置创建dao代理实现类的扫描器--> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.itheima.dao"></property> </bean> <!--配置service--> <!--让Spring开启注解扫描--> <context:component-scan base-package="com.itheima"></context:component-scan> </beans>
-
service层
package com.itheima.service.impl; /** * 账户业务接口实现类 */ @Service("accountService") public class AccountServiceImpl implements AccountService { //依赖注入 @Autowired private AccountDao accountDao; //增删改查方法 无修改, 笔记中代码省略 }
-
测试类
/*
测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:ApplicationContext.xml"})
public class AccountServiceTest {
@Autowired
private AccountService accountService;
//测试类方法省略
@Test
public void findAll() {
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println("account = " + account);
}
}
}
第四节 IoC的综合案例(CRUD) - 纯注解开发
-
@Configuration标识当前类是Spring的一个配置类
-
@ComponentScan替代xml中的
<context:component-scan/>
-
@Import引入其他配置类,被引入的配置类可以不加@Configuration注解
-
@PropertySource:引入外部properties文件,注意加classpath:
-
@Value对成员变量赋值
-
@Bean将一个方法的返回值对象加入到Spring的容器当中管理
-
@Qualifier可以使用在方法参数上,表明对应的形参引入/注入的对象类型
4.1 SpringConfig框架启动配置类
只要在一个类上加@Configuration,那么这个类就是spring的配置类
/**
* Spring框架的核心配置文件(注解配置)
* @author Lucky
* @date 2020/8/29
*/
// 标识当前类是spring的一个配置类
@Configuration
// 开启SpringIOC容器的注解扫描
/**
* xml代码
* <!-- 扫描包,开启注解 -->
* <context:component-scan base-package="com.zhuxu" />
* */
@ComponentScan({"com.zhuxu"})
// 加载properties配置文件
// @PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
// 配置数据源
/**
* xml源码
* <!-- 配置Druid数据源 -->
* <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
* <property name="driverClassName" value="${jdbc.driverClassName}"/>
* <property name="url" value="${jdbc.url}"/>
* <property name="username" value="${jdbc.username}"/>
* <property name="password" value="${jdbc.password}"/>
* </bean>
*/
@Bean("druidDataSource")
public DataSource createDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
// 配置相关驱动信息(驱动、url、用户名、密码)
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/ssm_lx");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
return druidDataSource;
}
// 配置SqlSessionFactory对象
// 参数DataSource dataSource,会自动从Spring IOC容器中找配置的Bean对象
/**
* 配置SqlSessionFactory对象xml源码
* <!-- 配置SqlSessionFactory的对象 是为了让spring完成mybatis的对象创建的时候,可以知道通过SqlSessionFactoryBean就能帮助找到mybatis所用到的sqlsession
* -->
* <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
* <!-- 数据源 -->
* <property name="dataSource" ref="druidDataSource"/>
* <!-- pojo的别名映射-->
* <property name="typeAliasesPackage" value="com.zhuxu.pojo"/>
* </bean>
* */
@Bean
public SqlSessionFactoryBean createSqlSessionFactoryBean(@Qualifier("druidDataSource") DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 配置相关信息(数据源,pojo别名)
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.zhuxu.pojo");
return sqlSessionFactoryBean;
}
/**
* xml代码
* <!-- 配置dao层代码动态代理对象生成的扫描器 -->
* <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
* <!-- 指定扫描的dao层的路径 -->
* <property name="basePackage" value="com.zhuxu.dao"/>
* </bean>
* */
// dao 扫描器
@Bean
public MapperScannerConfigurer createMapperScannerConfigurer(){
MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
// 配置相关信息(指定扫描的dao层的路径)
scannerConfigurer.setBasePackage("com.zhuxu.dao");
return scannerConfigurer;
}
}
- 测试
/*
测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
//框架启动入口, 注解方式配置文件启动(2选1)
@ContextConfiguration(classes = SpringConfig.class)
public class CRUDTest {
@Autowired
private AccountService accountService;
@Test
public void findByName(){
Account account = accountService.findByName("迪丽热巴");
System.out.println("account = " + account);
}
@Test
public void findAll(){
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println("account = " + account);
}
}
}
4.2 注解开发的SpringConfig配置优化
-
SpringConfig框架启动配置类
/* 作为Spring框架的主配置文件 */ @Configuration //开启Spring容器的注解扫描 @ComponentScan({"com.itheima"}) //导入子配置文件 @Import({JDBCConfig.class, MybatisConfig.class}) public class SpringConfig { }
-
JDBCConfig配置类
//用于指定与数据库相关配置的配置文件 @Configuration @PropertySource({"classpath:jdbc.properties"}) public class JDBCConfig { @Value("${jdbc.driverClassName}") private String driverClassName; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; //配置数据源 @Bean("ataSource") public DataSource createDataSource(){ //创建Druid数据源 DruidDataSource druidDataSource = new DruidDataSource(); //配置相关信息(Driver, url, username, password) druidDataSource.setDriverClassName(driverClassName); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } }
-
Mybatis配置类
//用于配置与Mybatis相关的配置 @Configuration public class MybatisConfig { //配置SqlSessionFactoryBean对象 @Bean("sqlSessionFactory") public SqlSessionFactoryBean createSqlSessionFactoryBean(DataSource ds){ //创建SqlSessionFactoryBean 对象 SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); //配置相关信息(数据源, pojo别名映射) sqlSessionFactory.setDataSource(ds); sqlSessionFactory.setTypeAliasesPackage("com.itheima.pojo"); return sqlSessionFactory; } //配置dao的包扫描 @Bean("scannerConfigurer") public MapperScannerConfigurer createMapperScannerConfigurer(){ //创建MapperScannerConfigurer 对象 MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer(); //配置相关信息(扫描的dao包, 找到了dao层的接口文件, 找到了SQL映射文件, 生成接口实现类对象并存到Spring容器中) scannerConfigurer.setBasePackage("com.itheima.dao"); return scannerConfigurer; } }
-
测试类
/* 测试类 */ @RunWith(SpringJUnit4ClassRunner.class) //加载的xml配置文件 //@ContextConfiguration(locations = {"classpath:ApplicationContext.xml"}) //加载的注解形式的Spring主配置文件 @ContextConfiguration(classes = {SpringConfig.class}) public class AccountServiceTest { @Autowired private AccountService accountService; //其他方法省略 @Test public void findAll() { List<Account> accountList = accountService.findAll(); for (Account account : accountList) { System.out.println("account = " + account); } } }
第五节 案例:模拟转账
案例:模拟转账(并且模拟转账异常)
- 汇款人账户减少一定的金额
- 收款人账户增加一定的金额
- 计算之后,更新数据库
- 问题:模拟转账异常(人为制造异常,在两次update之间造了异常)
5.1 转账编码
1、引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
2、业务层
/**
* 账户的业务层接口
*/
public interface AccountService {
/**
* 转账
*/
void transfer(String source, String target, double money);
//其他方法省略
}
/**
* 账户业务接口实现类
*/
@Service("accountService")
public class AccountServiceImpl implements AccountService {
//依赖注入
@Autowired
private AccountDao accountDao;
/*
转账业务逻辑
1.先查询账户信息
2.修改账户信息
3.持久化账户信息
*/
@Override
public void transfer(String source, String target, double money) {
try {
//开启事务
// 转账业务逻辑
//1 先查询账户信息
Account sourceAccount = accountDao.findByName(source);
Account targetAccount = accountDao.findByName(target);
//2 修改账户信息
sourceAccount.setMoney(sourceAccount.getMoney() - money);
targetAccount.setMoney(targetAccount.getMoney() + money);
//3 持久化账户信息
accountDao.update(sourceAccount);
//int i = 1/0;
accountDao.update(targetAccount);
//提交事务
} catch (Exception e){
e.printStackTrace();
//回滚事务
} finally {
//关闭资源
}
}
//其他方法省略
}
3、测试
/*
测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class CRUDTest {
@Autowired
private AccountService accountService;
@Test
public void tranfer(){
accountService.transfer("迪丽热巴","古力娜扎",1);
}
}
4、发现问题
转账过程出现事务问题