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语句
-
创建方式: 通过
SqlSessionFactory的openSession方法创建。 -
核心方法
SqlSession是Mybatis的核心接口, 主要包含三类用途的方法:- 执行SQL命令。
select: 执行一条查询语句。selectOne: 查询一条数据。selectList: 查询多条数据,返回一个列表。selectMap: 查询结果返回Map类型值。selectCurosr: 返回一个游标, 允许数据惰性加载。insert: 插入数据。update: 更细数据。delete: 删除数据。
- 事务管理。
commit: 提交事务。rollback: 回滚事务。
- 获取映射器实例。
getMapper: 获取映射器实例。
- 执行SQL命令。
-
作用域
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依赖的属性值, 比如设置Mybatis的dataSource。
<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)加载时,遵从以下顺序:
- 读取
mybatis配置文件中的指定属性。 - 读取
mybatis引入的外部Java属性文件中的特定属性值, 如有则覆盖。 - 读取方法传递的属性值, 如有则覆盖。
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中的参数或结果集取出一个值时, 使用类型处理器对数据进行转换。
自定义类型处理器有两种方式:
- 实现
org.apache.ibatis.type.TypeHandler接口. - 继承
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)
对象工厂用来实例化新的对象实例, 默认的对象工厂仅实现目标实例, 有两种方式:
- 通过无参构造函数实现
- 通过参数映射调用带有参数的构造方法。
插件(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有三个属性type、method、args, 分别对应拦截目标的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:事务管理器,支持JDBC和MANAGED2种类型。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实战示例
首先创建示例表:
创建数据表对应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
- 创建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);
}
- 执行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
- 创建映射接口类
public interface AuthorMapper {
Author get(long id);
}
- 创建映射配置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>
- 执行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'}