Skip to content

MyBatis

MyBatis 框架使得在面向对象的应用程序中能更加容易使用关系数据库。 MyBatis 使用 XML 描述符或注释将对象与存储过程或 SQL 语句结合起来。 简单是 MyBatis 的最大优势。

GitHub | Documentation

最新版本:

安装

mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。

xml
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>
kotlin
implementation("org.mybatis:mybatis:x.x.x")
toml
mybatis = { group = "org.mybatis", name = "mybatis", version = "x.x.x"}

构建 SqlSessionFactory

SqlSessionFactory 是每一个基于 MyBatis 的应用的核心。 其实例可以通过 SqlSessionFactoryBuilder 获得。 而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

从 XML 中构建 SqlSessionFactory

可以从任意 XML 输入流(InputStream)构建 SqlSessionFactory,MyBatis 提供了一个 Resources 工具类,可以更方便的加载资源文件。

创建 db.properties 配置数据库连接信息:

properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=root

创建 mybatis-config.xml 文件:

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

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

创建 SqlSessionFacotry 对象:

java
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
在 Java 代码中直接构建 SqlSessionFactory
java
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

获取 SqlSession

通过调用 SqlSessionFactoryopenSession 方法获取 SqlSession 对象。

java
try (SqlSession session = sqlSessionFactory.openSession()) {
    // TODO ...
}
用户表 DDL 和对应实体类

DDL 语句

sql
create table tb_user
(
    id int primary key auto_increment,
    nickname varchar(32)
);

User 类

java
package icu.twtool.entity;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class User {

    private Integer id;
    private String nickname;

}

创建 UserMapper.xml 文件:

xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="icu.twtool.mapper.UserMapper">
    <select id="selectOne" resultType="icu.twtool.entity.User">
        select * from tb_user where id = #{id}
    </select>
</mapper>

namesapce 的作用

  1. 利用更长的全限定名来将不同的语句隔离开来(对于全局唯一的 id 则可以不需要 namespace 前缀,否则需要带上前缀用于区分)
  2. 实现和 Mapper 接口的绑定(需要绑定 Mapper 接口则需要将 namespace 指定为接口的全限定名)

将 Mapper 文件添加到 SqlSessionFactoryBuilder 中。

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

通过 SqlSession 提供的方法直接执行映射的 SQL 语句

java
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
    User user = session.selectOne("selectOne", 1);
    assertEquals(user, User.builder().id(1).nickname("lisi").build());
}

通过 Mapper 接口查询

创建 UserMapper.java 接口

java
package icu.twtool.mapper;

import icu.twtool.entity.User;

public interface UserMapper {
    User selectOne(Integer id);
}

使用 getMapper 方法获取到对应接口的代理实现类

java
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.selectOne(1);
    assertEquals(user, User.builder().id(1).nickname("lisi").build());
}

MyBatis 常用配置

Configuration

文件头

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

</configuration>

日志输出

支持: SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

xml
<setting name="logImpl" value="SLF4J"/>

下划线转驼峰映射

xml
<setting name="mapUnderscoreToCamelCase" value="true"/>

自动生成主键

xml
<setting name="useGeneratedKeys" value="true"/>

类型别名

为 Java 类型指定一个简短名称。

xml
<typeAliases>
  <typeAlias alias="User" type="icu.twtool.entity.User"/>
  <!-- 自动映射包下的所有类 -->
  <package name="icu.twtool.entity"/>
</typeAliases>
使用 @Alias 注解指定别名(需要和 package 组合使用)

默认使用 package 自动映射的别名为类名,可通过注解修改。

java
@Alias("user")
public class User {

    private Integer id;
    private String nickname;

}
MyBatis 为部分类型提供了内置别名
别名类型
_bytebyte
_char (since 3.5.10)char
_character (since 3.5.10)char
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
char (since 3.5.10)Character
character (since 3.5.10)Character
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
bigintegerBigInteger
objectObject
date[]Date[]
decimal[]BigDecimal[]
bigdecimal[]BigDecimal[]
biginteger[]BigInteger[]
object[]Object[]
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

类型处理器(typeHandlers)

可在 类型处理器(typeHandlers) 中查看 MyBatis 内置的类型处理器。

⚠️ 注意

MyBatis 从 3.4.5 开始才支持 JSR-310(Date and Time API)

可以重写内置的类型处理器或者创建自定义类型处理器, 通过实现 org.apache.ibatis.type.TypeHandler 接口或者继承 org.apache.ibatis.type.BaseTypeHandler 类来实现。

自定义通用枚举处理

创建通用枚举接口

java
package icu.twtool.core;

public interface IEnum {

    int code();
}

修改用户表,和数据

sql
alter table tb_user add sex int;
update tb_user set sex = 1 where id = 1;

创建性别枚举

java
package icu.twtool.enumerate;

import icu.twtool.core.IEnum;

public enum Sex implements IEnum {
    FEMALE(0),
    MALE(1);

    private final int code;

    Sex(int code) {
        this.code = code;
    }

    @Override
    public int code() {
        return code;
    }
}

修改 User 类

java
package icu.twtool.entity;

import icu.twtool.enumerate.Sex;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class User {

    private Integer id;
    private String nickname;

    private Sex sex; 
}

创建枚举处理器

java
package icu.twtool.handlers;

import icu.twtool.core.IEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

@MappedJdbcTypes(JdbcType.INTEGER) // 可选的,自定义映射的 JDBC 类型
public class EnumTypeHandler<E extends IEnum> extends BaseTypeHandler<E> {

    private final Map<Integer, E> map = new HashMap<>();

    public EnumTypeHandler(Class<E> clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("Clazz argument cannot be null");
        }
        if (!clazz.isEnum()) {
            throw new IllegalArgumentException("Clazz argument must be an enum");
        }
        for (E constant : clazz.getEnumConstants()) {
            map.put(constant.code(), constant);
        }
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.code());
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int code = rs.getInt(columnName);
        return map.get(code);
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int code = rs.getInt(columnIndex);
        return map.get(code);
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int code = cs.getInt(columnIndex);
        return map.get(code);
    }
}

然后在配置文件中设置:

xml
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler" javaType="icu.twtool.enumerate.Sex"/>
</typeHandlers>

Mapper

支持的子标签有:

  • cache: 该命名空间的缓存配置。
  • cache-ref: 引用其它命名空间的缓存配置。
  • resultMap: 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • parameterMap: 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
  • sql: 可被其它语句引用的可重用语句块。
  • insert: 映射插入语句。
  • update: 映射更新语句。
  • delete: 映射删除语句。
  • select: 映射查询语句。

文件头

xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="namespace">

</mapper>

查询语句(select)是 MyBatis 中最常用的元素之一。

支持的参数
  • id: 在命名空间中唯一的标识符,可以被用来引用这条语句。
  • parameterType: 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以根据语句中实际传入的参数计算出应该使用的类型处理器(TypeHandler),默认值为未设置(unset)。
  • parameterMap: 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
  • resultType: 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultTyperesultMap 之间只能同时使用一个。
  • resultMap: 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultTyperesultMap 之间只能同时使用一个。
  • flushCache: 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
  • useCache: 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
  • timeout: 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
  • fetchSize: 这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
  • statementType: 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
  • resultSetType: FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
  • databaseId: 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
  • resultOrdered: 这个设置仅针对嵌套结果 select 语句:如果为 true,则假设结果集以正确顺序(排序后)执行映射,当返回新的主结果行时,将不再发生对以前结果行的引用。 这样可以减少内存消耗。默认值:false。
  • resultSets: 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。
  • affectData: 在编写返回数据的 INSERT、UPDATE 或 DELETE 语句时将其设置为 true,以便正确控制事务。另请参阅事务控制方法。默认值: false(自 3.5.12 起)

增删改