Custom TypeHandler not getting called if I use selectByExample

1,194 views
Skip to first unread message

Melvin Lim

unread,
Feb 19, 2014, 12:42:42 AM2/19/14
to mybati...@googlegroups.com
Hi folks,

I'm using MyBatis 3.2.5 and MyBatis Generator 1.3.2.

I installed custom TypeHandlers for jdbcTypes TIMESTAMP and DOUBLE to massage the data to and from the database.

In both cases, it works with insert and select, but when I use selectByExample (generated by MyBatis generator), it goes back and use the default TypeHandler.

I've attached my config and xml mapper. I did try to add typeHandler property in the BaseResultMap, but it doesn't seem to help.

Could anybody please give me a hint what I'm doing wrong?

Thanks,

Melvin




DoubleTypeHandler.java
mybatis-config.xml
TestRowMapper.xml

Paul Krause

unread,
Feb 19, 2014, 11:02:55 AM2/19/14
to mybati...@googlegroups.com
You did not post your config file for MyBatis Generator, but I suspect that it is where the problem lies.

Melvin Lim

unread,
Feb 19, 2014, 11:56:22 AM2/19/14
to mybati...@googlegroups.com
Here it is. Thanks.
mybatis-generator-config.xml

Paul Krause

unread,
Feb 19, 2014, 1:19:23 PM2/19/14
to mybati...@googlegroups.com
Try adding column overrides like this

<table tableName="test" domainObjectName="TestRow">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
<columnOverride column="double_data" property="doubleData" javaType="Double" typeHandler="com.extrategic.dashboard.db.type.DoubleTypeHandler" />
</table>

Melvin Lim

unread,
Feb 19, 2014, 3:10:27 PM2/19/14
to mybati...@googlegroups.com
Thanks, Paul! That made it work.

And the main difference I see is that the criterion produced by the generator now has an explicit typeHandler:

<when test="criterion.singleValue">
   and ${criterion.condition} #{criterion.value,typeHandler=com.extrategic.dashboard.db.type.DoubleTypeHandler}
</when>


I actually went back to my old config without the override and hacked the TestRowMapper.xml id="Example_Where_Clause" like this:

<when test="criterion.singleValue">
   and ${criterion.condition} #{criterion.value,jdbcType=DOUBLE}
</when>


And it also worked. So this appears to be a MyBatis Generator limitation. If it could produce something like (syntax is probably wrong, and probably implementation is not trivial):

<when test="criterion.singleValue">
   and ${criterion.condition} #{criterion.value,jdbcType=criterion.jdbcType}
</when>


then I think it would be able to pickup the custom handler.

I could use this column override solution, but I have thousands of Double data type in my database. What I'm really trying to achieve is to intercept the Double data types so I can encode the special values like NaN to and from the database. So another way to solve the problem is to globally override the Double data type handler. Is there a way to do this? This configuration:

  <typeHandlers>
    <typeHandler handler="com.extrategic.dashboard.db.type.DoubleTypeHandler"/>
  </typeHandlers>

Only seem to work if it's in the form #{value, jdbcType=DOUBLE}. If I remove the jdbcType it falls back to the original DoubleTypeHandler, even in insert and select statements.

Thanks,

Melvin

Jeff Butler

unread,
Feb 19, 2014, 3:37:33 PM2/19/14
to mybati...@googlegroups.com
This is a limitation in the way the dynamic SQL is created by the generator.  If you notice, you'll see that the generated where clause is about twice as big as the original and it will grow and grow the more you add these column overrides.

I'd first try making your type handler a bit easier for MyBatis to find by adding the @MappedTypes annotation to it.  That might make it work without having to do these column overrides.

Jeff Butler



--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Melvin Lim

unread,
Feb 19, 2014, 6:22:45 PM2/19/14
to mybati...@googlegroups.com
Hi Jeff,

I tried:

Option #1: it didn't work

@MappedTypes(value=Double.class)
@MappedJdbcTypes(JdbcType.DOUBLE)
public class DoubleTypeHandler extends BaseTypeHandler<Double>

Option #2: it works
@MappedTypes(value=Double.class)
// @MappedJdbcTypes(JdbcType.DOUBLE)
public class DoubleTypeHandler extends BaseTypeHandler<Double>

Option #3: it works
// @MappedTypes(value=Double.class)
// @MappedJdbcTypes(JdbcType.DOUBLE)
public class DoubleTypeHandler extends BaseTypeHandler<Double>

Is this expected behavior?

Anyway, I think I'll just opt for Option #2. It's a little inconvenient for Date-type, but I guess I could just write a TypeHandler that can accommodate TIMESTAMP, DATE, and TIME dynamically.

Jeff and Paul, THANK YOU for your help!

Melvin

Ikchan Sim

unread,
Feb 19, 2014, 7:42:11 PM2/19/14
to mybati...@googlegroups.com
Hi.

The basic idea is for type handlers, refer to the following file.
And try to review the following.

1. DoubleTypeHandler Class Declare anotation.
-----------------------------------------------------------------------------------------
@MappedTypes(?) <--- missing java data type.
@MappedJdbcTypes(JdbcType.DOUBLE)
public class DoubleTypeHandler extends BaseTypeHandler<Double> {
...
}
-----------------------------------------------------------------------------------------
ex)
@MappedTypes(value=String.class)
@MappedJdbcTypes(value={jdbcType.CLOB})
-----------------------------------------------------------------------------------------


2. TestRowMapper.xml XML File
-----------------------------------------------------------------------------------------
  <resultMap id="BaseResultMap" type="com.extrategic.dashboard.db.row.TestRow">
    <!--
      WARNING - @mbggenerated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Tue Feb 18 21:34:20 PST 2014.
    -->
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="double_data" jdbcType="DOUBLE" property="doubleData" /> <--- Declare TypyeHandler
    <result column="date_data" jdbcType="DATE" property="dateData" />
    <result column="date_time_data" jdbcType="TIMESTAMP" property="dateTimeData" />
  </resultMap>
-----------------------------------------------------------------------------------------
ex) 
<result column="double_data" jdbcType="DOUBLE" property="doubleData" typeHandler="" />
typeHandler="???" <--- Custom TypeHandler full package path or TypeAlias name
-----------------------------------------------------------------------------------------


I hope you have good results.
Bye.


==================================
IkChan SIM
Software Architeture.
H.P : 010 - 8242 - 2727
Nothing in the world can take the place of persistence.
==================================
chapter5_typehandler_part20.pdf

Jeff Butler

unread,
Feb 20, 2014, 9:35:28 AM2/20/14
to mybati...@googlegroups.com
I would say this is expected, but not completely intuitive behavior.  Type Handling in MyBatis can be very strange.

The basic idea is that TypeHandlers are picked first by the Java type.  The JDBC type is only really used when a single Java type could be mapped to more than one JDBC type (mainly String or Date).

In your case, with a Double, the Java type is more important than the JDBC type.

Jeff Butler

Melvin Lim

unread,
Feb 20, 2014, 11:20:13 PM2/20/14
to mybati...@googlegroups.com
Thanks for the clarification, Jeff.

Even stranger, I found out that I could use the same method to override the TypeHandler for JdbcType.TIMESTAMP by using @MappedTypes(value=Date.class), but the same will NOT work with JdbcType.Date and JdbcType.Time because the Criteria code casts these types to java.sql.Date and java.sql.Time!

Anyway, I'll just confine myself with TIMESTAMP for now since I need to work with fractional seconds.

Thanks,

Melvin
Reply all
Reply to author
Forward
0 new messages