文章

04.MyBatis-Spring

04.MyBatis-Spring

MyBatis-Spring

什么是 MyBatis-Spring?

  • MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

Page not found · GitHub Pages

MyBatis-Spring 使用

  • 导入依赖
1
2
3
4
5
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>2.0.6</version>
</dependency>
  • 要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。
  • 在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean 来创建 SqlSessionFactory。 要创建工厂 bean,将下面的代码放到 Spring 的 XML 配置文件中:
1
2
3
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>
  • SqlSessionFactory 需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。
  • 在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。
  • 在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。
  • SqlSessionFactory 有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。
  • 一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings>< typeAliases> 元素。
  • 需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(),数据源()和 MyBatis 的事务管理器()都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。

  • SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。 SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。
  • 当调用 SQL 方法时(包括由 getMapper () 方法返回的映射器中的方法),SqlSessionTemplate 将会保证使用的 SqlSession 与当前 Spring 的事务相关。此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的 DataAccessExceptions。
  • 由于模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。
  • 可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。
1
2
3
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
  • 现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
public class UserDaoImpl implements UserDao {

  private SqlSession sqlSession;

  public void setSqlSession(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }

  public User getUser(String userId) {
    return sqlSession.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}
  • 按下面这样,注入 SqlSessionTemplate
1
2
3
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
  <property name="sqlSession" ref="sqlSession" />
</bean>

MyBatis-Spring 整合

普通整合

  • User.java
1
2
3
4
5
6
7
8
9
10
11
package me.hacket.spring.model;

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • UserMapper.java
1
2
3
4
5
6
7
8
9
10
11
12
package me.hacket.mapper;

import me.hacket.spring.model.User;

import java.util.List;

public interface UserMapper {

    // select
    //查询所有用户
    List<User> selectUser();
}
  • mybatis-config.xml 配置 MySQL 的连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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>
    <!--导入properties文件-->
    <properties resource="db.properties"/>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--下划线驼峰自动转换-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启全局缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <typeAliases>
        <typeAlias type="me.hacket.spring.model.User" alias="User"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
  • spring_user_mapper.xml 配置 Mapper
1
2
3
4
5
6
7
8
9
10
11
<?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="me.hacket.mapper.UserMapper">
    <select id="selectUser" resultType="me.hacket.spring.model.User">
        select *
        from user
    </select>
</mapper>
  • 引入 Spring 配置文件 spring-dao.xml
    • 配置数据源替换 mybaits 的数据源
    • 配置 SqlSessionFactory,关联 MyBatis 配置文件,Mapper 配置文件
    • 注册 sqlSessionTemplate,关联 sqlSessionFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 通过注解的方式注入 -->
    <context:annotation-config/>

    <!-- DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid -->
    <!-- 我们这里使用spring是供JDBC:org.springframework.jdbc.datasource -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=UTF-8&amp;allowPublicKeyRetrieval=false"/>
        <property name="username" value="root"/>
        <property name="password" value="zfs1314520"/>
    </bean>

    <!--配置SqlSessionFactory,关联MyBatis,spring_beans_mybatis.xml文件-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 绑定mybatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:spring_user_mapper.xml"/>
    </bean>

    <!--注册sqlSessionTemplate,关联sqlSessionFactory;spring_beans_mybatis.xml文件-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 只能使用构造器注入sqlSessionFactory,因为它没有set方法 -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
</beans>
  • 注册 sqlSessionTemplate,关联 sqlSessionFactory;UserMapperImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package me.hacket.mapper;

import me.hacket.spring.model.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper {
    // 我们的所有操作,都使用sqlSession来执行,在原来,现在都使用SqlSessionTemplate;
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
  • 注册 bean 实现,spring_application_context.xml 文件
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resource="spring_dao.xml"/>

    <bean id="userMapper" class="me.hacket.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>
  • 测试代码
1
2
3
4
5
6
7
8
@Test
public void selectUser2() throws IOException {
	ApplicationContext context = new ClassPathXmlApplicationContext("spring_application_context.xml");
	UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
	for (User user : userMapper.selectUser()) {
		System.out.println(user);
	}
}

SqlSessionDaoSupport

mybatis-spring1.2.3 版以上的才有这个。

dao 继承 Support 类,直接利用 getSqlSession() 获得,然后直接注入 SqlSessionFactory。比起方式 1,不需要管理 SqlSessionTemplate , 而且对事务的支持更加友好。可跟踪源码查看

  • 将上面写的 UserMapperImpl.java 修改一下
1
2
3
4
5
6
7
8
9
10
11
import com.github.subei.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
    public List<User> selectUser() {
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
  • 修改 spring_application_context.xml 中的文件 bean 的配置
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resource="spring_dao.xml"/>

    <bean id="userMapper2" class="me.hacket.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>
  • 测试
1
2
3
4
5
6
7
8
@Test
public void selectUser3() throws IOException {
	ApplicationContext context = new ClassPathXmlApplicationContext("spring_application_context.xml");
	UserMapperImpl2 userMapper = context.getBean("userMapper2", UserMapperImpl2.class);
	for (User user : userMapper.selectUser()) {
		System.out.println(user);
	}
}

总结 : 整合到 Spring 以后可以完全不要 mybatis 的配置文件,除了这些方式可以实现整合之外,还可以使用注解来实现。

Spring 事务

Spring 在不同的事务管理 API 之上定义了一个抽象层,使得开发人员不必了解底层的事务管理 API 就可以使用 Spring 的事务管理机制。Spring 支持编程式事务管理声明式的事务管理

编程式事务管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理==(交由容器管理事务)

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

声明式事务管理

  • spring_dao.xml 配置声明式事务
1
2
3
4
 <!-- 配置声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>
  • spring_dao.xml 使用 Spring 管理事务,注意头文件的约束导入 : tx
1
2
3
4
5
6
7
8
9
<?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  
        https://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/aop        http://www.springframework.org/schema/aop/spring-aop.xsd        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx.xsd">
<beans>
  • spring_dao.xml 配置好事务管理器后,去配置事务的通知
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 结合AOP实现事物的织入 -->
<!-- 配置事务的通知: -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<!-- 配置哪些方法使用什么样的事务,配置事务的传播特性 -->
		<tx:method name="add" propagation="REQUIRED"/>
		<tx:method name="delete" propagation="REQUIRED"/>
		<tx:method name="update" propagation="REQUIRED"/>
		<tx:method name="search*" propagation="REQUIRED"/>
		<tx:method name="get" read-only="true"/>
		<tx:method name="*" propagation="REQUIRED"/>
	</tx:attributes>
</tx:advice>
  • spring_dao.xml 导入 aop 的头文件
1
2
3
4
5
<!-- 配置事务的切入 -->
<aop:config>
	<aop:pointcut id="txPointcut" expression="execution(* me.hacket.mapper.*.*(..))"/>
	<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

spring 事务传播特性

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring 支持 7 种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 propagation_required 类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

为什么需要配置事务 ?

  • 如果不配置事务,可能存在数据提交不一致的情况下;
  • 如果我们不在 Spring 中去配置声明式事务,就需要手动提交控制事务;
  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题
本文由作者按照 CC BY 4.0 进行授权