在Mybatis中可以定义一个TypeHandler类型,通过它可以实现Java类型跟数据库类型的相互转换。
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* Gets the result.
*
* @param rs
* the rs
* @param columnName
* Colunm name, when configuration useColumnLabel
is false
* @return the result
* @throws SQLException
* the SQL exception
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
typeHandler接口实现了4个方法,熟悉jdbc应该从方法定义就可以看懂这写接口方法的意思。
不过多解释了,如果不熟悉还是多写写原生jdbc
Mybatis还为我们提供一个实现了TypeHandler接口的抽象类BaseTypeHandler。所以我们也可以通过继承BaseTypeHandler来实现自己的TypeHandler。
// 继承TypeReference主要提供了一个方法用来获取父类的泛型类型
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
/**
* @deprecated Since 3.5.0 - See https://github.com/mybatis/mybatis-3/issues/1203. This field will remove future.
*/
@Deprecated
protected Configuration configuration;
/**
* Sets the configuration.
*
* @param c
* the new configuration
* @deprecated Since 3.5.0 - See https://github.com/mybatis/mybatis-3/issues/1203. This property will remove future.
*/
@Deprecated
public void setConfiguration(Configuration c) {
this.configuration = c;
}
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
+ "Cause: " + e, e);
}
} else {
try {
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different configuration property. "
+ "Cause: " + e, e);
}
}
}
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
try {
return getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
}
@Override
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
try {
return getNullableResult(rs, columnIndex);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set. Cause: " + e, e);
}
}
@Override
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
try {
return getNullableResult(cs, columnIndex);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement. Cause: " + e, e);
}
}
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* Gets the nullable result.
*
* @param rs
* the rs
* @param columnName
* Colunm name, when configuration useColumnLabel
is false
* @return the nullable result
* @throws SQLException
* the SQL exception
*/
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}
public abstract class TypeReference<T> {
private final Type rawType;
//子类调用父类构造器时调用getSuperclassTypeParameter方法获取rawType
protected TypeReference() {
rawType = getSuperclassTypeParameter(getClass());
}
Type getSuperclassTypeParameter(Class<?> clazz) {
// 主要为获取父类泛型及父类类型,Type是接口实现类是ParameterizedTypeImpl
Type genericSuperclass = clazz.getGenericSuperclass();
if (genericSuperclass instanceof Class) {
// try to climb up the hierarchy until meet something useful
if (TypeReference.class != genericSuperclass) {
return getSuperclassTypeParameter(clazz.getSuperclass());
}
throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
+ "Remove the extension or add a type parameter to it.");
}
// 获取泛型类型,如
Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
// TODO remove this when Reflector is fixed to return Types
if (rawType instanceof ParameterizedType) {
rawType = ((ParameterizedType) rawType).getRawType();
}
return rawType;
}
public final Type getRawType() {
return rawType;
}
@Override
public String toString() {
return rawType.toString();
}
}
1、定义表结构
CREATE TABLE
dept
(
id
int(11) NOT NULL AUTO_INCREMENT,
name
varchar(255) DEFAULT NULL,
loc
varchar(255) DEFAULT NULL,
list
varchar(255) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
pom以来
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent-mybatisartifactId>
<groupId>org.examplegroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>mybatis-demo1artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>8source>
<target>8target>
configuration>
plugin>
plugins>
build>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.49version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiterartifactId>
<version>RELEASEversion>
<scope>compilescope>
dependency>
dependencies>
project>
2、定义实体
package com.lhl.demo1;
import java.util.List;
public class Dept {
private int id;
private String name;
private String loc;
private List<String> list;
public List<String> getList() {
return list;
}
public Dept setList(List<String> list) {
this.list = list;
return this;
}
public int getId() {
return id;
}
public Dept setId(int id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public Dept setName(String name) {
this.name = name;
return this;
}
public String getLoc() {
return loc;
}
public Dept setLoc(String loc) {
this.loc = loc;
return this;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Dept{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append(", loc='").append(loc).append('\'');
sb.append(", list=").append(list);
sb.append('}');
return sb.toString();
}
}
3、mybatis配置文件及mapper文件
<configuration>
<typeHandlers>
<typeHandler handler="com.lhl.demo1.MyTypeHandler" javaType="List" jdbcType="VARCHAR">typeHandler>
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-test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="DeptMapper.xml"/>
mappers>
configuration>
<mapper namespace="com.lhl.demo1.DeptMapper">
<insert id="insertDept">
insert into dept(name, loc, list)
// 配置typeHandler
values (#{name}, #{loc}, #{list,javaType=List,jdbcType=VARCHAR,typeHandler=com.lhl.demo1.MyTypeHandler})
insert>
// 配置typeHandler
<resultMap id="selectDeptMap" type="com.lhl.demo1.Dept">
<result column="list" property="list" typeHandler="com.lhl.demo1.MyTypeHandler">result>
resultMap>
<select id="selectDept" resultMap="selectDeptMap">
select *
from dept
select>
resultMap>
mapper>
mapper接口
package com.lhl.demo1;
import java.util.List;
public interface DeptMapper {
void insertDept();
public List<Dept> selectDept();
}
自定义TypeHandler写法
package com.lhl.demo1;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
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 java.util.stream.Collectors;
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class MyTypeHandler extends BaseTypeHandler<List> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, List parameter, JdbcType jdbcType) throws SQLException {
String str = String.valueOf(parameter.stream().collect(Collectors.joining(",")));
ps.setString(i,str);
}
@Override
public List getNullableResult(ResultSet rs, String columnName) throws SQLException {
String result = rs.getString(columnName);
return Arrays.asList(result.split(","));
}
@Override
public List getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}
@Override
public List getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}
1.@MappedJdbcTypes定义是JdbcType类型,必须要是枚举类org.apache.ibatis.type.JdbcType所枚举的数据类型
2.@MappedTypes定义的是JavaType的数据类型,描述哪些Java类型可被拦截。
将TypeHandler注册到mybatis中
<typeHandlers>
<typeHandler handler="com.lhl.demo1.MyTypeHandler" javaType="List" jdbcType="VARCHAR">typeHandler>
typeHandlers>
<typeHandlers>
<package name="com.lhl.demo1"/>
typeHandlers>
在Mapper中配置
<insert id="insertDept">
insert into dept(name, loc, list)
// 配置typeHandler
values (#{name}, #{loc}, #{list,javaType=List,jdbcType=VARCHAR,typeHandler=com.lhl.demo1.MyTypeHandler})
insert>
// 配置typeHandler
<resultMap id="selectDeptMap" type="com.lhl.demo1.Dept">
<result column="list" property="list" typeHandler="com.lhl.demo1.MyTypeHandler">result>
resultMap>
<select id="selectDept" resultMap="selectDeptMap">
select *
from dept
select>
测试类
package com.lhl.demo1;
import org.apache.ibatis.executor.result.DefaultResultHandler;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
public class Test01 {
SqlSession sqlSession = null;
@BeforeEach
public void before(){
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
}
@Test
public void test1(){
Dept dept =new Dept();
dept.setLoc("湖北");
dept.setName("金融事业单位");
dept.setList(Arrays.asList("1","2","3333"));
int insertDept = sqlSession.insert("insertDept", dept);
sqlSession.commit();
}
@Test
public void test2(){
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
System.out.println(mapper.selectDept());
}
}
TypeHandlerRegistry
自定义的TypeHandler起作用主要依赖TypeHandlerRegistry进行注册
register方法定义了一部分mybatis自己的类型转换方式,自己实现的typeHandler最终也是调用的register方法来进行注册的。
如:register(List.class,new MyTypeHandler() );
register(JdbcType.VARCHAR,new MyTypeHandler() );
对应到@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(Reader.class, new ClobReaderTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new StringTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, unknownTypeHandler);
register(Object.class, JdbcType.OTHER, unknownTypeHandler);
register(JdbcType.OTHER, unknownTypeHandler);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
register(Instant.class, new InstantTypeHandler());
register(LocalDateTime.class, new LocalDateTimeTypeHandler());
register(LocalDate.class, new LocalDateTypeHandler());
register(LocalTime.class, new LocalTimeTypeHandler());
register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
register(OffsetTime.class, new OffsetTimeTypeHandler());
register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
register(Month.class, new MonthTypeHandler());
register(Year.class, new YearTypeHandler());
register(YearMonth.class, new YearMonthTypeHandler());
register(JapaneseDate.class, new JapaneseDateTypeHandler());
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());