2015年8月22日 星期六

TypeHandler(類型處理器) Mybatis3.x(十)

TypeHandler就是將型態轉成另一種型態

※事前規劃

CREATE TABLE TEST_BOOLEAN(
    ID NUMBER,
    NAME VARCHAR(10),
    BOO NUMBER,
    CONSTRAINT TEST_BOOLEAN_PK PRIMARY KEY(ID)
);
COMMENT ON TABLE TEST_BOOLEAN IS '測試布林表';
COMMENT ON COLUMN TEST_BOOLEAN.ID IS 'id';
COMMENT ON COLUMN TEST_BOOLEAN.NAME IS '名字';
COMMENT ON COLUMN TEST_BOOLEAN.BOO IS '0表示false';

INSERT INTO TEST_BOOLEAN(ID, NAME, BOO)VALUES(11, 'tom', 0);
INSERT INTO TEST_BOOLEAN(ID, NAME, BOO)VALUES(22, 'jerry', 1);
INSERT INTO TEST_BOOLEAN(ID, NAME, BOO)VALUES(33, 'apple', 2);
INSERT INTO TEST_BOOLEAN(ID, NAME, BOO)VALUES(44, 'banana', 1);
INSERT INTO TEST_BOOLEAN(ID, NAME, BOO)VALUES(55, 'guava', 0);

尤於資料庫沒有布林值,所以用1和0代表true和false
官方的說法有兩種方法,實作TypeHandler介面和繼承BaseTypeHandler

※TypeHandler

先寫一隻實作這介面並覆寫方法
getResult:將資料庫的數字轉成java的boolean
setParameter:將java的boolean轉成資料庫的數字
@MappedTypes({Boolean.class})
@MappedJdbcTypes({JdbcType.INTEGER})
public class BooleanIntegerTransfor implements TypeHandler<Boolean> {
    @Override
    public Boolean getResult(ResultSet rs, String columnName)
            throws SQLException {
        System.out.println("columnName=" + columnName);
        System.out.println(rs.getString(columnName));
        
        return rs.getShort(columnName) != 0;
    }

    @Override
    public Boolean getResult(ResultSet rs, int columnIndex) throws SQLException {
        System.out.println("rs columnIndex=" + columnIndex);
        System.out.println(rs.getString(columnIndex));
        return rs.getInt(columnIndex) != 0;
    }

    @Override
    public Boolean getResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        System.out.println("cs columnIndex=" + columnIndex);
        System.out.println(cs.getString(columnIndex));
        return cs.getInt(columnIndex) != 0;
    }

    @Override
    public void setParameter(PreparedStatement ps, int i, Boolean parameter,
            JdbcType jdbcType) throws SQLException {
        System.out.println("i=" + i);
        System.out.println("parameter=" + parameter);
        if (parameter == null) {
            ps.setNull(i, java.sql.Types.NULL);
        } else {
            if (parameter) {
                ps.setInt(i, 1);
            } else {
                ps.setInt(i, 0);
            }
        }
    }
}

vo只要注意boo是要轉換的類型,所以雖然資料庫是number,但在java還是寫boolean,然後setter/getter

設定檔的部分,看要偵對整個package或剛剛實作的java,javaType和jdbcType可用註解的方式,如實作的類上面那兩行,取其一種方式:
<typeHandlers>
<!--     <package name="org.mybatis.model" /> -->
    <typeHandler handler="org.mybatis.model.BooleanIntegerTransfor" javaType="java.lang.Boolean" jdbcType="INTEGER" />
</typeHandlers>

vo的xml要注意resultMap的javaType和jdbcType要打才會執行實作的類,而insert也要打,才能呼叫到setParameter方法:
<mapper namespace="a.b.c">
    <sql id="column">
        ID, NAME, BOO
    </sql>

    <resultMap type="TestBoolean" id="TestBooleanInterface">
        <id property="id" column="ID" />
        <result property="name" column="NAME" />
        <result property="boo" column="BOO" javaType="java.lang.Boolean" jdbcType="INTEGER" />
    </resultMap>

    <insert id="insert" parameterType="TestBoolean">
        INSERT INTO TEST_BOOLEAN (
            <include refid="column" />
        ) VALUES (
             #{id}, #{name}, #{boo, javaType=java.lang.Boolean, jdbcType=INTEGER}
        )
    </insert>

    <select id="findAll" resultMap="TestBooleanInterface">
        SELECT <include refid="column" /> FROM TEST_BOOLEAN
    </select>
</mapper>

測試類:
SqlSession sqlSession = MyBatisSessionFactory.getSession();
List<TestBoolean> all = sqlSession.selectList("a.b.c.findAll");
for (TestBoolean tb : all) {
    System.out.println("a=" + tb.getId());
    System.out.println("b=" + tb.getName());
    System.out.println("c=" + tb.getBoo());
}

TestBoolean tb = new TestBoolean();
tb.setId(78);
tb.setName("brush");
tb.setBoo(true);
System.out.println(sqlSession.insert("a.b.c.insert", tb));

sqlSession.commit();
MyBatisSessionFactory.closeSession();

心得:
.還有兩個getResult方法,不知怎麼呼叫
.javaType、jdbcType和@MappedTypes、@MappedJdbcTypes這兩組不打或兩組都打,我試也是ok的,可能有它的原理,但我覺得這樣不直覺
.只要打一組,大家都看得懂,好維護。 不打或全打嘛!搞不好哪天mybatis新版就不能使用了也說不定。
.我的這篇文章,講Annotation的,裡面有個@Target,1.8有新增一個叫TYPE_USE,理論上應該哪裡都能使用才對,但我測出來的不是這樣,所以才有那一張表; 但我覺得哪一天版本更新就都可以了也說不定,所以不用太過研究這種不好的設計
※BaseTypeHandler
@MappedTypes({ Boolean.class })
@MappedJdbcTypes({ JdbcType.INTEGER })
public class BooleanIntegerTransfor extends BaseTypeHandler<Boolean> {
    @Override
    public Boolean getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        return rs.getInt(columnName) != 0;
    }

    @Override
    public Boolean getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        return rs.getInt(columnIndex) != 0;
    }

    @Override
    public Boolean getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        return cs.getInt(columnIndex) != 0;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
            Boolean parameter, JdbcType jdbcType) throws SQLException {
        System.out.println(parameter);
        if (parameter == true) {
            ps.setInt(i, 1);
        } else {
            ps.setInt(i, 0);
        }
    }
}


沒有留言:

張貼留言