1. 程式人生 > 實用技巧 >Spring框架開發的三種模式

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