1.说说什么是MyBatis?

先吹一下

  • Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
  • MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

再说一下缺点

  • SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求
  • SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

ORM是什么?

ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单来说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。

为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

  • Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
  • 而Mybatis在查询关联对象或关联集合对象时,需要手动编写SQL来完成,所以,被称之为半自动ORM映射工具。


2.MyBatis使用过程?生命周期?

MyBatis基本使用的过程大概可以分为这么几步:

//1、 创建SqlSessionFactory
//可以从配置或者直接编码来创建SqlSessionFactory
    String resource = "org/mybatis/example/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

//2、 通过SqlSessionFactory创建SqlSession
//SqlSession(会话)可以理解为程序和数据库之间的桥梁
    SqlSession session = sqlSessionFactory.openSession();

//3、 通过sqlsession执行数据库操作
//第一种写法:可以通过 SqlSession 实例来直接执行已映射的 SQL 语句:
     Blog blog = (Blog)session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
//第二种写法:更常用的方式是先获取Mapper(映射),然后再执行SQL语句:
     BlogMapper mapper = session.getMapper(BlogMapper.class);
     Blog blog = mapper.selectBlog(101);

//4、 调用session.commit()提交事务
//如果是更新、删除语句,我们还需要提交一下事务。

//5、 调用session.close()关闭会话
//最后一定要记得关闭会话。
session.close();

3.MyBatis快速入门

IDEA 创建一个Maven工程,导入依赖

      <!-- Spring-Jdbc -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.1.6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.1.6</version>
        </dependency>

        <!-- aspectj的织入 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.22</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.16</version>
        </dependency>
        <!-- MyBatis整合Spring的适配包 -->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>6.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>

在 pom.xml 文件中引入:

<build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                    <include>**/*.java</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>

        </resources>
    </build>

创建数据库构建一些数据,建立相对应的bean类,和mapper接口,bean类我就不写了,你对应你数据库表写就行。

表sql

CREATE TABLE `money` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `basic` int DEFAULT NULL COMMENT '基本工资',
  `reward` int DEFAULT NULL COMMENT '奖金',
  `punishment` int DEFAULT NULL COMMENT '惩罚金',
  `version` int DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `did` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `dept` (
  `id` int NOT NULL AUTO_INCREMENT,
  `dept_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

mapper接口

@Mapper
public interface MoneyMapper {
//    @Select("select * from money where id = #{id}")
    Money selectMoneyById(@Param("id") int id);
}

创建mapper对应的xml文件,在接口同级目录下创建,例如:MoneyMapper.xml,这是 SQL 映射文件,封装了 SQL 代码。

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.MoneyMapper">

    <select id="selectMoneyById" resultType="org.example.pojo.Money">
        select *
        from money
        where id = #{id}
    </select>

</mapper>

在 resources 目录下创建 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>


<!--配置数据源-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/erp1?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="0000"/>
</dataSource>
</environment>
</environments>

<!--注册 mapper 文件,需要在pom文件中解决静态资源过滤的问题-->
<mappers>
<!-- 批量注册:要求sql映射文件和接口在同一目录下-->
<package name="org.example.mapper"/>
</mappers>


</configuration>

代码测试

public class MyBatisTest {

    //1、获取 sqlSessionFactory
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

  
    @Test
    public void test1() throws IOException {
        //2、获取 sqlSession
        SqlSession sqlSession = getSqlSessionFactory().openSession();

        //3、获取接口的实现类对象
        //会为接口自动创建一个代理对象,让代理对象去执行增删改查
        MoneyMapper mapper = sqlSession.getMapper(MoneyMapper.class);
        Money emp = mapper.selectMoneyById(1);
        System.out.println(emp); //输出数据库查询信息

        //4、关闭连接
        sqlSession.close();
    }
}

还有一种写法是sql语句不写在xml文件中,而是直接写在mapper接口中,如:

@Mapper
public interface MoneyMapper {
    @Select("select * from money where id = #{id}")
    Money selectMoneyById(@Param("id") int id);
}

4.MyBatis+Spring

首先在项目中加入spring的相关依赖和MyBatis-Spring 模块依赖,上面的步骤中已经在项目中加入了这些依赖。

要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。

在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean来创建 SqlSessionFactory。 要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:

  • 扩展:我们要把某个类加入到spring的IOC容器中,这里有两种写法,xml文件中和config文件中,写一种即可。两种都要掌握,推荐是写在config文件中,SpringBoot项目已经不用xml文件的形式了。

在resources目录下创建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:mybatis="http://mybatis.org/schema/mybatis-spring"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
    http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

       <!-- 数据源 -->
       <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/erp1?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8"/>
        <property name="username" value="root"/>
        <property name="password" value="0000"/>
    </bean>

    <!-- SqlSessionFactoryBean 来创建 SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>  

    <!-- 使用MapperFactoryBean 把 MoneyMapper接口添加到spring IOC容器中   -->
    <bean id="moneyMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="org.example.mapper.MoneyMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>


</beans>

等效的 Java 代码如下:

@Configuration
public class MyBatisConfig {
   @Bean
    public DataSource dataSource() {
        PooledDataSource pooledDataSource = new PooledDataSource();
        pooledDataSource.setDriver("com.mysql.cj.jdbc.Driver");
        pooledDataSource.setUrl("jdbc:mysql://localhost:3306/erp1?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
        pooledDataSource.setUsername("root");
        pooledDataSource.setPassword("0000");
        return pooledDataSource;

    }


    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource());
        factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        factoryBean.setTypeAliasesPackage("org.example.pojo");

        return factoryBean.getObject();
    }
}

注意:SqlSessionFactory 需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。

 mapper 接口:

@Mapper
public interface MoneyMapper {
    @Select("select * from money where id = #{id}")
    Money selectMoneyById(@Param("id") int id);
}

可以通过 MapperFactoryBean 将接口加入到 Spring 中,在applicationContext.xml中已经写了。

需要注意的是:所指定的映射器类必须是一个接口,而不是具体的实现类。在这个示例中,通过注解来指定 SQL 语句,但是也可以使用 MyBatis 映射器的 XML 配置文件。

配置好之后,你就可以像 Spring 中普通的 bean 注入方法那样,将映射器注入到你的业务或服务对象中。MapperFactoryBean 将会负责 SqlSession 的创建和关闭。 如果使用了 Spring 的事务功能,那么当事务完成时,session 将会被提交或回滚。最终任何异常都会被转换成 Spring 的 DataAccessException 异常。

使用 Java 代码来配置的方式如下:

@Configuration
public class MyBatisConfig {
    @Bean
    public MoneyMapper userMapper() throws Exception {
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory());
        return sqlSessionTemplate.getMapper(MoneyMapper.class);
    }
}

要调用 MyBatis 的数据方法,只需一行代码:

public interface FooService {
    public Money doSomeBusinessStuff(int id);
}
public class FooServiceImpl  implements FooService {
    @Autowired
    MoneyMapper moneyMapper;

  @Override
    public Money doSomeBusinessStuff(int id) {

        Money money = this.moneyMapper.selectMoneyById(id);
        System.out.println("money = " + money);

        return money;
    }
}

创建FooServiceImpl 对象加入IOC 容器

 <bean id="fooService" class="org.example.service.FooServiceImpl">
   
    </bean> 

测试方法

@SpringJUnitConfig(locations = {"classpath:applicationContext.xml"})
public class MyBatisTest {
    @Autowired
    FooService fooService;

      @Test
    public void test2() {
        fooService.doSomeBusinessStuff(1);

//        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//        MoneyMapper mapper = ctx.getBean(MoneyMapper.class); //
//        System.out.println(mapper.selectMoneyById(1));

    }
}

SqlSessionFactoryBean

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

要创建工厂 bean,将下面的代码放到 Spring 的 XML 配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

需要注意的是 SqlSessionFactoryBean 实现了 Spring 的 FactoryBean 接口(参见 Spring 官方文档 3.8 节 通过工厂 bean 自定义实例化逻辑 )。 这意味着由 Spring 最终创建的 bean 并不是 SqlSessionFactoryBean 本身,而是工厂类(SqlSessionFactoryBean)的 getObject() 方法的返回结果。这种情况下,Spring 将会在应用启动时为你创建 SqlSessionFactory,并使用 sqlSessionFactory 这个名字存储起来。

等效的 Java 代码如下:

@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionFactory sqlSessionFactory() {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource());
    return factoryBean.getObject();
  }
}

通常,在 MyBatis-Spring 中,你不需要直接使用 SqlSessionFactoryBean 或对应的 SqlSessionFactory。 相反,session 的工厂 bean 将会被注入到 MapperFactoryBean 或其它继承于 SqlSessionDaoSupport 的 DAO(Data Access Object,数据访问对象)中。

属性

SqlSessionFactory 有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。

一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 <settings> 或 <typeAliases> 元素。

需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(<environments>),数据源(<DataSource>)和 MyBatis 的事务管理器(<transactionManager>)都会被忽略。 SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。

如果 MyBatis 在映射器类对应的路径下找不到与之相对应的映射器 XML 文件,那么也需要配置文件。这时有两种解决办法:第一种是手动在 MyBatis 的 XML 配置文件中的 <mappers> 部分中指定 XML 文件的类路径;第二种是设置工厂 bean 的 mapperLocations 属性。

mapperLocations 属性接受多个资源位置。这个属性可以用来指定 MyBatis 的映射器 XML 配置文件的位置。属性的值是一个 Ant 风格的字符串,可以指定加载一个目录中的所有文件,或者从一个目录开始递归搜索所有目录。比如:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <!-- mybatis全局配置文件      -->
  <property name="configLocation" value="classpath:mybatis-config.xml"/>
  <!-- xml映射文件地址       -->
  <property name="mapperLocations" value="classpath*:mapper/*.xml"/>
</bean>

这会从类路径下加载所有在mappers 包和它的子包中的 MyBatis 映射器 XML 配置文件。

这时需要把mybatis全局配置文件中的package 标签注释掉。

提示 自 1.3.0 版本开始,新增的 configuration 属性能够在没有对应的 MyBatis XML 配置文件的情况下,直接设置 Configuration 实例。例如:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="configuration">
    <bean class="org.apache.ibatis.session.Configuration">
       <!-- 驼峰命名映射-->
      <property name="mapUnderscoreToCamelCase" value="true"/>
    </bean>
  </property>
</bean>

事务

一个使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。

一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。

事务配置好了以后,MyBatis-Spring 将会透明地管理事务。这样在你的 DAO 类中就不需要额外的代码了。

标准配置

要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:

 <!-- 事务相关配置    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource"/>
    </bean>
 <!-- 开启事务注解    -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

传入的 DataSource 可以是任何能够与 Spring 兼容的 JDBC DataSource。包括连接池和通过 JNDI 查找获得的 DataSource

注意:为事务管理器指定的 DataSource 必须和用来创建 SqlSessionFactoryBean 的是同一个数据源,否则事务管理器就无法工作了。

声明式事务管理

//FooServiceImpl 类中 添加方法
 
@Transactional
    @Override
    public void testTransaction() {

        int isTrue = moneyMapper.updateMoneyBasicById(1);
        System.out.println("isTrue = " + isTrue);

        //int i = 1/0;
        int isTrue1 = moneyMapper.updateMoneyBasicById1(2);
        System.out.println("isTrue1 = " + isTrue1);
    }
//MoneyMapper  接口中添加方法  

int updateMoneyBasicById(@Param("id") int id);
int updateMoneyBasicById1(@Param("id") int id);
//MoneyMapper.xml  文件中sql语句
  <update id="updateMoneyBasicById">
        update money
        set basic = basic + 100
        where id = #{id}
    </update>

    <update id="updateMoneyBasicById1">
        update money
        set basic = basic + 100
        where id = #{id}
    </update>

测试方法

@Test
public void testTransaction() {
fooService.testTransaction();
}

2条sql语句要么都修改成功,要么都修改失败。

编程式事务管理

MyBatis 的 SqlSession 提供几个方法来在代码中处理事务。但是当使用 MyBatis-Spring 时,你的 bean 将会注入由 Spring 管理的 SqlSession 或映射器。也就是说,Spring 总是为你处理了事务。

你不能在 Spring 管理的 SqlSession 上调用 SqlSession.commit()SqlSession.rollback() 或 SqlSession.close() 方法。如果这样做了,就会抛出 UnsupportedOperationException 异常。在使用注入的映射器时,这些方法也不会暴露出来。

无论 JDBC 连接是否设置为自动提交,调用 SqlSession 数据方法或在 Spring 事务之外调用任何在映射器中方法,事务都将会自动被提交。

如果你想编程式地控制事务,请参考 the Spring reference document(Data Access -Programmatic transaction management-) 。下面的代码展示了如何使用 PlatformTransactionManager 手工管理事务。

//FooServiceImpl 类中 添加方法 
  @Autowired
  DataSourceTransactionManager transactionManager;
  @Autowired
  PlatformTransactionManager platformTransactionManager;

@Override
    public void testTransactionTemplate() {

        //第一种,使用transactionManager,也就是DataSourceTransactionManager
        //或者platformTransactionManager
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        TransactionStatus transaction = transactionManager.getTransaction(definition);
        System.out.println("事务的隔离级别: " + definition.getIsolationLevel());
        definition.getPropagationBehavior();// 事务的传播行为
        definition.getTimeout();// 事务超时时间
        definition.isReadOnly();// 只读事务

        //第二种,使用 TransactionTemplate的时候,可以省略对 commit 和 rollback 方法的调用。
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        Object execute = transactionTemplate.execute(status -> {
            try {
                int isTrue = moneyMapper.updateMoneyBasicById(1);
                System.out.println("isTrue = " + isTrue);

                int i = 1/0;
                int isTrue1 = moneyMapper.updateMoneyBasicById1(2);
                System.out.println("isTrue1 = " + isTrue1);

                return Boolean.TRUE;
            } catch (Exception e) {
                status.setRollbackOnly();
                return Boolean.FALSE;
            }
        });
        System.out.println("execute = " + execute);

    }

测试方法

@Test
public void testTransaction() {
fooService.testTransactionTemplate();
}

SqlSession

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。 一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。 使用 MyBatis-Spring 之后,你不再需要直接使用 SqlSessionFactory 了,因为你的 bean 可以被注入一个线程安全的 SqlSession,它能基于 Spring 的事务配置来自动提交、回滚、关闭 session。

SqlSessionTemplate

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。 SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。

当调用 SQL 方法时(包括由 getMapper() 方法返回的映射器中的方法),SqlSessionTemplate 将会保证使用的 SqlSession 与当前 Spring 的事务相关。 此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的 DataAccessExceptions

由于模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。

可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。

    <!--    SqlSessionTemplate: 配置一个可以执行批量的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" name="sqlSessionFactory" ref="sqlSessionFactory" />
        <constructor-arg index="1" name="executorType" value="BATCH"/>
    </bean>

等效的 Java 代码如下:

@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionTemplate sqlSession() throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory());
  }
}

现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:

public class FooServiceImpl  implements FooService {
 
    @Autowired
    SqlSession sqlSession;

    @Override
    public Money sqlSessionTemplate(int id) {
        //通过 SqlSession 实例来直接执行已映射的 SQL 语句的一种写法
        return sqlSession.selectOne("org.example.mapper.MoneyMapper.selectMoneyById", id);
    }

}

测试方法:

  @Test
    public void testSqlSession() {
        
        System.out.println("fooService.sqlSessionTemplate(1) = " + fooService.sqlSessionTemplate(1));
   
    }

SqlSessionTemplate 还有一个接收 ExecutorType 参数的构造方法。这允许你使用如下 Spring 配置来批量创建对象,例如批量创建一些 SqlSession:

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
  <constructor-arg index="1" value="BATCH" />
</bean>

等效的 Java 代码如下:
@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionTemplate sqlSession() throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory(), ExecutorType.BATCH);
  }
}

现在所有的映射语句可以进行批量操作了,可以在 DAO 中编写如下的案例代码(仅供参考)

public class UserService {
  private final SqlSession sqlSession;
  public UserService(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }
  public void insertUsers(List<User> users) {
    for (User user : users) {
      sqlSession.insert("org.mybatis.spring.sample.mapper.UserMapper.insertUser", user);
    }
  }
}

注意,只需要在希望语句执行的方法与 SqlSessionTemplate 中的默认设置不同时使用这种配置。

这种配置的弊端在于,当调用这个方法时,不能存在使用不同 ExecutorType 的进行中的事务。要么确保对不同 ExecutorType 的 SqlSessionTemplate 的调用处在不同的事务中,要么完全不使用事务。

SqlSessionDaoSupport

SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法,就像下面这样:

public class FooServiceImpl  implements FooService {
    @Override
    public Money SqlSessionDaoSupport(int id) {
        return getSqlSession().selectOne("org.example.mapper.MoneyMapper.selectMoneyById", id);
    }    
 
}

在这个类里面,通常更倾向于使用 MapperFactoryBean,因为它不需要额外的代码。但是,如果你需要在 DAO 中做其它非 MyBatis 的工作或需要一个非抽象的实现类,那么这个类就很有用了。

SqlSessionDaoSupport 需要通过属性设置一个 sqlSessionFactory 或 SqlSessionTemplate。如果两个属性都被设置了,那么 SqlSessionFactory 将被忽略。

    <bean id="fooService" class="org.example.service.FooServiceImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>

映射器

与其在数据访问对象(DAO)中手工编写使用 SqlSessionDaoSupport 或 SqlSessionTemplate 的代码,还不如让 Mybatis-Spring 为你创建一个线程安全的映射器,这样你就可以直接注入到其它的 bean 中了

注册映射器

注册映射器的方法根据你的配置方法,即经典的 XML 配置或新的 3.0 以上版本的 Java 配置(也就是常说的 @Configuration),而有所不同。

XML 配置

在前面使用 MapperFactoryBean 以便将MoneyMapper映射器注册到 Spring 中。就像下面一样:

    <bean id="moneyMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="org.example.mapper.MoneyMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

如果映射器接口 UserMapper 在相同的类路径下有对应的 MyBatis XML 映射器配置文件,将会被 MapperFactoryBean 自动解析。不需要在 MyBatis 配置文件中显式配置映射器,除非映射器配置文件与接口类不在同一个类路径下。

注意 MapperFactoryBean 需要配置一个 SqlSessionFactory 或 SqlSessionTemplate。它们可以分别通过 sqlSessionFactory 和 sqlSessionTemplate 属性来进行设置。 如果两者都被设置,SqlSessionFactory 将被忽略。由于 SqlSessionTemplate 已经设置了一个 session 工厂,MapperFactoryBean 将使用那个工厂。

等效的 Java 代码如下:

@Configuration
public class MyBatisConfig {
    @Bean
    public MapperFactoryBean<MoneyMapper> moneyMapper() throws Exception {
        MapperFactoryBean<MoneyMapper> factoryBean = new MapperFactoryBean<>(MoneyMapper.class);
        factoryBean.setSqlSessionFactory(sqlSessionFactory());
        return factoryBean;
    }
}

这里就有一个问题,如果我们的Mapper不止一个,有很多个,怎么办?

发现映射器

不需要一个个地注册你的所有映射器。你可以让 MyBatis-Spring 对类路径进行扫描来发现它们。

有几种办法来发现映射器:

  • 使用 <mybatis:scan/> 元素
  • 使用 @MapperScan 注解
  • 在经典 Spring XML 配置文件中注册一个 MapperScannerConfigurer

在spring的配置文件中添加:

<!--    第一种方式 <mybatis:scan/> 元素会发现映射器,它发现映射器的方法与 Spring 内建的 <context:component-scan/> 发现 bean 的方法非常类似。-->

        <mybatis:scan base-package="org.example.mapper" />
 <!--    第三种方式-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="org.example.mapper" />
        </bean>

如果你需要指定 sqlSessionFactory 或 sqlSessionTemplate,那你应该要指定的是 bean 名而不是 bean 的引用,因此要使用 value 属性而不是通常的 ref 属性:

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />

提示 在 MyBatis-Spring 1.0.2 之前,sqlSessionFactoryBean 和 sqlSessionTemplateBean 属性是唯一可用的属性。 但由于 MapperScannerConfigurer 在启动过程中比 PropertyPlaceholderConfigurer 运行得更早,经常会产生错误。基于这个原因,上述的属性已被废弃,现在建议使用 sqlSessionFactoryBeanName 和 sqlSessionTemplateBeanName 属性。

@MapperScan

当你正在使用 Spring 的基于 Java 的配置时(也就是 @Configuration),相比于使用 <mybatis:scan/>,你会更喜欢用 @MapperScan

@MapperScan 注解的使用方法如下,在我们配置文件类上面添加注解

@Configuration
@ComponentScan("org.example")     // 扫描其他组件
@MapperScan("org.example.mapper") // 添加MyBatis mapper扫描
@EnableTransactionManagement // 开启事务注解
@EnableAspectJAutoProxy(proxyTargetClass = true)//启用aspect J自动代理
public class MyBatisConfig {

}

5.全局配置文件

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。

配置文档的顶层结构如下:

properties 标签

<!--dbconfig.properties文件-->
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=12345678
<!--
mybatis可以使用properties来引入外部properties配置文件的内容
  resource:引入类路径下的资源
  url:引入网络路径或者磁盘路径下的资源
-->
<properties resource="dbconfig.properties"/>

<!--获取外部properties配置文件中的值-->
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>

settings 标签

<!--设置项,比如驼峰命名映射-->
    <settings>
        <!-- 驼峰命名映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--jdbcTypeForNull 设置为 NULL-->
        <setting name="jdbcTypeForNull" value="NULL"/>
        <!--日志输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--开启懒加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--关闭积极加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="false"/>
        <!--开启二级缓存,默认是SESSION,表示在当前会话中有效,如果设置为STATEMENT,则表示在当前语句中有效-->
        <setting name="localCacheScope" value="SESSION"/>
        <!--自增长key-->
        <setting name="useGeneratedKeys" value="true"/>
    </settings>

typeAliases 标签与@Alias注解

<typeAliases>
    <!--
    1、typeAlias:为某个java类型起别名
    type:指定要起别名的类型全类名;默认别名就是类名小写;employee
    alias:指定新的别名
    -->
<!-- <typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/>-->

    <!--
    2、package:为某个包下的所有类批量起别名
    name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写))
    -->
    <package name="com.atguigu.mybatis.bean"/>
    
    <!-- 3、批量起别名的情况下,在类的上面使用@Alias注解为某个类指定新的别名 -->
</typeAliases>

environments 标签

environments:环境们,mybatis 可以配置多种环境 ,default指定使用某种环境。可以达到快速切换环境。

environment:配置一个具体的环境信息;必须有两个标签;id 代表当前环境的唯一标识。

transactionManager:事务管理器,type:事务管理器的类型:

  • JDBC(JdbcTransactionFactory)
  • MANAGED(ManagedTransactionFactory)
  • 自定义事务管理器:实现TransactionFactory接口.type指定为全类名

dataSource:数据源,type:数据源类型:

  • UNPOOLED(UnpooledDataSourceFactory)
  • POOLED(PooledDataSourceFactory)
  • JNDI(JndiDataSourceFactory)
  • 自定义数据源:实现DataSourceFactory接口,type是全类名
<environments default="dev_mysql">
  <environment id="dev_mysql">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
    </dataSource>
  </environment>
  <environment id="dev_oracle">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
      <property name="driver" value="${orcl.driver}"/>
      <property name="url" value="${orcl.url}"/>
      <property name="username" value="${orcl.username}"/>
      <property name="password" value="${orcl.password}"/>
    </dataSource>
  </environment>
</environments>

databaseIdProvider 标签

databaseIdProvider:支持多数据库厂商的

type="DB_VENDOR" 代表 VendorDatabaseIdProvider,作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),MyBatis 就能根据数据库厂商标识来执行不同的 sql,比如:MySQL,Oracle,SQL Server等等。

<databaseIdProvider type="DB_VENDOR">
    <!-- 为不同的数据库厂商起别名 -->
    <property name="MySQL" value="mysql"/>
    <property name="Oracle" value="oracle"/>
    <property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>

使用的时候,在mapper.xml文件中,databaseId 属性指明即可:

<mapper namespace="com.atguigu.mybatis.mapper.EmployeeMapper">
    <select id="getEmpById" resultType="emp" databaseId="mysql">
        select * from tbl_employee where id = #{id}
    </select>
</mapper>

mappers 标签

我们写好的sql映射文件(xxxMapper.xml)一定要注册到全局配置文件中才能起作用。

mapper:注册一个 sql 映射

  • 注册配置文件
  • resource:引用类路径下的 sql 映射文件,没有要求必须与接口放在一起。
  • url:引用网路路径或者磁盘路径下的sql映射文件。
  • 注册接口
  • class:引用(注册)接口。
  • 如果有 sql 映射文件,则映射文件名必须和接口同名,并且放在与接口同一目录下。
  • 如果没有 sql 映射文件,所有的 sql 都是利用注解写在接口上的。

推荐: 比较重要的复杂的Dao接口我们来写sql映射文件,不重要的简单的Dao接口为了开发快速可以使用注解。

<mappers>
    <mapper resource="com/jw/mybatis/mapper/EmployeeMapper.xml"/>
    <mapper class="com.jw.mybatis.mapper.EmployeeMapperAnnotation"/>
    
    <!-- 批量注册:要求sql映射文件和接口在同一目录下-->
    <package name="com.jw.mybatis.mapper"/>
</mappers>

分类: MyBatis

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注