深入理解MyBatis的原理(三):配置文件用法(续)
前言:前文讲解了 MyBatis 的配置文件一部分用法,本文将继续讲解 MyBatis 的配置文件的用法。
目录
1、typeHandler 类型处理器
MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,或者从结果集(ResultSet)中取出一个值时,都会用注册了的 typeHandle 进行处理。
由于数据库厂商不同,字段类型就不同,所以从 java 传递参数到数据库,或者从数据库中读取数据到 java,我们都需要进行字段的处理。typeHandle 的作用,就是将参数从 javaType 转化为 jdbcType,或者从数据库中取出结果时把 jdbcType 转化为 javaType。
typeHandle 和别名一样,也分为 MyBatis 系统定义和用户自定义两种。
1.1 dtd 规则
常用的配置:java 类型(javaType)、JDBC 类型(jdbcType)。
1.2 系统定义的 typeHandle
MyBatis 系统内部就定义了一系列的 typeHandle,源码在 org.apache.ibatis.type.TypeHandlerRegistry,如下:
- public TypeHandlerRegistry() {
- this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
- this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
- this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
- this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
- this.register((Class)Byte.class, (TypeHandler)(new ByteTypeHandler()));
- this.register((Class)Byte.TYPE, (TypeHandler)(new ByteTypeHandler()));
- this.register((JdbcType)JdbcType.TINYINT, (TypeHandler)(new ByteTypeHandler()));
- this.register((Class)Short.class, (TypeHandler)(new ShortTypeHandler()));
- this.register((Class)Short.TYPE, (TypeHandler)(new ShortTypeHandler()));
- this.register((JdbcType)JdbcType.SMALLINT, (TypeHandler)(new ShortTypeHandler()));
- this.register((Class)Integer.class, (TypeHandler)(new IntegerTypeHandler()));
- this.register((Class)Integer.TYPE, (TypeHandler)(new IntegerTypeHandler()));
- this.register((JdbcType)JdbcType.INTEGER, (TypeHandler)(new IntegerTypeHandler()));
- this.register((Class)Long.class, (TypeHandler)(new LongTypeHandler()));
- this.register((Class)Long.TYPE, (TypeHandler)(new LongTypeHandler()));
- this.register((Class)Float.class, (TypeHandler)(new FloatTypeHandler()));
- this.register((Class)Float.TYPE, (TypeHandler)(new FloatTypeHandler()));
- this.register((JdbcType)JdbcType.FLOAT, (TypeHandler)(new FloatTypeHandler()));
- this.register((Class)Double.class, (TypeHandler)(new DoubleTypeHandler()));
- this.register((Class)Double.TYPE, (TypeHandler)(new DoubleTypeHandler()));
- this.register((JdbcType)JdbcType.DOUBLE, (TypeHandler)(new DoubleTypeHandler()));
- this.register((Class)Reader.class, (TypeHandler)(new ClobReaderTypeHandler()));
- this.register((Class)String.class, (TypeHandler)(new StringTypeHandler()));
- this.register((Class)String.class, JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
- this.register((Class)String.class, JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
- this.register((Class)String.class, JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
- this.register((Class)String.class, JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
- this.register((Class)String.class, JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
- this.register((Class)String.class, JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
- this.register((Class)String.class, JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
- this.register((JdbcType)JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
- this.register((JdbcType)JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
- this.register((JdbcType)JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
- this.register((JdbcType)JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
- this.register((JdbcType)JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
- this.register((JdbcType)JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
- this.register((JdbcType)JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
- this.register((Class)Object.class, JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
- this.register((JdbcType)JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
- this.register((Class)BigInteger.class, (TypeHandler)(new BigIntegerTypeHandler()));
- this.register((JdbcType)JdbcType.BIGINT, (TypeHandler)(new LongTypeHandler()));
- this.register((Class)BigDecimal.class, (TypeHandler)(new BigDecimalTypeHandler()));
- this.register((JdbcType)JdbcType.REAL, (TypeHandler)(new BigDecimalTypeHandler()));
- this.register((JdbcType)JdbcType.DECIMAL, (TypeHandler)(new BigDecimalTypeHandler()));
- this.register((JdbcType)JdbcType.NUMERIC, (TypeHandler)(new BigDecimalTypeHandler()));
- this.register((Class)InputStream.class, (TypeHandler)(new BlobInputStreamTypeHandler()));
- this.register((Class)Byte[].class, (TypeHandler)(new ByteObjectArrayTypeHandler()));
- this.register((Class)Byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
- this.register((Class)Byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
- this.register((Class)byte[].class, (TypeHandler)(new ByteArrayTypeHandler()));
- this.register((Class)byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
- this.register((Class)byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
- this.register((JdbcType)JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
- this.register((JdbcType)JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
- this.register(Object.class, this.UNKNOWN_TYPE_HANDLER);
- this.register(Object.class, JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
- this.register(JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
- this.register((Class)Date.class, (TypeHandler)(new DateTypeHandler()));
- this.register((Class)Date.class, JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
- this.register((Class)Date.class, JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
- this.register((JdbcType)JdbcType.TIMESTAMP, (TypeHandler)(new DateTypeHandler()));
- this.register((JdbcType)JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
- this.register((JdbcType)JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
- this.register((Class)java.sql.Date.class, (TypeHandler)(new SqlDateTypeHandler()));
- this.register((Class)Time.class, (TypeHandler)(new SqlTimeTypeHandler()));
- this.register((Class)Timestamp.class, (TypeHandler)(new SqlTimestampTypeHandler()));
- if(Jdk.dateAndTimeApiExists) {
- Java8TypeHandlersRegistrar.registerDateAndTimeHandlers(this);
- }
- this.register((Class)Character.class, (TypeHandler)(new CharacterTypeHandler()));
- this.register((Class)Character.TYPE, (TypeHandler)(new CharacterTypeHandler()));
- }
其中 StringTypeHandle,源码如下
- public class StringTypeHandler extends BaseTypeHandler<String> {
- public StringTypeHandler() {
- }
- public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
- ps.setString(i, parameter);
- }
- public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
- return rs.getString(columnName);
- }
- public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- return rs.getString(columnIndex);
- }
- public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
- return cs.getString(columnIndex);
- }
- }
StringTypeHandle 继承了 BaseTypeHandle。而 BaseTypeHandle 实现了接口 typeHandle,并且自己定义了 4 个抽象方法。
setParameter 是 PreparedStatement 对象设置参数,它允许我们自己填写变换规则。
getResult 则分为 ResultSet 用列名(columnName)或者使用列下标(columnIndex)来获取结果数据。其中还包括了 CallableStatement(存储过程)获取结果及数据的方法。
1.3 自定义 typeHandler
从上面系统定义的 typeHandler 中来看,其实已经能够应付大部分的场景了,但是有一些特殊情况还是需要自定义 typeHandler。
这里实现一个字符串参数的 typeHandler。首先配置 XML 文件,确定我们需要处理上面类型的参数和结果。
- <!--自定义类型处理器-->
- <typeHandlers>
- <typeHandler handler="com.yule.mybatis.typehandle.MyStringTypeHandler" javaType="string" jdbcType="VARCHAR"/>
- </typeHandlers>
或者采取包扫描的方式
- <!--自定义类型处理器-->
- <typeHandlers>
- <package name="com.yule.mybatis.typehandler"/>
- </typeHandlers>
源码里的 StringTypeHandler 使用的是继承 BaseTypeHandler,而这里采取实现 TypeHandler 接口来实现。
自定义的 typeHandler 中配置的 jdbcType,javaType,需要使用注解在 handle 类中绑定。
- package com.yule.mybatis.typehandler;
- import org.apache.ibatis.type.JdbcType;
- import org.apache.ibatis.type.MappedJdbcTypes;
- import org.apache.ibatis.type.MappedTypes;
- import org.apache.ibatis.type.TypeHandler;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import java.sql.CallableStatement;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- /**
- * @author yule
- * @date 2018/8/12 17:52
- */
- @MappedTypes({String.class})
- @MappedJdbcTypes(JdbcType.VARCHAR)
- public class MyStringTypeHandler implements TypeHandler<String> {
- private Logger logger = LoggerFactory.getLogger(MyStringTypeHandler.class);
- @Override
- public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
- logger.info("使用我的 typeHandler");
- preparedStatement.setString(i, s);
- }
- @Override
- public String getResult(ResultSet resultSet, String s) throws SQLException {
- logger.info("使用我的 typeHandler,ResultSet 列名获取字符串");
- return resultSet.getString(s);
- }
- @Override
- public String getResult(ResultSet resultSet, int i) throws SQLException {
- logger.info("使用我的 typeHandler,ResultSet 下标获取字符串");
- return resultSet.getString(i);
- }
- @Override
- public String getResult(CallableStatement callableStatement, int i) throws SQLException {
- logger.info("使用我的 typeHandler,CallableStatement 下标获取字符串");
- return callableStatement.getString(i);
- }
- }
注解 @MappedTypes 定义的是 JavaType 类型,可以指定哪些 Java 类型被拦截。
注解 @MappedJdbcTypes 定义的是 JdbcType 类型,它需要满足枚举类 org.apache.ibatis.type.JdbcType 所列的枚举类型。
到这里,我们还需要去 sql 里标注哪些参数或结果类型去用我们自定义的 TypeHandler 进行转换,因为在没有任何标注的情况,MyBatis 是不会启用自定义的 TypeHandler。可以通过配置 jdbcType 和 javaType,或者直接使用 typeHandler 属性指定。
- <select id="queryUserByName" resultType="user">
- select t.id, t.name, t.age from t_user t
- where t.name = #{name,javaType=String,jdbcType=VARCHAR,typeHandler=com.yule.mybatis.typehandler.MyStringTypeHandler}
- </select>
这时运行 sql,就可以了。
1.4 枚举类型 typeHandler
2、ObjectFactory
当 MyBatis 在构建一个结果返回的时候,会使用 ObjectFactory(对象工厂)去创建 POJO,在 MyBatis 中可以定制自己的对象工厂。
一般情况下,我们使用默认的对象工厂即可,MyBatis 中默认的对象工厂是由 org.apache.ibatis.reflection.factory.DefaultObjectFactory 来提供服务的。
如果特殊情况下,需要定制特定的工厂,xml 中配置如下:
- <!--自定义对象工厂-->
- <objectFactory type="com.yule.mybatis.objectfactory.MyObjectFactory">
- <property name="name" value="MyObjectFactory"/>
- </objectFactory>
MyObjectFactory 可以参考 DefaultObjectFactory 的写法来,这里为了测试方便,直接继承 DefaultObjectFactory 来简化编程。
- package com.yule.mybatis.objectfactory;
- import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
- import java.util.List;
- import java.util.Properties;
- /**
- * @author yule
- * @date 2018/8/12 19:34
- */
- public class MyObjectFactory extends DefaultObjectFactory {
- private static final long serialVersionUID = -8855120656740914948L;
- @Override
- public void setProperties(Properties properties) {
- System.out.println("使用我自己的对象工厂 定制属性");
- super.setProperties(properties);
- }
- @Override
- public <T> T create(Class<T> type) {
- System.out.println("使用我自己的对象工厂 构建单个对象");
- return super.create(type);
- }
- @Override
- public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
- System.out.println("使用我自己的对象工厂 构建对象列表");
- return super.create(type, constructorArgTypes, constructorArgs);
- }
- @Override
- public <T> boolean isCollection(Class<T> aClass) {
- return false;
- }
- }
这样我们配置的对象工厂就已经生效。
大部分情况下,我们使用系统默认的对象工厂即可。
3、插件
4、environments 配置环境
4.1 概述
配置环境可以注册多个数据源(dataSource),每一个数据源分为两大部分:数据源的配置和数据库事务(transactionManager)的配置。
- <!--配置环境-->
- <environments default="development">
- <environment id="development">
- <transactionManager type="JDBC">
- <property name="autoCommit" value="false"/>
- </transactionManager>
- <dataSource type="POOLED">
- <property name="driver" value="${driver}"/>
- <property name="url" value="${datasourceurl}"/>
- <property name="username" value="${username}"/>
- <property name="password" value="${password}"/>
- </dataSource>
- </environment>
- </environments>
environments 中的属性 default,标明在缺省的情况下,我们将启用哪个数据源配置。
environment 元素是配置一个数据源,属性 id 是设置这个数据源的唯一标识,以便 MyBatis 上下文使用它。
transactionManager 配置的是数据库事务,其中 type 属性有 3 中配置方式:
(1)JDBC,采用 JDBC 方式管理事务,在独立编码中我们常常使用。
(2)MANAGED,采用容器方式管理事务,在 JNDI 数据源中常用。
(3)自定义,由使用者自定义数据库事务管理方式,适用于特殊应用。
property 元素则是可以配置数据源的各类属性,我们这个配置了 autoCommit = false,则是要求数据源不自动提交。
dataSource 标签,是配置数据源连接的信息,type 属性是提供我们队数据库连接方式的配置,同样 MyBatis 提供几种配置方式:
(1)UNPOOLED,非连接池数据库(UnpooledDataSource)。
(2)POOLED,连接池数据库(PooledDataSource)。
(3)自定义数据源。
其中,配置的 property 元素,就是定义数据库的各类参数。
4.2 数据源
MyBatis 内部提供了 3 中数据源的实现方式:
(1)UNPOOLED,非连接池,使用org.apache.ibatis.datasource.unpooled.UnpooledDataSource 实现。
(2)POOLED,连接池,使用 org.apache.ibatis.datasource.pooled.PooledDataSource 实现。
(3)JNDI,使用 org.apache.ibatis.datasource.jndi.JndiDataSourceFactory 来获取数据源。
这 3 中方式实现比较简单,只需要在 dataSource 标签的 type定义为 UNPOOLED,POOLED,JNDI 即可。
如果还需要其他的数据源,可以自定义一个类 MyDataSourceFactory,实现 org.apache.ibatis.datasource.DataSourceFactory 接口,可以参考 JndiDataSourceFactory,然后在 dataSource 标签中配置 type="xxx.xxx.MyDataSourceFactory",即可。
5、databaseIdProvider 数据库厂商标识
如果 MyBatis 需要运行在不同厂商的数据库中,会需要使用到数据库厂商标识,它为此提供一个数据库标识,并提供自定义,作用在于指定 SQL 到对应的数据库厂商提供的数据库中运行。
用的很少,所以就不先不在这里讲解。
6、引入映射器的方法
引入映射器的几种方式:
- <!-- 引入映射器 的几种方式 -->
- <mappers>
- <!--使用类注册引入-->
- <!--<mapper class="com.yule.user.dao.UserDao"/>-->
- <!--使用文件路径引入-->
- <mapper resource="com/yule/user/dao/UserDao.xml"/>
- <!--使用包名引入映射器-->
- <!--<package name="com.yule.user.dao"/>-->
- <!--使用 userDao.xml 引入-->
- <!--<mapper url="file:F:\IDEAworkspace\sdemo\src\main\java\com\yule\user\dao\UserDao.xml"/>-->
- </mappers>
深入理解MyBatis的原理(三):配置文件用法(续)的更多相关文章
- 深入理解MyBatis的原理:整个体系
前言:工作中虽然用到了 MyBatis,可完全不知道为什么,再不学习就晚了,这里将记录我的学习笔记,整个 MyBatis 的体系. 一.简介 1.传统的JDBC JDBC 是一种典型的桥接模式. 使用 ...
- 深入理解MyBatis的原理(三):配置文件(上)
前言:前文提到一个入门的demo,从这里开始,会了解深入 MyBatis 的配置,本文讲解 MyBatis 的配置文件的用法. 目录 1.properties 元素 2.设置(settings) 3. ...
- 深入理解MyBatis的原理(四):映射器的用法
前言:继续深入学习 mybatis 的用法及原理,还是先会用再学习原理. 映射器的主要元素有:select.insert.update.delete.parameterMap(即将被删除,不建议使用) ...
- 深入理解MyBatis的原理(一): 独立的入门demo
前言:不结合spring,只有 mybatis+maven.数据库使用 oracle.不尝试永远不知道会发生什么事,其中遇到两个小问题,也记录下来了.转载请注明出处:https://www.cnblo ...
- 深入理解Mybatis技术与原理
目录 第1章 Mybatis简介 1.1 传统的JDBC编程 1.2 ORM模型 1.4 MyBatis 1.5 什么时候用MyBatis 第2章 MyBatis入门 2.2 MyBatis构成 2. ...
- 《深入理解mybatis原理》 Mybatis初始化机制具体解释
对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...
- 《深入理解mybatis原理》 MyBatis的架构设计以及实例分析
作者博客:http://blog.csdn.net/u010349169/article/category/2309433 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简 ...
- 深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解(转)
文章转自http://blog.csdn.net/l454822901/article/details/51829785 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章 ...
- 《深入理解mybatis原理2》 Mybatis初始化机制详解
<深入理解mybatis原理> Mybatis初始化机制详解 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程 ...
随机推荐
- IAP远程在线升级
IAP远程在线升级 在上一篇中实现了LWIP网口通讯,那么肯定要加个在线升级功能,这个功能所占用的资源很少,但在物联网中很重要也很实用.在线升级就是像手机一样,先下载好系统,然后点击升级~然后就没然后 ...
- 698. Partition to K Equal Sum Subsets
Given an array of integers nums and a positive integer k, find whether it's possible to divide this ...
- ajax post 400 bad request
是前端ajax没有加声明:contentType:'application/json',
- iOS 关于布局问题的一些认识
///更新约束和布局 更新约束布局相关的API - (void)updateConstraintsIfNeeded 调用此方法,如果有标记为需要重新布局的约束,则立即进行重新布局,内部会调用upda ...
- POJ 1101
#include <iostream> #include <string> #define MAXN 78 #define min _min #define inf 12345 ...
- Seqlist L 与 Seqlist *L的区别
Seqlist L结构体变量,SeqlistInsert之后不可以带回新的插入数据. Seqlist *L 是传结构体指针 用SeqlistInsert之后可以有新的插入.
- c#连接oracle遇到的问题
1.最近在做项目,发现一个很有意思的现象.我在项目中引用System.Data.OracleClient进行oracle库的远程连接,一直出错.后来无意中,将.net framework 4.0框架改 ...
- (转)用Python写堡垒机项目
原文:https://blog.csdn.net/ywq935/article/details/78816860 前言 堡垒机是一种运维安全审计系统.主要的功能是对运维人员的运维操作进行审计和权限控制 ...
- android Service 学习总结
学习android开发已经四五个月,由于项目中职责的原因一直没有接触过Service的实际项目,今天重新学一遍Service用法. 问题: 作为四大组件,为什么需要Service? 它与Thread又 ...
- Git&GitHub学习日志
Git是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常大的项目版本管理. Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件.作为一个 ...