深入理解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的初始化过程 ...
随机推荐
- Ubuntu16.04 - 安装gtk+-3.0和appindicator3-0.1
今天在Ubuntu16.04里面遇到这样的问题: # pkg-config --cflags gtk+-3.0 appindicator3-0.1Package gtk+-3.0 was not fo ...
- Android逆向-Android基础逆向7(内购干货集合)
本文作者:MSTLab-EvilChen 0×00 前言 首先,本来想写NDK的,但是还是先把这个流程过一遍吧,这个流程是必不可少的.其次,RMB真的是一个好东西. 导航 由于本人为了节省时间,不想贴 ...
- PHP之旅9 MySQL数据库
PHP最主要的还是进行数据处理的,所以与数据库的交互是非常重要的. 现在主流的数据库有:Oracle.DB2.Microsoft SQL Server.MySQL等. MySQL由于其体积小.速度快. ...
- POJ 1154
#include<iostream> #include<stdio.h> #define MAXN 20 using namespace std; int DFS(int i, ...
- Vim实用技巧系列 - 搜索
最近发现了一个很好的VIM资源,best of vim tips, 展示了一系列很有用的vim 技巧.博主会逐个翻译介绍这些技巧. 来源: http://rayninfo.co.uk/vimtips. ...
- javascript数据基本类型和引用类型区别详解
JavaScript基本数据类型: js基本数据类型包括:undefined,null,number,boolean,string.基本数据类型是按值访问的,就是说我们可以操作保存在变量中的实际的值. ...
- 细说Activity与Task(任务栈)
Task概要: task是一个具有栈结构的容器,可以放置多个Activity实例.启动一个应用,系统就会为之创建一个task,来放置根Activity:默认情况下, 一个Activity启动另一个Ac ...
- python3 判断大小端的一种方法
这里用到了array.array('H', [1])来测试大小端,[1]可以转化为十六进制的0x0001,占两位,00位高位, 01位低位,通过第一位就可以判断大小端. 如果是小端,则转化为bytes ...
- 【Java并发编程】:多线程环境中安全使用集合API
在集合API中,最初设计的Vector和Hashtable是多线程安全的.例如:对于Vector来说,用来添加和删除元素的方法是同步的.如果只有一个线程与Vector的实例交互,那么,要求获取和释放对 ...
- 《Mysql技术内幕,Innodb存储引擎》——索引与算法
B+树 B+树中,所有记录节点都按照键值的大小顺序放在同一层叶子节点,各个叶子节点指针进行连接. 图中指针是单向的,但是书上的图是双向的,而且旋转应该也是双向才能完成) B+树插入处理 Leaf Pa ...