写点什么

MyBatis 核心功能介绍

用户头像
Java收录阁
关注
发布于: 2020 年 05 月 03 日

在大部分时候,我们都是在Spring里面去集成MyBatis,因为Spring对MyBatis的一些操作进行了封装,我们不能直接看到它的本质,所以先看下不使用容器的时候,也就是编程的方式,如何使用MyBatis。

  1. pom.xml中添加MyBatis的依赖

  2. 创建一个全局的配置文件(mybatis-config.xml),这里面是对MyBatis的核心行为的控制。

  3. 创建映射器文件(mapper.xml), 通常是一张表对应一个mapper.xml文件,我们会在这个xml文件中配置我们的增删改查的SQL语句,以及参数和结果集的映射关系

  4. 跟JDBC代码一样,我们要执行对数据库的操作,必须创建一个会话,这个在MyBatis中叫做SqlSession。SqlSession又是工厂类根据全局配置文件创建的

所以整个流程是下面代码这样,最后我们通过SqlSession接口上的方法,传入我们的Statement ID来执行SQL,这是第一种方式。

public void testStatement() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
Blog blog = (Blog) session.selectOne("com.gupaoedu.mapper.BlogMapper.selectBlogById", 1);
System.out.println(blog);
} finally {
session.close();
}
}

这种方式有一个明显的缺点,就是对statement ID的硬编码,不能在编译时进行类型检查,所以通常我们会使用第二种方式,就是定义一个mapper接口,这个接口必须和mapper.xml里面的namespace对应起来,方法也要和statement ID对应起来。

public void testSelect() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession(); // ExecutorType.BATCH
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}

这就是我们单独是用MyBatis的全部流程。

MyBatis核心对象的生命周期

在上面编程式的这个demo中,我们看到了MyBatis里面的几个核心对象:SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和Mapper对象。这几个核心对象分别在MyBatis的整个工作流程里面的不同环节发挥作用,如果说我们不用容器,自己去管理这些对象的话,我们必须去思考一个问题:什么时候去创建和销毁这些对象?



在一些分布式的应用里面,多线程高并发的场景中,如果要写出高效的代码,必须要了解这四个对象的生命周期。这四个对象的生命周期在官网上面也可以找到。 [MyBatis](http://www.mybatis.org/mybatis-3/zh/getting-started.html)



下面我们从每个对象的作用的角度来理解一下,只有理解了他们是干什么的,才能知道什么时候去创建和销毁:

  1. SqlSessionFactoryBuilder: 它是用来构建SqlSessionFactory的,而SqlSessionFactory只需要一个,所以只要构建了一个SqlSessionFactory,它的使命就完成了,也就没有存在的意义,所以它的生命周期只存在于方法的局部。

  2. SqlSessionFactory: SqlSessionFactory是用来创建SqlSession的,每次应用程序访问数据库都需要创建一个会话,因为我们一直会有创建会话的需求,所以SqlSessionFactory会一直存在于应用的整个生命周期中。创建SqlSession只需要一个实例来做就可以了,否则会产生很多的混乱和资源浪费,SqlSessionFactory一般是采用单例模式。

  3. SqlSession: SqlSession是一个会话,因为它不是线程安全的,不能线程间共享,所以我们在请求开始的时候创建一个新的SqlSession对象,在请求结束或者方法执行完成后要及时的关闭它。

  4. Mapper: Mapper是从SqlSession中获取的(实际上获取到的是一个代理对象),他的作用是发送SQL来操作数据库中的数据,它的生命周期应该是在SqlSession事务方法之内。



MyBatis核心配置的解读

大部分时候我们只需要很少的配置就可以让MyBatis运行起来,但是其实MyBatis里面提供的配置项非常多,我们没有配置的时候使用的是MyBatis提供的默认值。

MyBatis-3的代码托管在github上面,[源码地址](https://github.com/mybatis/mybatis-3/releases)



一级标签

configuration: 是整个配置文件的根标签,实际上也对应着MyBatis里面最重要的配置类Configuration。它贯穿MyBatis执行流程的每一个环节,我们打开这个类看一下, 可以看到里面有很多属性,也可以和其它标签对应上。

注意: Mybatis全局配置文件顺序是固定的,否则启动会报错。



Properties

第一个是Properties标签,用来配置参数信息,比如最常见的数据库连接信息。为了避免把参数直接写死在xml文件中,我们通常会把参数放到单独的properties文件中,通过properties标签引入进来,这样在xml中可以使用${}就可以引用改了

<properties resource="db.properties"></properties>



Setting

Setting里面是MyBatis的一些核心配置



typeAliases

TypeAlias是类型别名,和Linux中的alias一样,主要用来简化全路径类名的拼写。比如我们的参数类型和返回值类型都可能会用到我们的Bean,如果每个地方都配置全路径名字的话,内容就比较多,也很容易拼错。

我们可以为自己的bean定义一个别名,既可以指定单个类,也可以指定一个package,自动转换。配置了别名以后,只需要写别名就可以了。比如com.domain.Blog都只要写blog就可以了。

<typeAliases>

<typeAlias alias="blog" type="com.domain.Blog" />

</typeAliases>



MyBatis里面有系统预先定义好的类型别名,在TypeAliasRegistry中。



typeHandlers

由于Java类型和数据库的JDBC类型不是一一对应的(比如String与varchar),所以我们需要把Java对象转化成数据库的数据,和把数据库数据转化成Java对象,需要经过一定的转换,这两个方向的转换就要用到TypeHandler。



这时,我们可能会有疑问,我没有做任何类型的转化,为什么程序可以正常运行呢?这时因为MyBatis已经内置了很多TypeHandler,他们全部注册在TypeHandlerRegistry中,他们都继承了抽象类BaseTypeHandler,泛型就是要处理的Java数据类型。



当我们做类型转换的时候,就会自动调用对应的TypeHandler的方法。



如果我们需要自定义一些类型转换规则,或者要在处理类型的时候做一些特殊的动作,就可以编写自己的TypeHandler,跟系统自定义的TypeHandler一样,继承BaseTypeHandler<T>。它有四个抽象方法,我们可以分成两类:

Set方法是从Java类型转成JDBC类型:

public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;



get方法是从JDBC类型转成Java类型:

public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;



比如我们想要在获取或者设置String类型的时候做一些特殊处理,那我们可以写一个String类型的TypeHandler

public class MyTypeHandler extends BaseTypeHandler<String> {
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
// 设置 String 类型的参数的时候调用,Java类型到JDBC类型
// 注意只有在字段上添加typeHandler属性才会生效
// insertBlog name字段
System.out.println("---------------setNonNullParameter1:"+parameter);
ps.setString(i, parameter);
}
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 根据列名获取 String 类型的参数的时候调用,JDBC类型到java类型
// 注意只有在字段上添加typeHandler属性才会生效
System.out.println("---------------getNullableResult1:"+columnName);
return rs.getString(columnName);
}
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
// 根据下标获取 String 类型的参数的时候调用
System.out.println("---------------getNullableResult2:"+columnIndex);
return rs.getString(columnIndex);
}
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
System.out.println("---------------getNullableResult3:");
return cs.getString(columnIndex);
}
}



然后在mybatis-config.xml文件中进行注册

<typeHandlers>

<typeHandler handler="com.gupaoedu.type.MyTypeHandler"></typeHandler>

</typeHandlers>



最后在我们需要使用的字段上指定:

插入值的时候,从Java类型到JDBC类型,在字段属性中指定typeHandler

<insert id="insertBlog" parameterType="blog">
insert into blog (bid, name, author_id)
values (#{bid,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR,typeHandler=com.gupaoedu.type.MyTypeHandler},
#{authorId,jdbcType=CHAR})
</insert>



返回值的时候,从JDBC类型转到Java类型,在resultMap的列上指定typeHandler

<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.gupaoedu.type.MyTypeHandler"/>



objectFactory

当我们把数据库返回的结果集转化成实体类的时候,需要创建对象的实例,由于我们不知道需要处理的类型是什么,有哪些属性,所以不能用new的方式去创建。在MyBatis里面,它提供了一个工厂类的接口,叫做ObjectFactory,专门用来创建对象的实例。

ObjectFactory有一个默认的实现类DefaultObjectFactory,创建对象的方法最终都调用了instantiateClass(),是通过反射来实现的。

如果想要修改对象工厂在初始化实例类的时候的行为,就可以通过创建自己的对象工厂,集成DefaultObjectFactory来实现。



plugins

插件是MyBatis一个很强大的机制,和很多其它框架一样,MyBatis预留了插件的接口,让MyBatis更容易扩展。

根据官方的定义,插件可以拦截下面四个对象:

Executor: update、query、flushStatements、commit、rollback、getTransaction、close、isClosed

ParameterHandler: getParameterObject、setParameters

ResultSetHandler: handlerResultSets、handleOutputParameters

StatementHandler: prepare、parameterize、batch、update、query



environments

environments标签用来管理数据库的环境,比如我们可以有开发环境、测试环境、生产环境的数据库,可以在不同环境中使用不同的数据库地址和类型

<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
<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>



一个environment标签就是一个数据源,代表一个数据库。这里面有两个关键的标签,一个是事务管理器,一个是数据源。

transactionManager如果配置成JDBC,则会使用connection对象的commit()、rollback()、close()管理事务。

如果配置成MANAGED,会把事务交给容器管理,比如JBoss。如果使用Spring + MyBatis,则没有必要配置,因为我们会直接在spring的applicationContext.xml文件中配置数据源,来覆盖MyBatis的配置。



mapper

mappers标签配置的是我们的映射器,也就是mapper.xml文件的路径。目的是让MyBatis在启动的时候去扫描这些映射器,创建映射关系。



settings

MyBatis的一些关键配置都在这个标签中定义,具体细节可以参考[官网文档](http://www.mybatis.org/mybatis-3/zh/configuration.html#settings)



发布于: 2020 年 05 月 03 日阅读数: 78
用户头像

Java收录阁

关注

士不可以不弘毅,任重而道远 2020.04.30 加入

喜欢收集整理Java相关技术文档的程序员,欢迎关注同名微信公众号 Java收录 阁获取更多文章

评论

发布
暂无评论
MyBatis核心功能介绍