Mybatis新增同時返回主鍵功能的兩種方式

Mybatis新增並返回主鍵功能


本人使用的是Mybatis3.X的版本

官方文檔給出了兩種方式實現:

  1. useGeneratedKeys:(僅對 insert 和 update 有用)這會令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數據庫內部生成的主鍵(比如:像 MySQL 和 SQL Server 這樣的關係數據庫管理系統的自動遞增字段),默認值:false。
    如果你的數據庫支持自動生成主鍵的字段(比如 MySQL 和 SQL Server),那麼你可以設置 useGeneratedKeys=”true”,然後再把 keyProperty 設置到目標屬性上就OK了。
  2. 如果你的數據庫不支持自動生成主鍵的字段。你可以使用子標籤<selectKey>來實現。

示例

省略了實體類和mapper接口層的代碼,只給出xml文件的代碼作爲示例

<mapper namespace="com.mirt.mybatis_demo.mapper.AutoIncrementMapper">

    <resultMap id="BaseResult" type="com.mirt.mybatis_demo.entity.AutoIncrementEntity">
        <result column="id" property="id"/>
        <result column="comment" property="comment"/>
        <result column="create_time" property="createTime"/>
    </resultMap>

    <insert id="saveOneByUseGeneratedKeys" useGeneratedKeys="true" keyProperty="id">
        insert into auto_increment_table (comment, create_time) values (#{comment},now());
    </insert>

    <insert id="saveOneBySelectKey" parameterType="com.mirt.mybatis_demo.entity.AutoIncrementEntity">
        <selectKey resultType="int" order="AFTER" keyProperty="id">
            SELECT LAST_INSERT_ID();
        </selectKey>
        insert into auto_increment_table (comment, create_time) values (#{comment},now());
    </insert>
</mapper>

此處定義了兩種方法 saveOneByUseGeneratedKeys,saveOneBySelectKey
用於演示官方給出的兩種方法是否可行。

給出測試類下的方法以及結果

	@Test
    public void saveOneByUseGeneratedKeys() {
        AutoIncrementEntity aie = new AutoIncrementEntity();
        aie.setComment("useGeneratedKeys");
        boolean res = autoIncrementMapper.saveOneByUseGeneratedKeys(aie) > 0;
        if (res) {
            System.out.println(aie);
        }else {
            System.out.println("insert fail");
        }
    }

    @Test
    public void saveOneBySelectKey() {
        AutoIncrementEntity aie = new AutoIncrementEntity();
        aie.setComment("selectKey");
        boolean res = autoIncrementMapper.saveOneByUseGeneratedKeys(aie) > 0;
        if (res) {
            System.out.println(aie);
        }else {
            System.out.println("insert fail");
        }
    }

返回結果爲

{id:1}
{id:2}

可以看出來確實返回了自增的主鍵。


源碼

package org.apache.ibatis.executor.keygen;

import java.sql.Statement;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;

public interface KeyGenerator {
    void processBefore(Executor var1, MappedStatement var2, Statement var3, Object var4);

    void processAfter(Executor var1, MappedStatement var2, Statement var3, Object var4);
}

mybatis的KeyGenerator接口提供了兩個方法,一個是執行前一個是執行後,在上面的示例中由於主鍵是在插入後才獲得的,所以使用的都是processAfter這個方法。

該接口有三個實現類,默認使用的是Jdbc3KeyGenerator這個實現類。

	public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
        this.processBatch(ms, stmt, this.getParameters(parameter));
    }

    public void processBatch(MappedStatement ms, Statement stmt, Collection<Object> parameters) {
        ResultSet rs = null;

        try {
            rs = stmt.getGeneratedKeys();
            Configuration configuration = ms.getConfiguration();
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            String[] keyProperties = ms.getKeyProperties(); // 可以配置多個keyproperties
            ResultSetMetaData rsmd = rs.getMetaData();
            TypeHandler<?>[] typeHandlers = null;
            MetaObject metaParam;
            if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {
                for(Iterator var10 = parameters.iterator(); var10.hasNext(); this.populateKeys(rs, metaParam, keyProperties, typeHandlers)) {
                    Object parameter = var10.next();
                    if (!rs.next()) {
                        break;
                    }

                    metaParam = configuration.newMetaObject(parameter);
                    if (typeHandlers == null) {
                        typeHandlers = this.getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);
                    }
                }
            }
        } catch (Exception var20) {
            throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + var20, var20);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (Exception var19) {
                    ;
                }
            }

        }

    }
    
    // 將獲得的值存入到實體類指定的字段
  	private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
        for(int i = 0; i < keyProperties.length; ++i) {
            String property = keyProperties[i];
            TypeHandler<?> th = typeHandlers[i];
            if (th != null) {
                Object value = th.getResult(rs, i + 1);
                metaParam.setValue(property, value);
            }
        }

    }

mybatis的官方文檔中對於keyPropwety有如下描述

(僅對 insert 和 update 有用)唯一標記一個屬性,MyBatis 會通過 getGeneratedKeys 的返回值或者通過 insert 語句的 selectKey 子元素設置它的鍵值,默認:unset。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。

從源碼中瞭解到keyProperty也確實可以支持設置多個,統一在插入操作前後統一存入。

有一點需要注意就是如果你的主鍵沒有setter方法的話,這裏是會報錯的。mybatis沒有辦法把生成的主鍵信息存回到實體類當中去。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章