MyBatis
MyBatis 框架使得在面向对象的应用程序中能更加容易使用关系数据库。 MyBatis 使用 XML 描述符或注释将对象与存储过程或 SQL 语句结合起来。 简单是 MyBatis 的最大优势。
安装
将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
implementation("org.mybatis:mybatis:x.x.x")
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
配置数据库连接信息:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=root
创建 mybatis-config.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
对象:
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
在 Java 代码中直接构建 SqlSessionFactory
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
获取 SqlSession
通过调用 SqlSessionFactory
的 openSession
方法获取 SqlSession 对象。
try (SqlSession session = sqlSessionFactory.openSession()) {
// TODO ...
}
用户表 DDL 和对应实体类
DDL 语句
create table tb_user
(
id int primary key auto_increment,
nickname varchar(32)
);
User 类
package icu.twtool.entity;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User {
private Integer id;
private String nickname;
}
创建 UserMapper.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
的作用
- 利用更长的全限定名来将不同的语句隔离开来(对于全局唯一的 id 则可以不需要 namespace 前缀,否则需要带上前缀用于区分)
- 实现和 Mapper 接口的绑定(需要绑定 Mapper 接口则需要将 namespace 指定为接口的全限定名)
将 Mapper 文件添加到 SqlSessionFactoryBuilder 中。
<?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 语句
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
接口
package icu.twtool.mapper;
import icu.twtool.entity.User;
public interface UserMapper {
User selectOne(Integer id);
}
使用 getMapper
方法获取到对应接口的代理实现类
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 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
<setting name="logImpl" value="SLF4J"/>
下划线转驼峰映射
<setting name="mapUnderscoreToCamelCase" value="true"/>
自动生成主键
<setting name="useGeneratedKeys" value="true"/>
类型别名
为 Java 类型指定一个简短名称。
<typeAliases>
<typeAlias alias="User" type="icu.twtool.entity.User"/>
<!-- 自动映射包下的所有类 -->
<package name="icu.twtool.entity"/>
</typeAliases>
使用 @Alias
注解指定别名(需要和 package 组合使用)
默认使用 package 自动映射的别名为类名,可通过注解修改。
@Alias("user")
public class User {
private Integer id;
private String nickname;
}
MyBatis 为部分类型提供了内置别名
别名 | 类型 |
---|---|
_byte | byte |
_char (since 3.5.10) | char |
_character (since 3.5.10) | char |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
char (since 3.5.10) | Character |
character (since 3.5.10) | Character |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
biginteger | BigInteger |
object | Object |
date[] | Date[] |
decimal[] | BigDecimal[] |
bigdecimal[] | BigDecimal[] |
biginteger[] | BigInteger[] |
object[] | Object[] |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
类型处理器(typeHandlers)
可在 类型处理器(typeHandlers) 中查看 MyBatis 内置的类型处理器。
⚠️ 注意
MyBatis 从 3.4.5 开始才支持 JSR-310(Date and Time API)
可以重写内置的类型处理器或者创建自定义类型处理器, 通过实现 org.apache.ibatis.type.TypeHandler
接口或者继承 org.apache.ibatis.type.BaseTypeHandler
类来实现。
自定义通用枚举处理
创建通用枚举接口
package icu.twtool.core;
public interface IEnum {
int code();
}
修改用户表,和数据
alter table tb_user add sex int;
update tb_user set sex = 1 where id = 1;
创建性别枚举
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 类
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;
}
创建枚举处理器
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);
}
}
然后在配置文件中设置:
<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 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
: 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。resultType
和resultMap
之间只能同时使用一个。resultMap
: 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。resultType
和resultMap
之间只能同时使用一个。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 起)