场景
在 Java 实体类中,定义时间的属性一般是Date 类型,但数据库中却是字符串类型,每次查询、新增等操作都要做类型转换,但如果使用 Mybatis 提供的自定义 TypeHandler 的功能就能完美的解决这个问题。
实体类User.java
package com.lks.domain;
import java.io.Serializable;
import java.util.Date;
/**
* Created by likaisong on 2019/2/24.
*/
public class User implements Serializable {
private int id;
private String name;
private int age;
private String county;
private Date date;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getCounty() {
return county;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setCounty(String county) {
this.county = county;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date= date;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + ", county=" + county + ", date=" + date + "]";
}
}
自定义TypeHandle,有两种实现方式:直接实现 TypeHandler 也可以继承 BaseTypeHandler,这里我选择直接实现 TypeHandler :
DateToStringTypeHandler.java
package com.lks.handler;
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;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by likaisong on 2019/2/25.
*/
public class DateToStringTypeHandler implements TypeHandler{
/**
* 将时间戳传入数据库
* @param preparedStatement
* @param i
* @param o
* @param jdbcType
* @throws java.sql.SQLException
*/
@Override
public void setParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {
Date date = (Date) o;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = formatter.format(date);
preparedStatement.setString(i, dateString);
}
/**
* 将字符串类型的时间戳转换为Date
* @param resultSet
* @param s
* @return
* @throws SQLException
*/
@Override
public Object getResult(ResultSet resultSet, String s) throws SQLException {
String timeStr = resultSet.getString(s);
if (timeStr != null || "".equals(timeStr)){
return stringToDate(timeStr);
}
return null;
}
@Override
public Object getResult(ResultSet resultSet, int i) throws SQLException {
String timeStr = resultSet.getString(i);
if (timeStr != null || "".equals(timeStr)){
return stringToDate(timeStr);
}
return null;
}
@Override
public Object getResult(CallableStatement callableStatement, int i) throws SQLException {
String timeStr = callableStatement.getString(i);
if (timeStr != null || "".equals(timeStr)){
return stringToDate(timeStr);
}
return null;
}
private Date stringToDate(String timeStr) {
if (timeStr != null || "".equals(timeStr)) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ParsePosition pos = new ParsePosition(0);
Date date = formatter.parse(timeStr, pos);
return date;
}
return null;
}
}
mybatis配置文件中配置自定义typeHandler,主要看typeHandler标签:
mybatis-config:
<?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>
<typeHandlers>
<typeHandler handler="com.lks.handler.DateToStringTypeHandler" javaType="java.util.Date" jdbcType = "VARCHAR"/>
</typeHandlers>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="userMapper.xml"/>
</mappers>
</configuration>
mapper文件中新增或查询操作:
<?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,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的-->
<mapper namespace="userMapper">
<!--类型转换-->
<insert id="dateToString" parameterType="com.lks.domain.User" useGeneratedKeys="true" keyProperty="id">
insert into users (id, name, age, county, date)
values (#{id},#{name},#{age},#{county},#{date, typeHandler=com.lks.handler.DateToStringTypeHandler})
</insert>
<resultMap id="userResultMap" type="com.lks.domain.User">
<result property="date" column="date" typeHandler="com.lks.handler.DateToStringTypeHandler"/>
</resultMap>
<select id="stringToDate" parameterType="int"
resultType="com.lks.domain.User" resultMap="userResultMap">
select * from users where id=#{id}
</select>
</mapper>
新增数据的操作只要对需要进行类型转换的字段添加相关配置即可,但查询操作需要借助resultMap来实现转换。
对操作执行测试方法:
TestMain.java
package com.lks.test;
import com.lks.domain.User;
import com.lks.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 org.testng.annotations.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
/**
* Created by likaisong on 2019/2/24.
*/
public class TestMain {
private static SqlSession getSqlSession(String resource){
if (resource == null || "".equals(resource)){
return null;
}
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
return session;
}
@Test
public static void testDateToString(){
String resource = "mybatis-config.xml";
SqlSession session = getSqlSession(resource);
try {
String statement = "userMapper.dateToString";
User user = new User();
user.setName("阿飞");
user.setAge(20);
user.setCounty("中国");
user.setDate(new Date());
session.insert(statement, user);
session.commit();
System.out.println("插入数据后返回的自增ID:" + user.getId());
} finally {
session.close();
}
}
@Test
public static void testStringToDate(){
String resource = "mybatis-config.xml";
SqlSession session = getSqlSession(resource);
try {
//先插入一条数据
String insertStatement = "userMapper.dateToString";
User user = new User();
user.setName("阿飞");
user.setAge(20);
user.setCounty("中国");
user.setDate(new Date());
session.insert(insertStatement, user);
session.commit();
System.out.println("插入数据后返回的自增ID:" + user.getId());
//查询刚插入的数据
String selectStatement = "userMapper.stringToDate";
User resUser = session.selectOne(selectStatement, user.getId());
System.out.println(resUser);
} finally {
session.close();
}
}
}
通过查表或由直接打印出来对内容可以看到时间字段可以在Date和String中实现灵活准换,自定义的 TypeHandler 也经常用来解决项目中的枚举类型,也是比较方便的。