Mybatis学习入门

231 阅读7分钟

Mybatis简介

MyBatis 是一流的持久性框架,支持自定义 SQL、存储过程和高级映射。MyBatis 消除了几乎所有的 JDBC 代码和手动设置参数和检索结果。MyBatis 可以使用简单的 XML 或 Annotations 进行配置和映射原语、映射接口和 Java POJO(普通旧 Java 对象)到数据库记录。

Mybatis 安装

<dependency> 
    <groupId> org.mybatis </groupId> 
    <artifactId> mybatis </artifactId> 
    <version> xxx </version> 
</dependency>

核心概念

SqlSessionFactory

每个基于Mybatis的应用程序都是以一个SqlSessionFacotry的实例为核心的(如果是多数据源, 应该是每个数据源对应一个)。SeqSessionFactory的实例可以通过SqlSessionFacotryBuilder获得。

  • 主要作用 SqlSessionFactory的主要作用为维护数据源的会话连接, 即创建SqlSession

  • 创建方式

    • XML配置文件创建

    从XML配置文件创建SqlSessionFactory需要一个XML配置文件, 可以放在classpath下的资源文件中, 但也可以选择任意的输入流(InputStream)实例。 示例如下:

    String resources = "mybatis-config.xml"
    Inputstream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFacotryBuilder().buld(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    

    关键在于Mybatis的配置文件, 它里面定义了Mybatis里面核心的几个要素如:DataSource、Mapper、TypeAlias等等,详见配置部分。

    • 代码创建

    从代码创建SqlSessionFacotry主要是通过一个Configuration实例来构建, Configiuration的内容和XML配置文件的内容等价, XML配置文件本身是通过一个XMLConfigBuilder来解析XML配置文件获得一个Configuation对象。

    示例如下:

      DataSource dataSource = new PooledDataSource();
      TransactionFactory transactionFactory = new ManagedTransactionFactory();
      Environment environment =new Environment("prod", transactionFactory, dataSource);
      Configuration configuration =new Configuration();
      configuration.setEnvironment(environment);
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
      SqlSession sqlSession = sqlSessionFactory.openSession();
    
  • 核心方法

    • openSession(): 创建SqlSession实例。

      SqlSessionFactory有6个方法创建SqlSession实例, 创建时需要考虑以下几点:

      • 事务处理: 使用事务作用域还是自动提交。
      • 数据库连接: 使用已配置的数据库连接, 还是使用自己提供的连接。
      • 语句执行: 是否复用PreparedStatement或批量更新语句。 它主要有几个核心的参数:
      • boolean autoCommit: 是否开启自动提交。
      • TransactionIsolationLevel level: 事务隔离级别。
      • ExecutorType executorType: 执行器的行为, 包含3种:
        • SIMPLE:使用新的预处理语句。
        • REUSE: 复用预处理语句,
        • BATCH: 批量执行更新语句。 默认的无参openSession()方法会开启事务,具备以下特性:
      • 从配置中获取连接对象.
      • 使用数据库的事务隔离级别.
      • 开启事务(不自动提交).
      • 每次使用新的预处理语句.
    • getConfiguration(): 获取配置。
  • 作用域 应用生命周期, 不要重复创建, 最好使用单例模式。

SqlSession

  • 主要作用: Mybatis的主要接口, 会话实例, 用来

    • 获取映射器实例
    • 管理事务
    • 执行SQL语句
  • 创建方式: 通过SqlSessionFactoryopenSession方法创建。

  • 核心方法 SqlSession是Mybatis的核心接口, 主要包含三类用途的方法:

    1. 执行SQL命令。
      • select: 执行一条查询语句。
      • selectOne: 查询一条数据。
      • selectList: 查询多条数据,返回一个列表。
      • selectMap: 查询结果返回Map类型值。
      • selectCurosr: 返回一个游标, 允许数据惰性加载。
      • insert: 插入数据。
      • update: 更细数据。
      • delete: 删除数据。
    2. 事务管理。
      • commit: 提交事务。
      • rollback: 回滚事务。
    3. 获取映射器实例。
      • getMapper: 获取映射器实例。
  • 作用域

    SqlSession是线程不安全的, 因为不能线程共享, 因此每个线程都需要拥有自己的SqlSession实例, 最佳方式是请求作用域或方法作用域。 SqlSession使用完后需要关闭。

SqlSessionFactoryBuilder

  • 主要作用

    创建SqlSessionFactory, 它支持5个build方法从不同地方加载配置,可以总结为2类:

    • build(InputStream stream): 它及它的变形支持使用不同手段从配置文件加载SqlSessionFactory
    • build(Configuration configuration): 它使用一个Configuration实例来创建, 一般是通过代码方式。

    mybatis-config.xml中支持引用属性值, 也支持直接指定属性值, 属性值的加载优先级如下从高到低如下:

    • 参数中指定的属性值。
    • propeties中resources或url指定的属性值。
    • properties中指定的属性。
  • 作用域 它的主要作用是初始化SqlSessionFactory, 一旦初始化完成就不再需要它了, 最佳的作用域是方法作用域。 他可以复用创建多个SqlSessionFactory, 但是使用完最好释放,避免占用资源。

映射器(Mapper)

映射器(Mapper)是绑定映射语句的接口, 映射器的实例从SqlSession获得。它的最佳作用域是方法作用域。

示例如下:

public interface AuthorMapper {
  Author selectAuthor(int id);
  
  List<Author> selectAuthors();
  
  @MapKey("id")
  Map<Integer, Author> selectAuthors();
  
  int insertAuthor(Author author);
  
  int updateAuthor(Author author);
  
  int deleteAuthor(int id);
}

本地缓存

Mybatis使用到了2种缓存: 本地缓存二级缓存

  • 本地缓存SqlSession创建时, 会创建一个与之关联的本地缓存, 同一session内部执行过的查询结果会缓存在本地缓存中, 再次执行相同查询时,就不需要再查询数据库了。

  • 二级缓存

Mybatis 自身配置

Mybatis配置文件包含了会影响Mybatis行为的设置和属性信息,从顶层结构看,主要包含:

  • configuration: 最顶层配置
    • properties: 属性
    • settings: 设置
    • typeAliases: 类型别名
    • typeHanders: 类型处理器
    • objectFactory: 对象工厂
    • plugins: 插件
    • enviroments: 环境配置
      • enviroment: 环境变量
        • dataSource: 数据源
        • transactionManager: 事务管理器
    • databaseIdProvider: 数据库厂商标示
    • mappers: 映射器

properties (属性)

properties简介

属性(properties)用来设置额外属性, 比如数据库连接所需的参数等等。

属性(properties)既可以在Mybatis配置文件中设置, 也可以引用外部的Java属性文件, 还可以创建SqlSessionFactory时指定Java Properties实例。

示例1:引用外部的db.properties文件覆盖properties元素设置的属性值。

<properties resource="db.properties">
    <property name="username" value="test"/>
    <property name="password" value="test.123"/>
    <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>

示例2: 代码中指定Properties实例

 Properties properties = new Properties();
 properties.put("username", "test");
InputStream inputStream =  Resources.getResourceAsStream("mybatis-config.xml");
 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream, properties);

propeties 作用

属性(properties)主要用来设置Mybatis依赖的属性值, 比如设置MybatisdataSource

<environments default="test">
    <environment id="test">
        <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>

所有依赖外部属性值设置的地方都可以使用属性(properties)中定义的属性。

properties加载优先级

属性(properties)加载时,遵从以下顺序:

  1. 读取mybatis配置文件中的指定属性。
  2. 读取mybatis引入的外部Java 属性文件中的特定属性值, 如有则覆盖。
  3. 读取方法传递的属性值, 如有则覆盖。

properties设置默认值

属性(properties)设置属性值需要主动开启, 开启方式如下:

<properties resource="db.properties">
    <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>

使用方式如下:

<property name="username" value="${username:test}"/>

settings (设置)

设置(settings)用来设置Mybatis的自身行为, 非常重要, 可以用来做参数优化。

settings的配置方式如下:

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

支持的设置参数查看官方文档。

typeAlias (类型别名)

顾名思义, typeAlias用来设置类型别名, 目的是解决配置文件中类型名称冗长的问题, 使用方式如下:

示例1: 使用Author代替冗长的类型名称org.good.java.mybatis.dto.Author

<typeAliases>
    <typeAlias alias="Author" type="org.good.java.mybatis.dto.Author" />
</typeAliases>

示例2:使用package定义一个包名, Mybatis会在指定包名下搜索指定的类。

<typeAliases>
    <typeAlias alias="Author" type="org.good.java.mybatis.dto.Author" />
    <package name="org.good.java.mybatis.dto"/>
</typeAliases>

Mybatis也提供了注解@Alias来设置别名。

类型处理器(typeHandlers)

Mybatis支持自定义类型处理器解决不支持或不标准的类型处理器, 原理是在PreparedStatement中的参数或结果集取出一个值时, 使用类型处理器对数据进行转换。

自定义类型处理器有两种方式:

  1. 实现org.apache.ibatis.type.TypeHandler接口.
  2. 继承org.apache.ibatis.type.BaseTypeHandler类.

它有4个核心方法,分为2类:

  • setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType: 将PreparedStatement参数转换为数据库支持的类型。
  • T getResult(ResultSet rs, String columnName): 将查询结果的字段由数据库类型转换为Java类型。

它有2个重要的注解:

  • @MappedTypes: 指定映射的Java类型
  • @MappedJdbcTypes: 指定映射的数据库类型。

示例1:实现一个JSON类型处理器

@MappedJdbcTypes(JdbcType.VARCHAR)
public class JSONTypeHandler<T extends Object> implements TypeHandler<T> {

    private static final Logger LOGGER = LoggerFactory.getLogger(JSONTypeHandler.class);

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();


    private Class<T> tClass;

    public JSONTypeHandler(Class<T> tClass){
        if(tClass==null){
            throw new RuntimeException("class is null");
        }
        this.tClass = tClass;
    }

    private String toJSON(T t){
        try{
            return OBJECT_MAPPER.writeValueAsString(t);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    private T toT(String str){
        try{
            return OBJECT_MAPPER.readValue(str, tClass);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }


    @Override
    public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, toJSON(parameter));
    }

    @Override
    public T getResult(ResultSet rs, String columnName) throws SQLException {
        String s = rs.getString(columnName);
        return s == null ? null : toT(s);
    }

    @Override
    public T getResult(ResultSet rs, int columnIndex) throws SQLException {
        String s = rs.getString(columnIndex);
        return s == null ? null : toT(s);
    }

    @Override
    public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String s = cs.getString(columnIndex);
        return s == null ? null : toT(s);
    }
}

使用方式

  • 在XML配置中使用
<typeHandler handler="org.good.java.mybatis.typehandler.JSONTypeHandler" javaType="org.good.java.mybatis.dto.BodyContext" jdbcType="VARCHAR"/>

typeHandler袁术还可以设置package子元素指定扫描的包。

  • 在Java代码中使用
@Result(column = "body", property = "context", jdbcType = JdbcType.VARCHAR, typeHandler = JSONTypeHandler.class)

对象工厂(objectFacotry)

对象工厂用来实例化新的对象实例, 默认的对象工厂仅实现目标实例, 有两种方式:

  1. 通过无参构造函数实现
  2. 通过参数映射调用带有参数的构造方法。

插件(Plugins)

mybatis允许通过插件来拦截mabatis的执行过程, 默认允许拦截的点包括:

  • Executor: query/update/delete等sql操作。
  • ParameterHandler: get/set参数操作。
  • ResultSetHandler: 处理resultSet、出参等操作。
  • StatementHandler: statement的各类操作。

插件的作用有很多, 比如:

  • 埋点监控
  • 修改或增强类的行为

Plugin的实现方式首先需要定义一个继承 org.apache.ibatis.plugin.Interceptor的类声明拦截的行为,同时通过注解@Intercepts声明要拦截的目标。

  • @Intercepts注解可以声明多个@Signature定义要注解的方法,@Signature有三个属性typemethodargs, 分别对应拦截目标的Class、方法名称、方法参数。
  • Interceptor类声明拦截的行为, 核心方法intercept(Invocation invocation)通过AOP的方式代理目标对象,可以在该方法中定义要执行的行为。

示例:拦截query方法并输出日志

@Intercepts(
        value = {
                @Signature(
                        type = Executor.class,
                        method = "query",
                        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
                )

        }
)
public class ExamplePlugin implements Interceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExamplePlugin.class);
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        LOGGER.info("args: {}, method:{}, target:{}", invocation.getArgs(), invocation.getMethod(), invocation.getTarget());
        return invocation.proceed();
    }


    @Override
    public void setProperties(Properties properties) {

    }
}

环境配置(enviroments)

Mybatis可以配置成适应多环境, 适应开发、测试、预发、上线的不同环境需求。虽然可以设置多个环境, 但是每个SqlSessionFacotry只能对应一个环境、一个数据库。

enviroments的配置如下:

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <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>

它可以包含多个enviroment元素, 每个对应一个环境的配置,使用时根据id获取对应环境的enviroment配置。每个enviroment配置包含2个重要的子元素:

  • transactionManager:事务管理器,支持JDBCMANAGED2种类型。
    • JDBC: 直接使用JDBC的提交和回滚功能,依赖于从数据库获取的连接管理事务作用域。
    • MANAGED: 什么也不做,交给容器来管理事务作用域。
  • dataSource: 数据源配置, 使用标准的JDBC数据源配置, 支持3种内建的数据源类型:
    • UNPPOLED: 不使用数据库连接池, 每次请求打开和关闭连接。
    • POOLED: 使用数据库连接池。
    • JNDI: 应用在EJB或其他容器中。

示例:

<environments default="test">
    <environment id="test">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username:test}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

数据库厂商标识(databaseIdProvider)

数据库厂商标识的作用是发挥不同数据库的特性, 如果设置了它, 执行语句时会根据不同的数据库厂商执行不同的SQL语句。设置方式如下:

<databaseIdProvider type="DB_VENDOR" />

映射器(mappers)

映射器(mappers)是mybatis中的核心概念, 它主要用来定义SQL映射语句,mappers的配置方式:

示例1:设置SQL映射文件的资源路径, 可以是相对和绝对路径

<mappers>
    <mapper resource="mappers/test/AuthorMapper.xml"/>
</mappers>

示例2: 设置SQL映射实现类的完全类限定名

<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

示例3: 将包内的映射器接口全部定义为映射器

<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

以上Mybatis自身的核心配置就至此结束了,其中普遍会用到的配置有以下几个:

  • 配置(properties)
  • 类型别名(typeAlias)
  • 环境配置 (enviroments)
  • 映射器 (mappers)

Mybatis Mapper配置

Spring Mybatis

Mybatis实战示例

首先创建示例表:

image.png

创建数据表对应Java类型映射:

public class Author {

    private long id;

    private String userId;

    private String userName;

    @Override
    public String toString() {
        return "Author{" +
                "id=" + id +
                ", userId='" + userId + ''' +
                ", userName='" + userName + ''' +
                '}';
    }
}

Java Code

  1. 创建Mapper映射器
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.type.JdbcType;
import org.good.java.mybatis.dto.Author;

public interface AuthorMapper {

    @Select("select * from author where id = #{id}")
    @Results(
            id = "author",
            value = {
                    @Result(column = "id", property = "id", id = true, jdbcType = JdbcType.BIGINT),
                    @Result(column = "userid", property = "userId", jdbcType = JdbcType.VARCHAR),
                    @Result(column = "username", property = "userName", jdbcType = JdbcType.VARCHAR)
            }
    )
    Author get(@Param("id") long id);
}
  1. 执行SQL

import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.good.java.mybatis.dto.Author;
import org.good.java.mybatis.mapper.AuthorMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;

public class Main {

    private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://127.0.0.1:3306/test";
        String username = "test";
        String password = "test";
        // 创建DataSource
        DataSource dataSource = new PooledDataSource(driver, url, username, password);
        TransactionFactory transactionFactory = new ManagedTransactionFactory();
        Environment environment =new Environment("prod", transactionFactory, dataSource);
        // 创建Configuration
        Configuration configuration =new Configuration();
        // 添加Mapper映射
        configuration.addMapper(AuthorMapper.class);
        configuration.setEnvironment(environment);
        // 创建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        // 创建sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 获取AuthorMapper实例
        AuthorMapper authorMapper = sqlSession.getMapper(AuthorMapper.class);
        // 执行SQL
        Author author = authorMapper.get(1);
        LOGGER.info(String.valueOf(author));
    }
}
  • 输出结果
2022-06-04 16:55:29.187	 [main:1{pid=1648, priority=5, ThreadContext={}}] org.good.java.mybatis.Main:main[41] INFO: Author{id=1, userId='12345', userName='test'} 

XML Code

  1. 创建映射接口类

public interface AuthorMapper {

    Author get(long id);
}
  1. 创建映射配置XML文件
<?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="org.good.java.mybatis.mapper2.AuthorMapper">
    <resultMap id="author" type="Author">
        <result column="id" property="id"/>
        <result column="userid" property="userId"/>
        <result column="username" property="userName"/>
    </resultMap>

    <select id="get" resultMap="author" parameterType="long" >
        select * from author where id = #{id}
    </select>
</mapper>
  1. 执行SQL
public class Main2 {

    private static final Logger LOGGER = LoggerFactory.getLogger(Main2.class);

    public static void main(String[] args) throws IOException {

        Properties properties = new Properties();
        properties.put("username", "test");
       InputStream inputStream =  Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream, properties);
        SqlSession sqlSession = sessionFactory.openSession();

        AuthorMapper authorMapper = sqlSession.getMapper(AuthorMapper.class);
        Author author = authorMapper.get(1);
        LOGGER.info(String.valueOf(author));
    }
}

输出结果如下:

2022-06-11 12:03:08.826	 [main:1{pid=19540, priority=5, ThreadContext={}}] org.good.java.mybatis.Main2:main[31] INFO: Author{id=1, userId='12345', userName='test'}  

Spring(单数据源)

Spring (多数据源)