Skip to content

Commit

Permalink
fixes #591 TypeHandlerRegistry#getTypeHandler() now returns a type ha…
Browse files Browse the repository at this point in the history
…ndler if it's the only choice for a specified javaType regardless of jdbcType it is mapped to. When there are multiple type handlers mapped to a single javaType, one of them must be mapped to null jdbcType.
  • Loading branch information
harawata committed Mar 7, 2016
1 parent 0cfa24c commit b3f0e9f
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 9 deletions.
17 changes: 17 additions & 0 deletions src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
if (handler == null) {
handler = jdbcHandlerMap.get(null);
}
if (handler == null) {
// #591
handler = pickSoleHandler(jdbcHandlerMap);
}
}
if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
handler = new EnumTypeHandler((Class<?>) type);
Expand All @@ -182,6 +186,19 @@ private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
return (TypeHandler<T>) handler;
}

private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
boolean onlyChoice = true;
TypeHandler<?> soleHandler = null;
for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
if (soleHandler != null && !handler.equals(soleHandler)) {
onlyChoice = false;
break;
}
soleHandler = handler;
}
return onlyChoice ? soleHandler : null;
}

public TypeHandler<Object> getUnknownTypeHandler() {
return UNKNOWN_TYPE_HANDLER;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public interface Mapper {
@Select("select id, name from product where name = #{value}")
Product getProductByName(String name);

Product getProductByNameXml(String name);

@Select("select id, name from product where name = #{value}")
@ConstructorArgs({
@Arg(id = true, column="id", javaType = ProductId.class, jdbcType=JdbcType.INTEGER),
Expand Down
33 changes: 33 additions & 0 deletions src/test/java/org/apache/ibatis/submitted/typehandler/Mapper.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2009-2012 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.
-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.apache.ibatis.submitted.typehandler.Mapper">

<resultMap type="org.apache.ibatis.submitted.typehandler.Product"
id="productResult">
<result property="id" column="id" />
<result property="name" column="name" />
</resultMap>

<select id="getProductByNameXml" resultMap="productResult">
select * from product where name = #{name}
</select>

</mapper>
27 changes: 27 additions & 0 deletions src/test/java/org/apache/ibatis/submitted/typehandler/Product.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,31 @@ public ProductId getNullableResult(CallableStatement cs, int columnIndex) throws
return id;
}
}

public static class ConstantProductIdTypeHandler extends BaseTypeHandler<ProductId> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, ProductId parameter, JdbcType jdbcType) throws SQLException {
}

@Override
public ProductId getNullableResult(ResultSet rs, String columnName) throws SQLException {
return getConstantId();
}

@Override
public ProductId getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return getConstantId();
}

@Override
public ProductId getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return getConstantId();
}

private ProductId getConstantId() {
ProductId id = new ProductId();
id.setValue(999);
return id;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,29 @@
import java.io.Reader;
import java.sql.Connection;

import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.submitted.typehandler.Product.ProductId;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.apache.ibatis.submitted.typehandler.Product.ConstantProductIdTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.junit.Before;
import org.junit.Test;

public class TypeHandlerTest {

private static SqlSessionFactory sqlSessionFactory;
private SqlSessionFactory sqlSessionFactory;

@BeforeClass
public static void setUp() throws Exception {
@Before
public void setUp() throws Exception {
// create a SqlSessionFactory
Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/typehandler/mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
reader.close();
sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().register(StringTrimmingTypeHandler.class);
sqlSessionFactory.getConfiguration().addMapper(Mapper.class);

// populate in-memory database
SqlSession session = sqlSessionFactory.openSession();
Expand All @@ -54,22 +55,30 @@ public static void setUp() throws Exception {
session.close();
}

// Some tests need to register additional type handler
// before adding mapper.
private void addMapper() {
sqlSessionFactory.getConfiguration().addMapper(Mapper.class);
}

@Test
public void shouldGetAUser() {
addMapper();
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
User user = mapper.getUser(1);
Assert.assertEquals("User1", user.getName());
Assert.assertEquals("Carmel", user.getCity());
Assert.assertEquals("IN", user.getState());
assertEquals("User1", user.getName());
assertEquals("Carmel", user.getCity());
assertEquals("IN", user.getState());
} finally {
sqlSession.close();
}
}

@Test
public void shouldApplyTypeHandlerOnGeneratedKey() {
addMapper();
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
Expand All @@ -85,6 +94,7 @@ public void shouldApplyTypeHandlerOnGeneratedKey() {

@Test
public void shouldApplyTypeHandlerWithJdbcTypeSpecified() throws Exception {
addMapper();
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
Expand All @@ -97,6 +107,7 @@ public void shouldApplyTypeHandlerWithJdbcTypeSpecified() throws Exception {

@Test
public void shouldApplyTypeHandlerUsingConstructor() throws Exception {
addMapper();
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
Expand All @@ -109,6 +120,7 @@ public void shouldApplyTypeHandlerUsingConstructor() throws Exception {

@Test
public void shouldApplyTypeHandlerOnReturnTypeWithJdbcTypeSpecified() throws Exception {
addMapper();
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
Expand All @@ -118,4 +130,41 @@ public void shouldApplyTypeHandlerOnReturnTypeWithJdbcTypeSpecified() throws Exc
sqlSession.close();
}
}

@Test
public void shouldPickSoleTypeHandlerOnXmlResultMap() throws Exception {
addMapper();
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
Product product = mapper.getProductByNameXml("iPad");
assertEquals(Integer.valueOf(2), product.getId().getValue());
} finally {
sqlSession.close();
}
}

@Test(expected = BuilderException.class)
public void shouldFailIfMultipleHandlerMappedToAType() throws Exception {
sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().register(ProductId.class, JdbcType.BIGINT, ConstantProductIdTypeHandler.class);
// multiple type handlers are mapped to ProductId and
// none of them are mapped to null jdbcType.
addMapper();
}

@Test
public void shouldPickHandlerForNull() throws Exception {
sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().register(ProductId.class, null, ConstantProductIdTypeHandler.class);
// multiple type handlers are mapped to ProductId and
// one of them are mapped to null jdbcType.
addMapper();
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
Product product = mapper.getProductByNameXml("iPad");
assertEquals(Integer.valueOf(999), product.getId().getValue());
} finally {
sqlSession.close();
}
}
}

0 comments on commit b3f0e9f

Please sign in to comment.