mybatis的类型转换器typeHandler是mybatis用于Java类型与数据库类型之间的转换用到的东西,事实上mybatis的typeHandler已经能够应对大部分的场景了,但是有时候也会不够用,比如在使用枚举的时候,枚举有特殊的转化规则,这时就需要用到自定义的类型转换器来处理了。
我在使用时出现了两次这样类似的异常如图:
org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in mapper/UserMapper.xml
### The error occurred while processing mapper_resultMap[userMapper]
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.type.TypeException: Unable to find a usable constructor for class handler.SexEnumTypeHandler
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
at util.SqlSessionFactoryUtils.getSqlSessionFactory(SqlSessionFactoryUtils.java:28)
at util.SqlSessionFactoryUtils.openSqlSession(SqlSessionFactoryUtils.java:42)
at personal.Test.test1(Test.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at junit.framework.TestCase.runTest(TestCase.java:176)
at junit.framework.TestCase.runBare(TestCase.java:141)
at junit.framework.TestResult$1.protect(TestResult.java:122)
at junit.framework.TestResult.runProtected(TestResult.java:142)
at junit.framework.TestResult.run(TestResult.java:125)
at junit.framework.TestCase.run(TestCase.java:129)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:121)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.type.TypeException: Unable to find a usable constructor for class handler.SexEnumTypeHandler
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
... 20 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.type.TypeException: Unable to find a usable constructor for class handler.SexEnumTypeHandler
at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:120)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.parse(XMLMapperBuilder.java:92)
at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.loadXmlResource(MapperAnnotationBuilder.java:170)
at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parse(MapperAnnotationBuilder.java:121)
at org.apache.ibatis.binding.MapperRegistry.addMapper(MapperRegistry.java:72)
at org.apache.ibatis.binding.MapperRegistry.addMappers(MapperRegistry.java:97)
at org.apache.ibatis.binding.MapperRegistry.addMappers(MapperRegistry.java:105)
at org.apache.ibatis.session.Configuration.addMappers(Configuration.java:709)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:359)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:118)
... 22 more
Caused by: org.apache.ibatis.type.TypeException: Unable to find a usable constructor for class handler.SexEnumTypeHandler
at org.apache.ibatis.type.TypeHandlerRegistry.getInstance(TypeHandlerRegistry.java:360)
at org.apache.ibatis.builder.BaseBuilder.resolveTypeHandler(BaseBuilder.java:143)
at org.apache.ibatis.builder.MapperBuilderAssistant.buildResultMapping(MapperBuilderAssistant.java:377)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildResultMappingFromContext(XMLMapperBuilder.java:378)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:280)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:252)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElements(XMLMapperBuilder.java:244)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:116)
... 31 more
Caused by: java.lang.ClassCastException: handler.SexEnumTypeHandler cannot be cast to org.apache.ibatis.type.TypeHandler
at org.apache.ibatis.type.TypeHandlerRegistry.getInstance(TypeHandlerRegistry.java:358)
... 38 more
<mapper namespace="mapper.UserMapper">
<resultMap id="userMapper" type="user">
<result property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="sex" column="sex"
typeHandler="handler.SexEnumTypeHandler"/>
<result property="mobile" column="mobile"/>
<result property="tel" column="tel"/>
<result property="email" column="email"/>
<result property="note" column="note"/>
resultMap>
<select id="getUser" resultMap="userMapper" parameterType="long">
select id,user_name,password,sex,mobile,tel,email,note from t_user
where id=#{id}
select>
mapper>
我先检查了数据库要转换的jdbc类型是否有些错,发现没有问题,然后检查sql语句自动转换的resultMap是否有指向到上面自己定义的resultMap,发现也没有问题,那么是不是自己定义的resultMap字段映射不对呢,这里我将我自定义的类型转换器换成了apache自己定义的。
<mapper namespace="mapper.UserMapper">
<resultMap id="userMapper" type="user">
<result property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="sex" column="sex"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
<result property="mobile" column="mobile"/>
<result property="tel" column="tel"/>
<result property="email" column="email"/>
<result property="note" column="note"/>
resultMap>
<select id="getUser" resultMap="userMapper" parameterType="long">
select id,user_name,password,sex,mobile,tel,email,note from t_user
where id=#{id}
select>
mapper>
然后执行了测试方法,发现测试通过了,说明是我自己定义的SexEnumTypeHandler枚举类型转换器出了问题。
那么我的自定义枚举类型哪里不对呢,后来发现是我实现的接口的包不对,我导入的是我自己写的接口的包,我换成apache的包后就好了。
那么问题又来了,我自己写的接口那里不对呢?
package handler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
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 pojo.SexEnum;
@MappedTypes(SexEnum.class)
@MappedJdbcTypes(JdbcType.INTEGER)
public class SexEnumTypeHandler implements TypeHandler {
public void setParameter(PreparedStatement ps, int i, SexEnum parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getId());
}
public SexEnum getResult(ResultSet rs, String columnName) throws SQLException {
int id = rs.getInt(columnName);
return SexEnum.getSexById(id);
}
public SexEnum getResult(ResultSet rs, int columnIndex) throws SQLException {
int id = rs.getInt(columnIndex);
return SexEnum.getSexById(id);
}
public SexEnum getResult(CallableStatement cs, int columnIndex) throws SQLException {
int id = cs.getInt(columnIndex);
return SexEnum.getSexById(id);
}
}
自己写的TypeHandler接口
package handler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.JdbcType;
public interface TypeHandler {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
apachet提供的TypeHandler接口
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author Clinton Begin
*/
public interface TypeHandler {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
后来经过大神指点原来Mybatis只能认定它自己的接口,自己重写的接口是不能用的