1. typeHandlers类型转换器
每当MyBatis 设置参数到PreparedStatement 或者从ResultSet 结果集中取得值时,就会使用TypeHandler 来处理数据库类型与java 类型之间转换。下表描述了默认
TypeHandlers
1.1自定义类型
假设表中字段是int类型,而实体类与之对应的属性是boolean类型,此时可以采用自定义类型转换器进行对应
(1)Dept.java实体类
package org.sang.entity;
/**
* @author: wangxiaobo
* @create: 2020-08-25 22:51
**/
public class Dept {
private Integer deptNo;
//部门名称
private String dname;
//部门位置
private String Loc;
private boolean flag;
private String country;
public Integer getDeptNo() {
return deptNo;
}
public void setDeptNo(Integer deptNo) {
this.deptNo = deptNo;
}
public String getDName() {
return dname;
}
public void setDName(String dName) {
dname = dName;
}
public String getLoc() {
return Loc;
}
public void setLoc(String loc) {
Loc = loc;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
(2)dept表中字段
(3)开发自定义类型转换器:MyTypeHandler.java
继承并实现接口:TypeHandler.java
package org.sang.util;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author: wangxiaobo
* @create: 2020-08-26 13:13
* setParameter:这个方法在生成SQL语句时才被调用
* getResult:查询结束之后,在将ResultSet数据行转换为实体类对象时,
* 通知TypeHandler将当前数据行某个字段转换为何种类型
**/
public class MyTypeHandler implements TypeHandler {
@Override
public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
throws SQLException {
if (parameter==null){//dept.flag=null insertsql flag设置0
ps.setInt (i,0);
return;
}
Boolean flag = (Boolean) parameter;
if (flag==true){
ps.setInt (i,1);
}
else {
ps.setInt (i,0);
}
}
@Override
public Object getResult(ResultSet rs, String s) throws SQLException {
int flag = rs.getInt (s);//1 or 0
Boolean myFlag = Boolean.FALSE;
if (flag==1){
myFlag = Boolean.TRUE;
}
return myFlag;
}
@Override
public Object getResult(ResultSet resultSet, int i) throws SQLException {
return null;
}
@Override
public Object getResult(CallableStatement callableStatement, int i) throws SQLException {
return null;
}
}
(4) 在MyBatis核心配置文件注册自定义类型转换器:
myBatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 属性配置 -->
<properties resource="config.properties"></properties>
<!-- 别名配置 -->
<typeAliases>
<package name="org.sang.entity"></package>
<package name="org.sang.dao"></package>
</typeAliases>
<!-- ObjectFactory对象工厂 -->
<objectFactory type="org.sang.util.MyObjectFactory"></objectFactory>
<!-- 类型处理器 -->
<!--<typeHandlers>-->
<!--<!– 从java中的Boolean转jdbc中的NUMERIC –>-->
<!--<typeHandler handler="org.sang.util.MyTypeHandler"-->
<!--javaType="Boolean" jdbcType="NUMERIC"></typeHandler>-->
<!--</typeHandlers>-->
<!-- 环境配置 -->
<environments default="development">
<!-- 环境配置 -->
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" /> <!-- 驱动类型 -->
<property name="url" value="jdbc:mysql://localhost:3306/sam?characterEncoding=utf-8"/> <!-- 连接字符串 -->
<property name="username" value="root" /> <!-- 用户名 -->
<property name="password" value="" /> <!-- 密码 -->
</dataSource>
</environment>
</environments>
<!-- 映射器 -->
<mappers>
<!--<package name="org.sang.dao.DeptMapper.xml" />-->
<mapper resource="DeptMapper.xml"/> <!-- 映射SQL语句的XML文件 -->
</mappers>
(5) 创建接口:DeptMapper.java
package org.sang.dao;
import org.sang.entity.Dept;
import java.util.List;
/**
* @author: wangxiaobo
* @create: 2020-08-26 14:54
**/
public interface DeptMapper {
public void deptSave1(Dept dept);
public List<Dept> deptFind();
}
(6) DeptMapper.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="Dept">-->
<!--<!– 插入单个部门信息 –>-->
<!--<insert id="InsertDept">-->
<!--INSERT INTO DEPT (DNAME,LOC)-->
<!--VALUES (#{DName},#{Loc})-->
<!--</insert>-->
<!--</mapper>-->
<mapper namespace="org.sang.dao.DeptMapper">
<insert id="deptSave1" parameterType="org.sang.entity.Dept">
insert into dept (DEPTNO,DNAME,LOC,flag)
values(#{deptNo},#{dname},#{loc},#{flag})
</insert>
<resultMap type="dept" id="deptMap">
<result column="flag" property="flag" typeHandler="org.sang.util.MyTypeHandler"/>
</resultMap>
<select id="deptFind" resultType="Dept">
select deptNo,dname,loc,flag from dept
</select>
</mapper>
(7) 执行单元测试:TestMain_01.java
package org.sang.chenyanbin;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.sang.dao.DeptMapper;
import org.sang.entity.Dept;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author: wangxiaobo
* @create: 2020-08-26 15:11
**/
public class TestMain_01 {
private SqlSession session;
@Before
public void Start() {
try {
//加载MyBatis的配置文件
InputStream is = Resources.getResourceAsStream ("myBatis-config.xml");
//初始化SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder ().build (is);
session = factory.openSession();
} catch (Exception e) {
e.printStackTrace ();
}
}
@After
public void end() {
if (session == null) {
session.close ();
}
}
@Test
public void test01() throws IOException{
Dept d2 = new Dept();
d2.setDName ("上海事业部1");
d2.setLoc("上海3");
d2.setFlag(false);
session.insert("deptSave1", d2);
session.commit();
session.close();
}
@Test
public void test02() {
DeptMapper dao = session.getMapper (DeptMapper.class);
List<Dept> deptList=dao.deptFind ();
System.out.println("ok");
}
}
1.2 objectFactory 对象工厂
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。
1.2.1 自定义对象工厂
实体类:Dept.java
(1) 继承与DefaultObjectFactory:MyObjectFactory.java
package org.sang.util;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.sang.entity.Dept;
/**
* @author: wangxiaobo
* @create: 2020-08-28 11:58
**/
public class MyObjectFactory extends DefaultObjectFactory {
@Override
public Object create(Class type) {// 重新定义Dept类实例对象创建规则,其他类实例对象创建规则不想改变
if (Dept.class == type) {
// 依靠父类提供create方法创建Dept对象
Dept dept = (Dept) super.create(type);
// 设置自定义规则
dept.setCountry("China");
return dept;
}
return super.create(type);
}
}
(2) 在MyBatis核心文件中注册自定义工厂
1.3 Plugins 拦截器
拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。打个比方,对于Executor,Mybatis中有几种实现:BatchExecutor、ReuseExecutor、SimpleExecutor和CachingExecutor。这个时候如果你觉得这几种实现对于Executor接口的query方法都不能满足你的要求,那怎么办呢?是要去改源码吗?当然不。我们可以建立一个Mybatis拦截器用于拦截Executor接口的query方法,在拦截之后实现自己的query方法逻辑,之后可以选择是否继续执行原来的query方法。
对于拦截器Mybatis为我们提供了一个Interceptor接口,通过实现该接口就可以定义我们自己的拦截器。我们先来看一下这个接口的定义:
我们可以看到在该接口中一共定义有三个方法,intercept、plugin和setProperties。plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法,当然也可以调用其他方法,这点将在后文讲解。setProperties方法是用于在Mybatis配置文件中指定一些属性的。
定义自己的Interceptor最重要的是要实现plugin方法和intercept方法,在plugin方法中我们可以决定是否要进行拦截进而决定要返回一个什么样的目标对象。而intercept方法就是要进行拦截的时候要执行的方法。
对于plugin方法而言,其实Mybatis已经为我们提供了一个实现。Mybatis中有一个叫做Plugin的类,里面有一个静态方法wrap(Object target,Interceptor interceptor),通过该方法可以决定要返回的对象是目标对象还是对应的代理。
对于实现自己的Interceptor而言有两个很重要的注解,一个是@Intercepts,其值是一个@Signature数组。@Intercepts用于表明当前的对象是一个Interceptor,而@Signature则表明要拦截的接口、方法以及对应的参数类型。
创建自己的拦截器:MySimpleInterceptor.java
package org.sang.util;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.session.ResultHandler;
import java.util.Properties;
/**
* @author: wangxiaobo
* @create: 2020-08-28 12:33
**/
@Intercepts({ @Signature(method = "query", type = Executor.class, args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }) })
public class MySimpleInterceptor implements Interceptor {
/*
* 参数:Invocation {代理对象,被监控的方法对象,当前被监控方法运行时需要实参}
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("被拦截方法执行之前,做的辅助服务。。。。。");
Object object = invocation.proceed(); // 执行被拦截方法
System.out.println("被拦截方法执行之后,做的辅助服务。。。。。");
return object;
}
/*
* 参数:target 表示被拦截的对象,应该是Executor接口实例对象 作用: 如果 被拦截的对象所在的类是有实现接口就为当前拦截对象生成一个代理对象
* 如果被拦截的对象所在的类没有指定接口,这个对象之后的行为不会被代理操作
*/
@Override
public Object plugin(Object o) {
// TODO Auto-generated method stub
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
MyBatis核心配置文件:myBatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 属性配置 -->
<properties resource="config.properties"></properties>
<!-- 别名配置 -->
<typeAliases>
<package name="org.sang.entity"></package>
<package name="org.sang.dao"></package>
</typeAliases>
<!-- ObjectFactory对象工厂 -->
<objectFactory type="org.sang.util.MyObjectFactory"></objectFactory>
<!-- Plugins拦截器 -->
<plugins>
<plugin interceptor="org.sang.util.MySimpleInterceptor"></plugin>
</plugins>
<!-- 类型处理器 -->
<!--<typeHandlers>-->
<!--<!– 从java中的Boolean转jdbc中的NUMERIC –>-->
<!--<typeHandler handler="org.sang.util.MyTypeHandler"-->
<!--javaType="Boolean" jdbcType="NUMERIC"></typeHandler>-->
<!--</typeHandlers>-->
<!-- 环境配置 -->
<environments default="development">
<!-- 环境配置 -->
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" /> <!-- 驱动类型 -->
<property name="url" value="jdbc:mysql://localhost:3306/sam?characterEncoding=utf-8"/> <!-- 连接字符串 -->
<property name="username" value="root" /> <!-- 用户名 -->
<property name="password" value="" /> <!-- 密码 -->
</dataSource>
</environment>
</environments>
<!-- 映射器 -->
<mappers>
<!--<package name="org.sang.dao.DeptMapper.xml" />-->
<mapper resource="DeptMapper.xml"/> <!-- 映射SQL语句的XML文件 -->
</mappers>
</configuration>
单元测试
MyBatis自定义拦截器,可以拦截接口只有四种.
- Executor.class
- StatementHandler.class
- ParameterHandler.class
- ResultSetHandler.class