MyBatis Type Handlers causes table insert incompatibiity

449 views Asked by At

It could be that I've misunderstood/misused MyBatis Type Handlers but I've found that they can cause an incompatibility problem related to table insert methods.

Consider classes:

public class TableA {
    int idA;
    String name;
    // constructors, getters, etc.
}

public class TableB {
    int idB;
    TableA objA;
    Date startDate;
    // constructors, getters, etc.
}

I declare the associated table, where for TableB, Id_A is a foreign key to a Table_A table row.:

create table Table_A ( int Id_A int auto_increment primary key, name varchar(50) );
create table Table_B ( int Id_B int auto_increment primary key, Id_A int, Start_Date datetime );

I declare a straightforward mapper for TableA, TableAMapper.java with a method:

@Insert({ "insert into Table_A ( Id_A, name ) values ( #{idA}, #{name} )" })
@Options(useGeneratedKeys=true, keyProperty="idA")
int insertReturnPK( TableA record );

For TableBMapper, a Type Handler is used to populate TableB.objA, and a similar insertReturnPK method with a Type Handler for the objA field:

<resultMap id="BaseResultMap" type="TableA">
    <id column="Id_B" jdbcType="INTEGER" property="idB" />
    <result column="Id_A" jdbcType="INTEGER" property="objA" typeHandler="TableATypeHandler" />
    <result column="Start_Date" jdbcType="TIMESTAMP" property="startDate" />
</resultMap>

@Insert({ "insert into Table_B ( Id_A, Id_B, Start_Date )",
    "values ( #{idB},",
    "#{objA,typeHandler=TableATypeHandler}, #{startDate} )" })
@Options(useGeneratedKeys=true, keyProperty="idA")
int insertReturnPK( TableA record );

TableA Type Handler looks like this:

public class TableATypeHandler extends extends BaseTypeHandler<TableA> {
    ...

    @Override
    public void setNonNullParameter( PreparedStatement ps, int colIdx, TableA parameter, JdbcType jdbcType ) throws SQLException {
        // code to set the specified column indexed by colIdx in Table_A
    }
}

Finally the problem! TableA.insertReturnPK and TableB.insertReturnPK are incompatible. Or more specifically, they call TableATypeHandler.setNonNullParameter for two very different purpose:

  1. TableBMapper.insertReturnPK calls it with colIdx=2 to set column 2 to Table_B with the primary key of Table_A, i.e. Table_A.Id_A (an Integer)
  2. MyBatis implicitly (wish it didn't!) uses TableATypeHandler in the TableBMapper.insertReturnPK call to populate Table_A's columns. In other words, when it calls TableATypeHandler.setNonNullParameter with colIdx=2 it wants to set the prepare statement with a tableA.name value (a String). Furthermore, in this context, setNonNullParameter is called for each column in Table_A, whereas in 1. is is only called once to get Table_A's primary key.

The problem could easily have been avoided either by MyBatis not using TableATypeHandler implicitly in 2. Or alter setNonNullParameter to have 2 column indexes, i.e. one that pertains to ps (target column index) and one that peratains to parameter (source column index).

Anyone come across this? And is there a workaround?

0

There are 0 answers