目录
一、引言
MyBatis的类型处理器(TypeHandler)是一种用于处理数据库返回的数据类型的机制,它可以将数据库返回的数据类型转换为Java对象,从而实现对数据的操作。在Mybatis中给我们内置提供了多种常用的类型处理器,这也就是为什么我们在日常开发过程中,不需要关心Java和数据库之间的类型问题,Java代码和MySQL等数据库之间却能自动进行转化的原因。
Mybatis除了提供多种内置的常用的类型处理器,同时也支持我们按需求自定义类型处理器。
二、类型处理器的原理及使用场景
-
核心概念与联系:MyBatis的类型处理器是MyBatis中的一个重要组件,它可以处理数据库返回的基本数据类型(如int、double、String等)和复杂数据类型(如Date、Blob、Clob等)。在MyBatis的映射文件中,可以通过类型处理器来指定数据库返回的数据类型如何转换为Java对象。
-
工作原理:MyBatis的类型处理器基于数据库返回的数据类型和Java对象之间的映射关系进行工作。首先,MyBatis需要解析映射文件中的类型处理器配置,以获取数据库返回的数据类型和Java对象之间的映射关系。然后,MyBatis需要根据映射关系,将数据库返回的数据类型转换为Java对象。最后,MyBatis需要将转换后的Java对象返回给调用方。
-
自定义类型处理器:MyBatis允许用户自定义类型处理器来处理特殊的数据类型或转换逻辑。通过继承
BaseTypeHandler
类并实现相关方法,用户可以创建自己的类型处理器来处理特定的数据类型转换需求,例如枚举类型、JSON数据等。 -
应用场景:无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会使用类型处理器将获取的值以合适的方式转换成Java类型。这确保了数据在Java代码和数据库之间正确、高效地转换。
综上所述,MyBatis的类型处理器是连接Java代码和数据库的桥梁,通过提供灵活的类型转换机制,极大地简化了数据库操作,提高了开发效率。
三、自定义类型处理器
我们可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型。
3.1. 具体做法
1. 实现org.apache.ibatis.type.TypeHandler接口
或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler
2. 选择性地将它映射到一个JDBC类型。
3.2. 需求
一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
3.3. 开发步骤
① 定义转换类继承类BaseTypeHandler<T>
② 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成java的Type类型的方法。
③ 在MyBatis核心配置文件中进行注册
④ 测试转换是否正确
四、完整代码实现
4.1. 工程结构图
4.2. 类型处理器代码
package com.example.handler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
public class DateTypeHandler extends BaseTypeHandler<Date> {
/**
* 将Java类型转换成数据库需要的类型
* @param ps JDBC的预执行对象
* @param i 字段所在的索引位置
* @param date 要转换的Java日期字段值
* @param jdbcType 要转换的数据库类型
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Date date, JdbcType jdbcType) throws SQLException {
// 将Java日期转换成符合数据库的长整型
long time = date.getTime();
ps.setLong(i, time);
}
//----------- 下面的三方法目的是一样的,只不过Mybatis有时候会调用不同的参数方法 ------------------
/**
* 将数据库中类型转换成Java类型
* @param rs 数据库返回的结果集,包含了要转换的字段值
* @param columnName 要转换的字段名称
* @return
* @throws SQLException
*/
@Override
public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 获取结果集中需要的某个字段数据
long num = rs.getLong(columnName);
return new Date(num);
}
/**
* 将数据库中类型转换成Java类型
* @param rs
* @param columnIndex
* @return
* @throws SQLException
*/
@Override
public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
// 获取结果集中需要的某个字段数据
long num = rs.getLong(columnIndex);
return new Date(num);
}
/**
* 将数据库中类型转换成Java类型
* @param cs
* @param columnIndex
* @return
* @throws SQLException
*/
@Override
public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Long num = cs.getLong(columnIndex);
return new Date(num);
}
}
4.3. 核心配置代码注册类型处理器
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 加载外部properties -->
<properties resource="jdbc.properties"></properties>
<settings>
<!-- 其他配置 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<typeAlias type="com.example.domain.User" alias="user"></typeAlias>
</typeAliases>
<!-- 注册类型处理器 -->
<typeHandlers>
<typeHandler handler="com.example.handler.DateTypeHandler"></typeHandler>
</typeHandlers>
<!-- 数据源环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>
</configuration>
4.4. UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- 插入操作 -->
<insert id="save" useGeneratedKeys="true" parameterType="user">
insert into user values(#{id}, #{username}, #{password}, #{birthday})
</insert>
<!-- 查询操作 -->
<select id="findById" parameterType="java.lang.Integer" resultType="user">
select * from user where id = #{id}
</select>
</mapper>
4.5. properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=Wangzhexiao
4.6. User实体类
package com.example.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private int id;
private String username;
private String password;
private Date birthday;
}
4.7. UserMapper
package com.example.mapper;
import com.example.domain.User;
import java.io.IOException;
import java.util.List;
public interface UserMapper {
void save(User user) throws IOException;
List<User> findById(Integer id) throws IOException;
}
4.8. UserService
package com.example.service;
import com.example.domain.User;
import com.example.mapper.UserMapper;
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 java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class UserService {
public static void main(String[] args) throws IOException {
// 获得核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 获得Session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得Session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取Dao代理对象,Mybatis内部已经帮我们完成了实现(与我们传统方式代码中的实现类相似)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 模拟条件user
User user = new User();
user.setUsername("孙红雷");
user.setPassword("sunhonglei");
user.setBirthday(new Date());
mapper.save(user);
System.out.println(mapper.findById(3));
}
}
五、效果展示