三、mybatis中typehandler详解

前言:今天介绍一个mybatis操作数据库时的一个类似黑匣子的东西,TypeHandler


闲聊:
      在我们平常开发操作数据库时,查询、插入数据等操作行为,有时会报数据类型不匹配异常,就可以得知数据的类型是不唯一的必然是多种不同的数据类型。并且我们必须要明确的一点就是java作为一门编程语言有自己的数据类型,数据库也是有自己的数据类型的。

jdbc数据类型:org.apache.ibatis.type.JdbcType 此枚举就是所有的数据库支持类型
java数据类型:int、long、string、…

        一定要分清,例如java重的date数据插入到数据库中,应该是已经转换成了数据库的某种类型,必然跟java已经没有关系了。中间有一些我们看不见的操作做了数据处理。
        假设此时的java类型与数据库数据类型是一样的,哪么其他语言中的日期数据插入数据库时又该怎么解释,例如C#操作数据库存入时间类型,C#与java肯定没有关系吧。所以每种语言与数据库之间有种数据类型关系对应。


思考:

  1. 因为java与数据库各自有数据类型,所以在将java数据存入数据库前中间是否有其他操作,是我们看不见的,不然java数据怎么知道自己与哪个jdbc数据类型匹配?

    答:mybatis框架为每种数据类型做了默认的关系对应,BaseTypeHandler的所有实现类,就是来做这些处理的。

  2. 例如:java中的date插入数据库时是jdbc哪种类型,怎么就是这种类型? 中间具体有什么操作?
    答:DateTypeHandler就是来解决date数据类型的处理。

在这里插入图片描述

源码查看: 看一下源码中具体怎么处理date数据类型的。

先看一下接口:

//此接口作用是用于指定jdbc与java的数据类型间对应关系处理。
public interface TypeHandler<T> {
  // 保存操作,数据入库之前时数据处理
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  //下面三个则是,从数据库加载数据后,vo对象封装前的数据处理
  T getResult(ResultSet rs, String columnName) throws SQLException;
  T getResult(ResultSet rs, int columnIndex) throws SQLException;
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

具体处理效果:BaseTypeHandler 实现了TypeHandler 接口

public class DateTypeHandler extends BaseTypeHandler<Date> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType)
      throws SQLException {//插入数据前,类型转关
    ps.setTimestamp(i, new Timestamp((parameter).getTime())); 
  }
//查询结果数据类型转换
  @Override
  public Date getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    Timestamp sqlTimestamp = rs.getTimestamp(columnName);
    if (sqlTimestamp != null) {
      return new Date(sqlTimestamp.getTime());
    }
    return null;
  }

  @Override
  public Date getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    Timestamp sqlTimestamp = rs.getTimestamp(columnIndex);
    if (sqlTimestamp != null) {
      return new Date(sqlTimestamp.getTime());
    }
    return null;
  }

  @Override
  public Date getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    Timestamp sqlTimestamp = cs.getTimestamp(columnIndex);
    if (sqlTimestamp != null) {
      return new Date(sqlTimestamp.getTime());
    }
    return null;
  }
}



自定义对应关系TypeHandler
  1. 将date数据插入数据库varchar字段,并显示结果为距1970年的秒数

  2. 将list数据插入数据库varchar字段,结果为: a,b,c,d 这样的数据

实现前提:
表结构

CREATE TABLE `employee` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(20) DEFAULT NULL,
  `gender` varchar(2) DEFAULT NULL,
  `email` varchar(20) DEFAULT NULL,
  `hobbys` varchar(200) DEFAULT NULL,
  `createtime` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8

vo类


public class Employee {
	private Integer id;
	private String userName;
	private String gender;
	private String email;
	private Date createtime;      //--->1563871033745
	private List<String> hobbys;  //---> 1,2,3,4
	}
解决第一个需求,date类型转换
  1. 自定义日期转换处理类
package com.cjy.mybatis.typehandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

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.apache.log4j.Logger;

@MappedJdbcTypes({JdbcType.VARCHAR})  //对应数据库类型
@MappedTypes({Date.class})			  //java数据类型
public class MyDateTypeHandler implements TypeHandler<Date>{

	private Logger logger = Logger.getLogger(MyDateTypeHandler.class);
	
	//入库前的类型转换
	@Override
	public void setParameter(PreparedStatement ps, int i, Date parameter,
			JdbcType jdbcType) throws SQLException {
		logger.info("setParameter(PreparedStatement ps, int i, Date parameter,JdbcType jdbcType)....");
		ps.setString(i, String.valueOf(parameter.getTime()));
	}
	//查询后的数据处理
	@Override
	public Date getResult(ResultSet rs, String columnName) throws SQLException {
		logger.info("getResult(ResultSet rs, String columnName)....");
		return new Date(rs.getLong(columnName));
	}
	@Override
	public Date getResult(ResultSet rs, int columnIndex) throws SQLException {
		logger.info("getResult(ResultSet rs, int columnIndex)....");
		return new Date(rs.getLong(columnIndex));
	}
	@Override
	public Date getResult(CallableStatement cs, int columnIndex)
			throws SQLException {
		logger.info("getResult(CallableStatement cs, int columnIndex)....");
		return cs.getDate(columnIndex);
	}

}


  1. 配置自定义处理类–>mybatis-config.xml,注意jdbcType是枚举类型所以只能是大写 VARCHAR
  <typeHandlers>
         <typeHandler javaType="Date" jdbcType="VARCHAR" handler="com.cjy.mybatis.typehandler.MyDateTypeHandler"/>
        <!--  <package name=""/> -->
  </typeHandlers>

  1. sql处理,注意sql中的配置,其中javatype与jdbcType忽略了,完整格式为:

{createtime,typeHandler=com.cjy.mybatis.typehandler.MyDateTypeHandler,javaType=date,jdbcType=VARCHAR}

  <insert id="saveEmpOne" parameterType="com.cjy.mybatis.entity.Employee" >
      INSERT INTO employee(user_name,gender,email,createtime) 
      VALUES(#{userName},#{gender},#{email},#{createtime,typeHandler=com.cjy.mybatis.typehandler.MyDateTypeHandler})
  </insert>

注意:这里只是测试createtime


  1. 测试效果
    @Test
    public void test2(){
    	SqlSessionFactory sqlSessionFactory = MySqlSessionFacoty.getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
		 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
		 int saveEmpOne = mapper.saveEmpOne(new Employee(null, "songba", "1", "songba@163.com", new Date()));
		 System.out.println(saveEmpOne+"-------------------");
	     openSession.commit();
	     openSession.close();
    }
    //setParameter 数据入库前调用此函数
2019-07-23 16:37:23 INFO  com.cjy.mybatis.typehandler.MyDateTypeHandler - setParameter(PreparedStatement ps, int i, Date parameter,JdbcType jdbcType)....
1-------------------

    数据库结果,hobbys字段不用管,下面会介绍这个

在这里插入图片描述

  1. 数据查询效果
  <select id="selectEmp" resultType="com.cjy.mybatis.entity.Employee" parameterType="int">
    select id,user_name userName,gender,email,createtime from employee where id = #{id}
  </select>
    @Test
    public void test2(){
    	SqlSessionFactory sqlSessionFactory = MySqlSessionFacoty.getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
		EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
		Employee selectEmp = mapper.selectEmp(8);
		System.out.println(selectEmp);
	        openSession.commit();
	        openSession.close();
    }
    
//调用此函数:getResult
2019-07-24 14:49:02 INFO  com.cjy.mybatis.typehandler.MyDateTypeHandler - getResult(ResultSet rs, String columnName)....
//结果,createtime字段自动转换
Employee [id=8, userName=tinaqi, gender=1, email=tinaqi@163.com, createtime=Tue Jul 23 16:18:35 CST 2019, hobbys=null]



示例二:list类型转varchar
java程序中的list字段插入到数据库前转换成varchar类型例如
list [1,2,3] ==》varchar 1,2,3
从数据库中加载到程序时自动转换从list类型



代码实现示例:
  1. 编写typehandler实现类,制定转换规则
package com.cjy.mybatis.typehandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

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.apache.log4j.Logger;

@MappedJdbcTypes(JdbcType.VARCHAR)  //数据库类型
@MappedTypes({List.class})			//java数据类型
public class ListTypeHandler implements TypeHandler<List<String>>{

	private Logger logger = Logger.getLogger(ListTypeHandler.class);
	
	@Override
	public void setParameter(PreparedStatement ps, int i,
			List<String> parameter, JdbcType jdbcType) throws SQLException {
		logger.info("method ====>>> setParameter");
        String hobbys = dealListToOneStr(parameter);
        ps.setString(i , hobbys);
	}

	/**
	 * 集合拼接字符串
	 * @param parameter
	 * @return
	 */
	private String dealListToOneStr(List<String> parameter){
		if(parameter == null || parameter.size() <=0)
			return null;
		String res = "";
		for (int i = 0 ;i < parameter.size(); i++) {
			if(i == parameter.size()-1){
				res+=parameter.get(i);
				return res;
			}
			res+=parameter.get(i)+",";
		}
		return null;
	}
	//
	@Override
	public List<String> getResult(ResultSet rs, String columnName)
			throws SQLException {
		logger.info("method ====>>> getResult(ResultSet rs, String columnName)");
		return Arrays.asList(rs.getString(columnName).split(","));
	}

	@Override
	public List<String> getResult(ResultSet rs, int columnIndex)
			throws SQLException {
		logger.info("method ====>>> getResult(ResultSet rs, int columnIndex)");
		  return Arrays.asList(rs.getString(columnIndex).split(","));
	}

	@Override
	public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException{
		logger.info("method ====>>> getResult(CallableStatement cs, int columnIndex)");
	    String hobbys = cs.getString(columnIndex);
	    return Arrays.asList(hobbys.split(","));
	}
}

  1. 配置mybatis xml文件
         <typeHandler javaType="list" jdbcType="VARCHAR" handler="com.cjy.mybatis.typehandler.ListTypeHandler"/>

  1. sql xml 编写
  <select id="selectEmp" resultType="com.cjy.mybatis.entity.Employee" parameterType="int">
    select id,user_name userName,gender,email,hobbys,createtime from employee where id = #{id}
  </select>
  
  <insert id="saveEmpOne" parameterType="com.cjy.mybatis.entity.Employee" >
  <!-- or parameterMap="parameMap" -->
      INSERT INTO employee(user_name,gender,email,createtime,hobbys) 
      VALUES(#{userName},#{gender},#{email},#{createtime,typeHandler=com.cjy.mybatis.typehandler.MyDateTypeHandler},
      #{hobbys,typeHandler=com.cjy.mybatis.typehandler.ListTypeHandler})
      <!--数据插入前:对hobbys做数据格式转换。  -->
  </insert>
  1. 代码测试
    @Test
    public void test2(){
    	SqlSessionFactory sqlSessionFactory = MySqlSessionFacoty.getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
    	 Employee employee = new Employee(null, "yanjiu", "1", "yanjiu@163.com", new Date(),Arrays.asList(new String[]{"yuwen","shuxue","yingyu","zhengzhi"}));
		 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
		 int saveEmpOne = mapper.saveEmpOne(employee);
		 System.out.println(saveEmpOne+"-------------------");
	     openSession.commit();
	     openSession.close();
    }

测试结果成功
在这里插入图片描述

查询数据测试varchar–》list 转换

    @Test
    public void test5(){
    	SqlSessionFactory sqlSessionFactory = MySqlSessionFacoty.getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
		 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
		 Employee selectEmp = mapper.selectEmp(10);
		 System.out.println(selectEmp);
/**数据加载成功
2019-07-24 15:08:02 INFO  com.cjy.mybatis.typehandler.ListTypeHandler - method ====>>> getResult(ResultSet rs, String columnName)
2019-07-24 15:08:02 INFO  com.cjy.mybatis.typehandler.MyDateTypeHandler - getResult(ResultSet rs, String columnName)....
Employee [id=10, userName=yanjiu, gender=1, email=yanjiu@163.com, createtime=Wed Jul 24 15:05:58 CST 2019, hobbys=[yuwen, shuxue, yingyu, zhengzhi]]
 */
    }



typeHandlers注册方式:
  1. 单个注册:typeHandler 标签指定handler 全类名
  2. 多个注册:指定多个单个注册方式;或者使用package 标签
  <typeHandlers>
         <!-- <typeHandler javaType="list" jdbcType="VARCHAR" handler="com.cjy.mybatis.typehandler.ListTypeHandler"/>
         <typeHandler javaType="Date" jdbcType="VARCHAR" handler="com.cjy.mybatis.typehandler.MyDateTypeHandler"/> -->
         <package name="com.cjy.mybatis.typehandler"/>
         <!--
         typeHandler: 单个注册方式
         package:多个注册方式
           -->
  </typeHandlers>

至此结束typeHandler 介绍

demo工程:
https://github.com/chenjy512/mybatis-demo/tree/master/source-mybatis-typehandler

  • 15
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值