前言:今天介绍一个mybatis操作数据库时的一个类似黑匣子的东西,TypeHandler
闲聊:
在我们平常开发操作数据库时,查询、插入数据等操作行为,有时会报数据类型不匹配异常,就可以得知数据的类型是不唯一的必然是多种不同的数据类型。并且我们必须要明确的一点就是java作为一门编程语言有自己的数据类型,数据库也是有自己的数据类型的。
jdbc数据类型:org.apache.ibatis.type.JdbcType 此枚举就是所有的数据库支持类型
java数据类型:int、long、string、…
一定要分清,例如java重的date数据插入到数据库中,应该是已经转换成了数据库的某种类型,必然跟java已经没有关系了。中间有一些我们看不见的操作做了数据处理。
假设此时的java类型与数据库数据类型是一样的,哪么其他语言中的日期数据插入数据库时又该怎么解释,例如C#操作数据库存入时间类型,C#与java肯定没有关系吧。所以每种语言与数据库之间有种数据类型关系对应。
思考:
因为java与数据库各自有数据类型,所以在将java数据存入数据库前中间是否有其他操作,是我们看不见的,不然java数据怎么知道自己与哪个jdbc数据类型匹配?
答:mybatis框架为每种数据类型做了默认的关系对应,BaseTypeHandler的所有实现类,就是来做这些处理的。
例如: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;
}
}
将date数据插入数据库varchar字段,并显示结果为距1970年的秒数
将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
}
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);
}
}
<typeHandlers>
<typeHandler javaType="Date" jdbcType="VARCHAR" handler="com.cjy.mybatis.typehandler.MyDateTypeHandler"/>
typeHandlers>
{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
@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", "[email protected]", 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-------------------
<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类型
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(","));
}
}
<typeHandler javaType="list" jdbcType="VARCHAR" handler="com.cjy.mybatis.typehandler.ListTypeHandler"/>
<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" >
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})
insert>
@Test
public void test2(){
SqlSessionFactory sqlSessionFactory = MySqlSessionFacoty.getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
Employee employee = new Employee(null, "yanjiu", "1", "[email protected]", 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 protected], createtime=Wed Jul 24 15:05:58 CST 2019, hobbys=[yuwen, shuxue, yingyu, zhengzhi]]
*/
}
<typeHandlers>
<package name="com.cjy.mybatis.typehandler"/>
typeHandlers>
至此结束typeHandler 介绍
demo工程:
https://github.com/chenjy512/mybatis-demo/tree/master/source-mybatis-typehandler