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、發現問題
轉賬過程出現事務問題