Spring框架整合MyBatis框架

一、Spring整合MyBatis步骤

第一步 引入依赖包

项目所依赖的jar包如下:

Maven: aopalliance:aopalliance:1.0

Maven: com.google.protobuf:protobuf-java:3.6.1

Maven: commons-dbcp:commons-dbcp:1.4

Maven: commons-pool:commons-pool:1.6

Maven: junit:junit:4.13

Maven: log4j:log4j:1.2.17

Maven: mysql:mysql-connector-java:8.0.18

Maven: org.aspectj:aspectjweaver:1.9.6

Maven: org.hamcrest:hamcrest:2.2

Maven: org.hamcrest:hamcrest-core:2.2

Maven: org.mybatis:mybatis:3.5.6

Maven: org.mybatis:mybatis-spring:2.0.6

Maven: org.springframework:spring-aop:5.3.4

Maven: org.springframework:spring-beans:5.3.4

Maven: org.springframework:spring-context:5.3.4

Maven: org.springframework:spring-core:5.3.4

Maven: org.springframework:spring-expression:5.3.4

Maven: org.springframework:spring-jcl:5.3.4

Maven: org.springframework:spring-jdbc:5.3.4

Maven: org.springframework:spring-tx:5.3.4

:使用Maven创建项目,需要用到的所有依赖包如上。其中Maven: org.mybatis:mybatis-spring:2.0.6是MyBatis使用Spring框架整合的依赖包。 

 pom文件

<?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>com.bdqn</groupId>

<artifactId>s3-spring-mybatis</artifactId>

<version>1.0-SNAPSHOT</version>

<name>s3-spring-mybatis</name>

<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>

</properties>

<dependencies>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.13</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>5.3.4</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

<version>5.3.4</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>5.3.4</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>5.3.4</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-expression</artifactId>

<version>5.3.4</version>

</dependency>

<dependency>

<groupId>aopalliance</groupId>

<artifactId>aopalliance</artifactId>

<version>1.0</version>

</dependency>

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.9.6</version>

</dependency>

<dependency>

<groupId>org.hamcrest</groupId>

<artifactId>hamcrest-core</artifactId>

<version>2.2</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>1.2.17</version>

</dependency>

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.5.6</version>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>8.0.18</version>

</dependency>

<!-- Spring整合MyBatis所需要的包 -->

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis-spring</artifactId>

<version>2.0.6</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

<version>5.3.4</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-tx</artifactId>

<version>5.3.4</version>

</dependency>

<dependency>

<groupId>commons-dbcp</groupId>

<artifactId>commons-dbcp</artifactId>

<version>1.4</version>

</dependency>

<dependency>

<groupId>commons-pool</groupId>

<artifactId>commons-pool</artifactId>

<version>1.6</version>

</dependency>

</dependencies>



<!-- 配置资源目录 -->

<build>

<resources>

<resource>

<directory>src/main/resources</directory>

<includes>

<include>**/*.properties</include>

<include>**/*.xml</include>

</includes>

</resource>

</resources>

</build>

</project>

第二步 编写实体类、三层模式

第三步 配置MyBatis核心配置文件

mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!-- 类型别名,配置基础包,配置之后在Mapper.xml文件中可以使用类名称首字母小写方式简写 -->
    <typeAliases>
        <package name="com.bdqn.pojo" />
    </typeAliases>

</configuration>

:将数据源的配置交给Spring完成,按如上配置,注释掉引入外部数据源配置和MyBatis数据源环境配置。

第四步 配置Spring核心配置文件
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">

    <!-- 配置数据库连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/cvs_db?serverTimezone=UTC&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!-- 配置SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 引用数据库配置<bean id="dataSource"> -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 引入MyBatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <!--配置SQL映射文件信息-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/dao/**/*.xml</value>
            </list>
        </property>
    </bean>

    <!-- 配置SqlSessionTemplate -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!--将sqlSessionFactory注入到sqlSessionTemplate中(ref="sqlSessionFactory" ==> <bean id="sqlSessionFactory">)-->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    
    <!-- 配置Mapper -->
    <bean id="sysUserMapper" class="com.mapper.SysUserMapperImpl">
        <property name="sqlSession" ref="sqlSessionTemplate" />
    </bean>
    <!-- 配置Service -->
    <bean id="sysUserService" class="com.service.SysUserServiceImpl">
        <property name="sysUserMapper" ref="sysUserMapper" />
    </bean>
</beans>
 单元测试
package com;

import com.pojo.SysUser;
import com.service.SysUserService;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.ArrayList;
import java.util.List;

/**
 * SysUser业务测试
 *
 * @author 
 * @since 2023-10-18
 */
public class SysUserTest {

    private Logger logger = Logger.getLogger(SysUserTest.class);

    @Before
    public void setUp() throws Exception {
    }

    @Test
    public void testGetUserList() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        SysUserService userService = (SysUserService) ctx.getBean("sysUserService");
        List<SysUser> userList = new ArrayList<SysUser>();
        SysUser sysUser = new SysUser();
        sysUser.setRealName("赵");
        sysUser.setRoleId(2);
        userList = userService.getList(sysUser);

        for (SysUser userResult : userList) {
            logger.debug("testGetUserList account: "
                    + userResult.getAccount() + " and realName: "
                    + userResult.getRealName() + " and roleId: "
                    + userResult.getRoleId() + " and roleName: "
                    + userResult.getRoleIdName() + " and address: "
                    + userResult.getAddress());
        }
    }

}

【测试结果】

至此,Spring整合MyBatis配置完成。

优化一 使用SqlSessionDaoSupport替换SqlSessionTemplate

除了直接使用SqlSessionTempIate获取SqlSession实例处理数据的方式外,MyBatis-Spring还提供了SqlSessionDaoSupport类来简化SqlSessionTemplate。通过 this.getSqlSession() 获取到 SqlSession 对象来执行数据库操作。

在 com.dao 包下新建 SysUserMapperDaoSupportImpl.java
package com.dao;

import com.pojo.SysUser;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

/**
 * 继承SqlSessionDaoSupport抽象类实现(通过getSqlSession()方法获得SqlSession对象操作数据库)
 *
 * @author 
 * @since 2023-10-18
 */
public class SysUserMapperDaoSupportImpl extends SqlSessionDaoSupport implements SysUserMapper {

    /**
     * 查询用户列表
     *
     * @param sysUser
     * @return
     */
    @Override
    public List<SysUser> selectSysUserList(SysUser sysUser) {
        return this.getSqlSession().getMapper(SysUserMapper.class).selectSysUserList(sysUser);
    }

}

:继承了SqlSessionDaoSupport之后,不需要定义属性SqlSession sqlSession直接通过this.getSqlSession()获取到 SqlSession 对象来执行数据库操作。

修改 applicationContext.xml 对应配置
<!-- 配置DAO 一、使用SqlSessionTemplate版本 -->
<!-- MyBatis-Spring提供了SqlSessionTemplate类,继承了SqlSession接口,操作数据库 -->
<!--<bean id="sysUserMapper" class="com.dao.SysUserMapperImpl">
    <property name="sqlSession" ref="sqlSessionTemplate" />
</bean>-->
<!-- 配置DAO 二、使用SqlSessionDaoSupport版本 -->
<!-- MyBatis-Spring提供了通过this.getSqlSession()方法获得SqlSession实例操作数据库,不需要定义SqlSession对象,使用起来更方便 -->
<bean id="sysUserMapper" class="com.dao.SysUserMapperDaoSupportImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

 :注释第一种方式SqlSessionTemplate版本,替换为第二种方式,使用SqlSessionDaoSupport版本。

优化二 使用MapperFactoryBean注入映射器

MyBatis-Spring提供的MapperFactoryBean能够以配置的方式生成映射器的实现类,并注入给业务组件,无需编写DAO实现类。

修改 applicationContext.xml 对应配置
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 引用数据库配置<bean id="dataSource"> -->
    <property name="dataSource" ref="dataSource"/>
    <!-- 引入MyBatis配置文件 -->
    <property name="configLocation" value="classpath:mybatis-config.xml" />
    <!-- 配置SQL映射文件信息 -->
    <!-- 使用MyBatis-Spring提供的MapperFactoryBean如果映射器对应的SQL映射文件与映射器的类路径相同,该映射文件可以自动被MapperFactoryBean解析。在此情况下,配置SessionFactoryBean时可以不必指定SQL映射文件的位置。反之,如果映射器与映射文件的类路径不同,则仍需明确指定映射文件的位置 -->
    <!--<property name="mapperLocations">
        <list>
            <value>classpath:com/dao/**/*.xml</value>
        </list>
    </property>-->
</bean>

<!-- 配置DAO 一、使用SqlSessionTemplate版本 -->
<!-- MyBatis-Spring提供了SqlSessionTemplate类,继承了SqlSession接口,操作数据库 -->
<!--<bean id="sysUserMapper" class="com.dao.SysUserMapperImpl">
    <property name="sqlSession" ref="sqlSessionTemplate" />
</bean>-->
<!-- 配置DAO 二、使用SqlSessionDaoSupport版本 -->
<!-- MyBatis-Spring提供了通过this.getSqlSession()方法获得SqlSession实例操作数据库,不需要定义SqlSession对象,使用起来更方便 -->
<!--<bean id="sysUserMapper" class="com.dao.SysUserMapperDaoSupportImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>-->
<!-- 配置DAO 三、使用MapperFactoryBean版本 -->
<!-- MyBatis-Spring提供的MapperFactoryBean能够以配置的方式生成映射器的实现类,并注入给业务组件,无需编写DAO实现类 -->
<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    <property name="mapperInterface" value="com.dao.SysUserMapper" />
</bean>

 :通过使用MapperFactoryBean方式(第三种)配置后,无需编写DAO的实现类,只需编写Mapper.xml的映射文件。

优化三 使用MapperScannerConfig注入映射器

使用MapperFactoryBean对映射器做配置,在很大程度上简化了DAO模块的编码。不过,如果映射器很多,则相应的配置项也会很多。为了简化配置工作量,MyBatis-Spring中提供了MapperScannerConfigurer,它可以扫描指定包中的接口并将它们直接注册为MapperFactoryBean。

修改 applicationContext.xml 对应配置
<!-- 配置DAO 一、使用SqlSessionTemplate版本 -->
<!-- MyBatis-Spring提供了SqlSessionTemplate类,继承了SqlSession接口,操作数据库 -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperImpl">
    <property name="sqlSession" ref="sqlSessionTemplate" />
</bean>-->
<!-- 配置DAO 二、使用SqlSessionDaoSupport版本 -->
<!-- MyBatis-Spring提供了通过this.getSqlSession()方法获得SqlSession实例操作数据库,不需要定义SqlSession对象,使用起来更方便 -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperDaoSupportImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>-->
<!-- 配置DAO 三、使用MapperFactoryBean版本 -->
<!-- MyBatis-Spring提供的MapperFactoryBean能够以配置的方式生成映射器的实现类,并注入给业务组件,无需编写DAO实现类 -->
<!--<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    <property name="mapperInterface" value="com.bdqn.dao.SysUserMapper" />
</bean>-->
<!-- 配置DAO 四、使用MapperScannerConfig版本 -->
<!-- MyBatis-Spring提供的MapperScannerConfigurer将递归扫描基准包(包括各层级子包)下所有接口。如果他们在SQL映射文件中定义过,则将它们动态注册为映射器实现类,即可批量生成映射器的实现类了 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.bdqn.dao" />
</bean><!-- 配置DAO 一、使用SqlSessionTemplate版本 -->
<!-- MyBatis-Spring提供了SqlSessionTemplate类,继承了SqlSession接口,操作数据库 -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperImpl">
    <property name="sqlSession" ref="sqlSessionTemplate" />
</bean>-->
<!-- 配置DAO 二、使用SqlSessionDaoSupport版本 -->
<!-- MyBatis-Spring提供了通过this.getSqlSession()方法获得SqlSession实例操作数据库,不需要定义SqlSession对象,使用起来更方便 -->
<!--<bean id="sysUserMapper" class="com.bdqn.dao.SysUserMapperDaoSupportImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>-->
<!-- 配置DAO 三、使用MapperFactoryBean版本 -->
<!-- MyBatis-Spring提供的MapperFactoryBean能够以配置的方式生成映射器的实现类,并注入给业务组件,无需编写DAO实现类 -->
<!--<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    <property name="mapperInterface" value="com.bdqn.dao.SysUserMapper" />
</bean>-->
<!-- 配置DAO 四、使用MapperScannerConfig版本 -->
<!-- MyBatis-Spring提供的MapperScannerConfigurer将递归扫描基准包(包括各层级子包)下所有接口。如果他们在SQL映射文件中定义过,则将它们动态注册为映射器实现类,即可批量生成映射器的实现类了 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.bdqn.dao" />
</bean>

:通过使用MapperScannerConfigurer方式(第四种)配置后,指定的basePackage包称为基准包,MapperScannerConfigurer将递归扫描基准包(包括各层级子包)下所有接口。如果他们在SQL映射文件中定义过,则将它们动态注册为映射器实现类,即可批量生成映射器的实现类了

完整 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">

    <!-- 配置数据库连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/cvs_db?serverTimezone=UTC&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!-- 配置SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 引用数据库配置<bean id="dataSource"> -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 引入MyBatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <!-- 配置SQL映射文件信息 -->
        <!-- 使用MyBatis-Spring提供的MapperFactoryBean如果映射器对应的SQL映射文件与映射器的类路径相同,该映射文件可以自动被MapperFactoryBean解析。在此情况下,配置SessionFactoryBean时可以不必指定SQL映射文件的位置。反之,如果映射器与映射文件的类路径不同,则仍需明确指定映射文件的位置 -->
        <!--<property name="mapperLocations">
            <list>
                <value>classpath:com/dao/**/*.xml</value>
            </list>
        </property>-->
    </bean>

    <!-- 配置SqlSessionTemplate -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 将sqlSessionFactory注入到sqlSessionTemplate中(ref="sqlSessionFactory" ==> <bean id="sqlSessionFactory">) -->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <!-- 配置DAO 一、使用SqlSessionTemplate版本 -->
    <!-- MyBatis-Spring提供了SqlSessionTemplate类,继承了SqlSession接口,操作数据库 -->
    <!--<bean id="sysUserMapper" class="com.dao.SysUserMapperImpl">
        <property name="sqlSession" ref="sqlSessionTemplate" />
    </bean>-->
    <!-- 配置DAO 二、使用SqlSessionDaoSupport版本 -->
    <!-- MyBatis-Spring提供了通过this.getSqlSession()方法获得SqlSession实例操作数据库,不需要定义SqlSession对象,使用起来更方便 -->
    <!--<bean id="sysUserMapper" class="com.dao.SysUserMapperDaoSupportImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>-->
    <!-- 配置DAO 三、使用MapperFactoryBean版本 -->
    <!-- MyBatis-Spring提供的MapperFactoryBean能够以配置的方式生成映射器的实现类,并注入给业务组件,无需编写DAO实现类 -->
    <!--<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
        <property name="mapperInterface" value="com.dao.SysUserMapper" />
    </bean>-->
    <!-- 配置DAO 四、使用MapperScannerConfig版本 -->
    <!-- MyBatis-Spring提供的MapperScannerConfigurer将递归扫描基准包(包括各层级子包)下所有接口。如果他们在SQL映射文件中定义过,则将它们动态注册为映射器实现类,即可批量生成映射器的实现类了 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.mapper" />
    </bean>
    <!-- 配置Service -->
    <bean id="sysUserService" class="com.service.SysUserServiceImpl">
        <property name="sysUserMapper" ref="sysUserMapper" />
    </bean>
</beans>

注解方式 使用注解简化业务层配置

映射器被注册到Spring容器时,Spring框架会根据其接口名称为其命名,默认是首字母小写的非完全限定名。例如,SysUserMapper类型的组件默认命名为sysUserMapper。在开发中,可以使用@Autowired@Resource注解实现对业务组件的依赖注入,以简化业务组件的配置

使用注解修改 SysUserServiceImpl.java 文件
package com.service;

import com.dao.SysUserMapper;
import com.pojo.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * SysUserService服务层实现类
 *
 * @author 
 * @since 2023-10-18
 */
@Service("sysUserService") // 注册为一个Service层的Bean
public class SysUserServiceImpl implements SysUserService {

    @Autowired // @Resource // 使用注解的方式将DAO层的Bean注入
    private SysUserMapper sysUserMapper;

    @Override
    public List<SysUser> getList(SysUser sysUser) {
        try {
            return sysUserMapper.selectSysUserList(sysUser);
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw e;
        }
    }

}

总结:

Spring注册Bean的注解包括

  1. @Component :用于标注类为一个Bean
  2. @Repository :用于标注DAO类为一个Bean
  3. @Service :用于标注业务类为一个Bean
  4. @Controller :用于标注控制器类为一个Bean

:注解方式配置需要修改Spring配置文件,移除业务层Bean的配置,添加扫描注解定义的业务Bean

修改 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">

    <!-- 配置数据库连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/cvs_db?serverTimezone=UTC&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!-- 配置SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 引用数据库配置<bean id="dataSource"> -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 引入MyBatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <!-- 配置SQL映射文件信息 -->
        <!-- 使用MyBatis-Spring提供的MapperFactoryBean如果映射器对应的SQL映射文件与映射器的类路径相同,该映射文件可以自动被MapperFactoryBean解析。在此情况下,配置SessionFactoryBean时可以不必指定SQL映射文件的位置。反之,如果映射器与映射文件的类路径不同,则仍需明确指定映射文件的位置 -->
        <!--<property name="mapperLocations">
            <list>
                <value>classpath:com/dao/**/*.xml</value>
            </list>
        </property>-->
    </bean>

    <!-- 配置SqlSessionTemplate -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 将sqlSessionFactory注入到sqlSessionTemplate中(ref="sqlSessionFactory" ==> <bean id="sqlSessionFactory">) -->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <!-- 配置DAO 一、使用SqlSessionTemplate版本 -->
    <!-- MyBatis-Spring提供了SqlSessionTemplate类,继承了SqlSession接口,操作数据库 -->
    <!--<bean id="sysUserMapper" class="com.dao.SysUserMapperImpl">
        <property name="sqlSession" ref="sqlSessionTemplate" />
    </bean>-->
    <!-- 配置DAO 二、使用SqlSessionDaoSupport版本 -->
    <!-- MyBatis-Spring提供了通过this.getSqlSession()方法获得SqlSession实例操作数据库,不需要定义SqlSession对象,使用起来更方便 -->
    <!--<bean id="sysUserMapper" class="com.dao.SysUserMapperDaoSupportImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>-->
    <!-- 配置DAO 三、使用MapperFactoryBean版本 -->
    <!-- MyBatis-Spring提供的MapperFactoryBean能够以配置的方式生成映射器的实现类,并注入给业务组件,无需编写DAO实现类 -->
    <!--<bean id="sysUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
        <property name="mapperInterface" value="com.dao.SysUserMapper" />
    </bean>-->
    <!-- 配置DAO 四、使用MapperScannerConfig版本 -->
    <!-- MyBatis-Spring提供的MapperScannerConfigurer将递归扫描基准包(包括各层级子包)下所有接口。如果他们在SQL映射文件中定义过,则将它们动态注册为映射器实现类,即可批量生成映射器的实现类了 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.dao" />
    </bean>

    <!-- 配置Service -->
    <!-- 使用注解方式配置业务Bean时,删除该配置 -->
    <!--<bean id="sysUserService" class="com.service.SysUserServiceImpl">
        <property name="sysUserMapper" ref="sysUserMapper" />
    </bean>-->

    <!-- 通过注解的方式进行配置,定义扫描的包 -->
    <context:component-scan base-package="com.service" />
</beans>

 每次优化配置后,重新运行单元测试

Spring整合MyBatis配置总结

配置DAO及优化方式有以下四种:

  1. SqlSessionTemplate MyBatis-Spring提供了SqlSessionTemplate类,继承了SqlSession接口,操作数据库
  2. SqlSessionDaoSupport MyBatis-Spring提供了通过this.getSqlSession()方法获得SqlSession实例操作数据库,不需要定义SqlSession对象,使用起来更方便
  3. MapperFactoryBean MyBatis-Spring提供的MapperFactoryBean能够以配置的方式生成映射器的实现类,并注入给业务组件,无需编写DAO实现类
  4. MapperScannerConfig MyBatis-Spring提供的MapperScannerConfigurer将递归扫描基准包(包括各层级子包)下所有接口。如果他们在SQL映射文件中定义过,则将它们动态注册为映射器实现类,即可批量生成映射器的实现类了

二、配置声明式事务 

何为事务?

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。就是把一系列的操作当成原子性去执行。

事务四个属性ACID:

  1. 原子性(Atomicity)

事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

  1. 一致性(Consistency)

一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

  1. 隔离性(Isolation)

可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

  1. 持久性(Durability)

事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

实现事务的分类:

  • 编程式事务管理

将事务管理代码嵌到业务方法中来控制事务的提交和回滚

缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

  • 声明式事务管理

一般情况下比编程式事务好用。将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。将事务管理作为横切关注点,通过AOP方法模块化。Spring中通过Spring AOP框架支持声明式事务管理

事务管理器:

Spring的核心事务管理抽象,管理封装了一组独立于技术的方法,无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的

方式一:编写配置的方式进行事务配置

第一步 导入命名空间

该步骤在IDEA中可通过在编写配置节点时,通过Alt+Enter快捷键自动导入。

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"

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:aop="http://www.springframework.org/schema/aop"

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

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/aop

https://www.springframework.org/schema/aop/spring-aop.xsd">

第二步 定义事务管理器 

事务管理器提供了对事务处理的全面支持和统一管理,相当于AOP中增强处理的角色。配置方式如下:

applicationContext.xml

<!-- 声明式事务配置 -->

<!-- 首先,定义事务管理器(这里使用的是JDBC事务管理器,除此之外还有Java原生API事务管理器、JPA事务管理器、Hibernate事务管理器等) -->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<!-- 注入数据源 -->

<property name="dataSource" ref="dataSource" />

</bean>

 这里使用Spring框架提供的事务管理器类DataSourceTransactionManager。需要注意的是,配置DataSourceTransactionManager时,要为其注入事先定义好的数据源组件。

除此之外,在不同编程环境中还有以下事务配置方式:

<!-- 定义事务管理器 Hibernate事务 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 定义事务管理器 JPA事务 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 定义事务管理器 Java原生API事务 -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManagerName" value="java:/TransactionManager" />
</bean>

第三步 设置事务属性

事务管理器可以通过设定事务的属性为不同的业务方法指定具体的事务规则。

applicationContext.xml
<!-- 声明式事务配置,为不同业务方法指定具体的事务规则(transaction-manager属性的默认值是transactionManager。也就是说,如果事务管理器Bean名称是transactionManager,则可以不指定该属性值) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 根据方法名指定事务的属性,星号(*)表示通配符 -->
        <tx:method name="*"/>
        <!-- propagation 配置事务传播行为 -->
        <tx:method name="purchase" propagation="REQUIRES_NEW"/>
        <!-- isolation 配置事务的隔离级别 -->
        <tx:method name="update*" isolation="SERIALIZABLE"/>
        <!-- rollback-for 配置事务遇到异常必须回滚; no-rollback-for 配置事务遇到异常不回滚 -->
        <tx:method name="add*" rollback-for="java.io.IOException" no-rollback-for="com.bdqn.common.SendEMailException"/>
        <!-- read-only 配置事务只读属性 -->
        <tx:method name="find*" read-only="true"/>
        <!-- timeout 配置事务的超时属性 -->
        <tx:method name="get*" timeout="3"/>
    </tx:attributes>
</tx:advice>

注意:这里通过<tx:advice>标签配置事务增强,其中transaction-manager属性的默认值是transactionManager。也就是说,如果定义的事务管理器Bean名称是transactionManager,则可以不指定该属性值。

可以在<tx:attributes>标签中配置事务的传播机制,隔离级别等属性,这些属性通过<tx:attributes>标签下的<tx:method>标签进行设置。<tx:method>标签中的name属性是必需的,用于指定匹配的方法,通配符(*)表示任意字符匹配,其他属性均为可选配置。具体特点和用法如下表:

事务属性

说明

name

哪些方法需要有事务控制,支持 * 通配符

propagation

事务传播机制(通常情况下,第1种和第2种用的比较多)

  1. REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则就开启一个新的事务,并在自己的事务内运行,默认传播行为
  2. REQUIRED_NEW 当前方法必须启动新事务,并在自己的事务内运行,如果有事务正在运行,则将它挂起
  3. SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则可以不运行在事务中
  4. NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
  5. MANDATORY 当前的方法必须运行在事务内部,如果没有正在运行的事务,就会抛出异常
  6. NEVER 当前方法不应该运行在事务中,如果有运行的事务,就抛出异常
  7. NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样

isolation

事务隔离级别,并发事务会导致发生以下三种类型的问题

  • 脏读 发生在一个事务读取了另一个事务改写尚未提交的数据时,改写的数据被回滚了,那么第一个事务获取的数据无效
  • 不可重复读 当同一个事务执行两次及以上相同的查询时,每次都得到不同的数据。一般因为另一并发事务在两次查询期间进行了更新
  • 幻读 第一个事务读取了一些数据,此时第二个事务在该表中插入了一些新数据,这时第一个事务再读取相同的数据就会多出几行

不可重复读和幻读的区别:不可重复读侧重点在相同数据被修改,而幻读是删除或新增

从理论上讲,事务应该完全隔离,避免并发事务导致的问题,但是这样可能对性能产生极大影响,因为事务必须按顺序进行了。所以在实际的开发中,为了提升性能,事务会以比较低的隔离级别运行。Spring中事务的隔离级别可以通过隔离属性指定:

  1. DEFAULT 使用底层数据库的默认隔离级别,大部分数据库,默认隔离级别都是READ_COMMITED
  2. READ_COMMITED 只允许事务读取已经被其他事务提交的更改,可以避免脏读,但不可重复读和幻读问题仍然可能出现
  3. READ_UNCOMMITED 允许事务读取未被其他事务提交的更改。效率最高,脏读、不可重复读、幻读都可能会出现
  4. REPEATABLE_READ 确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事务对这个字段进行更新
  5. SERIALIZABLE 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新,删除。所有的并发问题都能避免,最安全,但是性能比较低

注意:事务的隔离级别需要底层数据库引擎的支持,而不是应用程序或者框架的支持

Oracle支持2种事务隔离级别:READ_COMMITED、SERIALIZABLE

MySQL支持4种事务隔离级别:READ_COMMITED、READ_UNCOMMITED、REPEATABLE_READ、SERIALIZABLE

timeout

事务超时时间

允许事务运行的最长时间,以秒(s)为单位,超过给定的时间自动回滚,防止事务执行时间过长而影响系统性能。该属性需要底层的实现支持。默认值为-1,表示不超时

readonly

Boolean值,是否是只读事务

  1. 如果为true,告诉数据库此事务为只读事务。数据库优化,会对性能有一定提升,所以只要是查询的方法,建议设置为true
  2. 如果为 false(默认值),表示需要提交的事务。建议新增、修改、删除

rollback-for

能够触发回滚的异常类型

Spring框架默认只在抛出RuntimeException时才标识事务回滚,可通过全限定类名自行指定需要回滚事务的异常,多个类名用英文逗号(,)隔开

no-rollback-for

不触发回滚的异常类型

Spring框架默认CheckedException不会触发事务回滚,可通过全限定类名自行指定需要回滚事务的异常,多个类名用英文逗号隔开

 第四步 定义事务切面

通过定义切面,可以将事务规则应用到指定的方法上。

applicationContext.xml
<!-- 定义切面,把事务切入点和事务属性关联起来 -->
<aop:config>
    <!-- 定义切入点 expression="execution(* com.service..*.*(..))" 第一个[*]表示任意返回值类型;[com.bdqn.service..]代表匹配com.service包及其子包;[*.*(..)]代表匹配包中所有类的所有方法 -->
    <aop:pointcut id="serviceMethod" expression="execution(* com.service..*.*(..))"/>
    <!-- 将切入点和事务属性关联起来 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>

 :这里通过<aop:advisor>标签的advice-ref属性引用<tx:advice>标签设定的事务属性组件。

至此,Spring框架的声明式事务通过以上四个步骤配置完成。 

applicationContext.xml声明式事务完整配置

 

<!-- 声明式事务配置 -->

<!-- 首先,定义事务管理器(这里使用的是JDBC事务管理器,除此之外还有Java原生API事务管理器、JPA事务管理器、Hibernate事务管理器等) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源 -->
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 配置事务属性,方式一:编写配置的方式进行事务配置 -->

<!-- 声明式事务配置,为不同业务方法指定具体的事务规则(transaction-manager属性的默认值是transactionManager。也就是说,如果事务管理器Bean名称是transactionManager,则可以不指定该属性值) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 根据方法名指定事务的属性,星号(*)表示通配符 -->
        <tx:method name="*"/>
        <!-- propagation 配置事务传播行为 -->
        <tx:method name="purchase" propagation="REQUIRES_NEW"/>
        <!-- isolation 配置事务的隔离级别 -->
        <tx:method name="update*" isolation="SERIALIZABLE"/>
        <!-- rollback-for 配置事务遇到异常必须回滚; no-rollback-for 配置事务遇到异常不回滚 -->
        <tx:method name="add*" rollback-for="java.io.IOException" no-rollback-for="com.common.SendEMailException"/>
        <!-- read-only 配置事务只读属性 -->
        <tx:method name="find*" read-only="true"/>
        <!-- timeout 配置事务的超时属性 -->
        <tx:method name="get*" timeout="3"/>
    </tx:attributes>
</tx:advice>
<!-- 定义切面,把事务切入点和事务属性关联起来 -->
<aop:config>
    <!-- 定义切入点 expression="execution(* com.bdqn.service..*.*(..))" 第一个[*]表示任意返回值类型;[com.service..]代表匹配com.service包及其子包;[*.*(..)]代表匹配包中所有类的所有方法 -->
    <aop:pointcut id="serviceMethod" expression="execution(* com.service..*.*(..))"/>
    <!-- 将切入点和事务属性关联起来 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>

其中,com.bdqn.common.SendEMailException是自定义异常类型,表示发送邮件时发生的异常。代码如下:

SendEMailException.java
package com.common;

/**
 * 发送EMail异常
 *
 * @author 
 * @since 2023-10-18
 */
public class SendEMailException extends RuntimeException {

   private static final long serialVersionUID = 1L;

   public SendEMailException(String message) {
      super(message);
   }

   public SendEMailException(Throwable cause) {
      super(cause);
   }

   public SendEMailException(String message, Throwable cause) {
      super(message, cause);
   }

   public SendEMailException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
      super(message, cause, enableSuppression, writableStackTrace);
   }

}

 

方式二:使用注解进行事务配置

除了使用配置文件处理事务外,Spring框架还支持注解配合少量配置的方式处理声明式事务,相对于纯配置文件的方式,这种方式所写的代码要简洁很多。

applicationContext.xml使用注解进行事务配置
<!-- 声明式事务配置 -->

<!-- 首先,定义事务管理器(这里使用的是JDBC事务管理器,除此之外还有Java原生API事务管理器、JPA事务管理器、Hibernate事务管理器等) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源 -->
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 开启注解处理事务功能,方式二:使用注解进行事务配置(注意,仍然需要配置事务管理器) -->
<tx:annotation-driven />

配置文件编写完成之后,就可以在代码中使用 @Transactional 注解处理事务

测试事务

当执行添加测试时,要么两次add操作同时成功,要么同时失败。

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值