MyBatis之基於XML的屬性與列名映射


上一博客主要是對單表的增刪改查,比較簡單,而且每個屬性與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>
View Code

還有就是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;
    }
}
View Code

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));
    }

}
View Code
    <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;
    }
}
View Code

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);
        }
    }

}
View Code

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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM