上一博客主要是對單表的增刪改查,比較簡單,而且每個屬性與table表的列都是一一對應名字也一樣,今天主要學習屬性與table表列名不一致的處理,主要有兩種一是屬性與列名不一致,二是枚舉的情況,這里暫時考慮的屬性與列名不一致只是單表的情況,至於屬性如果是其他model涉及表與表之間的關系的放在下一博客。不過先介紹幾個其他的知識點。這些都是參考官網http://www.mybatis.org/mybatis-3/zh/index.html,大家也可以直接參考官網的。本篇博客還是在上一篇博客的基礎上做的修改。
一、Properties
上一博客創建了一個DBConfig.xml,因為在上一博客只是做了關於數據庫的配置,其實它不僅僅可以配置數據庫的屬性,還可以配置其他的好多屬性,比如properties。所以從這篇開始把DBConfig.xml的名字改為了Config.xml。這里為了使用properties,先建了一properties文件:config.properties.
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis
在Config.xml的configuration節點增加如下配置:
<properties resource="config.properties"> <property name="username" value="root"/> <property name="password" value="123456"/> </properties>
如果再配置數據庫連接的話就可以使用properties。

<environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <!-- 配置數據庫連接信息 --> <dataSource type="POOLED"> <property name="driver" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </dataSource> </environment> </environments>
還有就是Properties在configuration節點設置的位置的問題,可以按照下圖的順序設置各個屬性節點,因為在這個地方我也踩到了坑,就是在使用typeAlias的時候,我把typeAlias放在了mappers下面,然后就報錯了。
二、typeAlias
alias別名,這個在sql種也經常用到,在上一博客種我們在UserMapper.xml種如果要參數類型或返回值類型時都會這樣Cuiyw.MyBatis.Model.User寫上User的全稱,其實我們可以使用typeAlias來簡化它來減少冗余。這樣在用到Cuiyw.MyBatis.Model.User的地方都可以用別名User代替。
<typeAliases> <typeAlias type="Cuiyw.MyBatis.Model.User" alias="User"/> </typeAliases>
如果還覺得麻煩,可以直接指定包名就可以了。
<typeAliases> <package name="Cuiyw.MyBatis.Model"/> <!-- <typeAlias type="Cuiyw.MyBatis.Model.User" alias="User"/> --> </typeAliases>
三、屬性與列名映射
這是今天的主題,主要是兩個內容,一是枚舉類型映射,二是屬性名與列名不一致怎么映射。
1.自帶枚舉
如果想使用mybatis自帶的枚舉類處理,有2種方式,一個是EnumTypeHandler,一個是EnumOrdinalTypeHandler。2者的區別是EnumTypeHandler直接存儲name值,而EnumOrdinalTypeHandler會存儲enum類里的序號值,此時數據庫表字段一般用int類型的處理。
<insert id="addUser" parameterType="User"> <selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id"> SELECT LAST_INSERT_ID() </selectKey> insert into user(name,age,status) values (#{name},#{age},#{status,typeHandler=org.apache.ibatis.type.EnumTypeHandler}) </insert>
<insert id="addUser" parameterType="User"> <selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id"> SELECT LAST_INSERT_ID() </selectKey> insert into user(name,age,status) values (#{name},#{age},#{status,typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler}) </insert>
上面就是就是使用兩種方式實現的新增,下面截圖是兩種方式在數據庫的存儲情況。
2.resultMap
上面在sql中的status參數中配置,但對於select的操作沒有參數那該怎么辦呢?於是resultMap出現了。其實它的作用還有好多,今天主要用它做個簡單的例子,同時也演示列名和屬性名不一致的情況。雖然先只考慮單表的情況,有時候數據庫表的字段名與類的屬性名也可能不是一一對應的,這種怎么解決呢?我們可以使用resultMap,用它來做關系映射,這樣以后在用到的地方也只需要在select中增加屬性resultMap,引用的它id就好也特別方便。這里要注意就是在resultMap設置的typeHandler與在insert中設置的要一致。
<resultMap type="User" id="userResult"> <result column="id" property="id"/> <result column="name" property="name"/> <result column="age" property="age"/> <result column="status" property="status" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> </resultMap>
<select id="getUser" parameterType="int" resultType="User" resultMap="userResult"> select * from user where id=#{id} </select>
還是使用昨天的代碼,先增加一個,然后把增加的通過id查詢出來。
3.自定義枚舉
有時候mybatis自帶的枚舉並不能滿足需求,那我們也可以自定義枚舉。MyBatis提供了org.apache.ibatis.type.BaseTypeHandler類用於我們自己擴展類型轉換器,上面的EnumTypeHandler和EnumOrdinalTypeHandler也都實現了這個接口。
User類

package Cuiyw.MyBatis.Model; public enum UserState { DISABLED(0), AVAILABLE(1); private int status; UserState(int status) { this.status=status; } public static UserState fromValue(int value) { for(UserState userState:UserState.values()) { if(userState.status==value) { return userState; } } throw new IllegalArgumentException("Cannot create evalue from value: " + value + "!"); } public int getStatus() { return status; } }
EnumStatusHandler自定義枚舉類 這里采用的EnumOrdinalTypeHandler模式,保存數字。在用的時候直接引用就好了。

package Cuiyw.MyBatis.Model; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; public class EnumStatusHandler extends BaseTypeHandler<UserState> { @Override public void setNonNullParameter(PreparedStatement ps, int i, UserState parameter, JdbcType jdbcType) throws SQLException { // TODO Auto-generated method stub ps.setInt(i, parameter.getStatus()); } @Override public UserState getNullableResult(ResultSet rs, String columnName) throws SQLException { // TODO Auto-generated method stub return UserState.fromValue(rs.getInt(columnName)); } @Override public UserState getNullableResult(ResultSet rs, int columnIndex) throws SQLException { // TODO Auto-generated method stub return UserState.fromValue(rs.getInt(columnIndex)); } @Override public UserState getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { // TODO Auto-generated method stub return UserState.fromValue(cs.getInt(columnIndex)); } }
<resultMap type="User" id="userResult"> <result column="id" property="id"/> <result column="name" property="name"/> <result column="age" property="age"/> <result column="status" property="status" typeHandler="Cuiyw.MyBatis.Model.EnumStatusHandler"/> </resultMap>
4.自定義枚舉優化
上面是針對每個枚舉類型創建一個TypeHandler,那如果多的話豈不是很麻煩,那該怎么辦呢?有什么優化的方法沒?答案當然是有的啦。有單獨的一個轉到能應用多個,那就會聯想的泛型。
1.定義接口
package Cuiyw.MyBatis.Model; public interface ValuedEnum { int getValue(); }
2.枚舉實現接口

package Cuiyw.MyBatis.Model; public enum UserState implements ValuedEnum { DISABLED(0), AVAILABLE(1); private int status; UserState(int status) { this.status=status; } // public static UserState fromValue(int value) // { // for(UserState userState:UserState.values()) // { // if(userState.status==value) // { // return userState; // } // } // throw new IllegalArgumentException("Cannot create evalue from value: " + value + "!"); // } // // public int getStatus() // { // return status; // } public int getValue() { // TODO Auto-generated method stub return status; } }
3.定義EnumTypeHandler
在ValuedEnumTypeHandler構造函數中使用了getEnumConstants()方法,它以聲明順序返回一個數組,該數組包含構成此 class 對象所表示的枚舉類的值,或者在此 class 對象不表示枚舉類型時返回 null,這樣就可以把枚舉值與數字值對應起來放在map中。

package Cuiyw.MyBatis.Model; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; public class ValuedEnumTypeHandler <E extends Enum<E>> extends BaseTypeHandler<E> { private Class<E> type; private Map<Integer, E> map = new HashMap<Integer, E>(); public ValuedEnumTypeHandler(Class<E> type) { if (type == null) { throw new IllegalArgumentException("Type argument cannot be null"); } this.type = type; E[] enums = type.getEnumConstants(); if (enums == null) { throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type."); } for (E e : enums) { ValuedEnum valuedEnum = (ValuedEnum) e; map.put(valuedEnum.getValue(), e); } } @Override public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { // TODO Auto-generated method stub ValuedEnum valuedEnum = (ValuedEnum) parameter; ps.setInt(i, valuedEnum.getValue()); } @Override public E getNullableResult(ResultSet rs, String columnName) throws SQLException { int i = rs.getInt(columnName); if (rs.wasNull()) { return null; } else { return getValuedEnum(i); } } @Override public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { int i = rs.getInt(columnIndex); if (rs.wasNull()) { return null; } else { return getValuedEnum(i); } } @Override public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { int i = cs.getInt(columnIndex); if (cs.wasNull()) { return null; } else { return getValuedEnum(i); } } private E getValuedEnum(int value) { try { return map.get(value); } catch (Exception ex) { throw new IllegalArgumentException( "Cannot convert " + value + " to " + type.getSimpleName() + " by value.", ex); } } }
4.使用
<resultMap type="User" id="userResult"> <result column="id" property="id"/> <result column="name" property="name"/> <result column="age" property="age"/> <result column="status" property="status" typeHandler="Cuiyw.MyBatis.Model.ValuedEnumTypeHandler"/> </resultMap>