1. 程式人生 > >避坑必看:很詳盡的MyBatis返回自增主鍵實驗(包括插入或更新SQL語句insert on duplicate key update的自增主鍵返回情況)

避坑必看:很詳盡的MyBatis返回自增主鍵實驗(包括插入或更新SQL語句insert on duplicate key update的自增主鍵返回情況)

目錄

(7)介面

5. 總結

本篇文章對MyBatis操作MySQL時自增主鍵返回情況進行詳細的實驗,給出不同情況下Mybatis返回自增主鍵的不同行為,僅基於實驗結果,不做原始碼分析。

1. 實驗對比維度

(1)單純的insert和insert on duplicate key update

這也是本文最大的特點,查詢網上各種闡述Mybatis返回主鍵的文章,基本只關注insert時Mybatis返回主鍵的情況,對於插入或更新的sql語句insert on duplicate key update時mybatis返回主鍵(此時還細分為僅insert,僅update和insert和update混合三種情況)的文章則比較少。

(2)selectKey和useGeneratedKeys

Mybatis用這兩個方法都能返回主鍵,前者一般用於單個插入,後者一般用於批量插入。本文還會給出兩者更加細緻的實驗區別。

(3)@Param和parameterType

Mybatis傳參時可以用@Param註解或者在xml中用parmaterType引用java物件,本文會實驗兩者傳參對返回主鍵的影響。

(4)單個和批量

單個插入、批量插入、單個插入或更新和批量插入或更新時Mybatis返回主鍵情況各不相同,本文針對此進行實驗分析。

(5)keyProperty寫法

這個維度和@Param、parameterType緊密結合。不管採用selectKey還是useGeneratedKeys,返回主鍵時都需要用keyProperty指定主鍵對應的Java物件屬性名,以便將主鍵設定到Java物件上(達到返回主鍵的目的)。而@Param和parameterType影響入參名字,也就會影響keyProperty對應的屬性名寫法,進一步會影響到返回主鍵的行為。

2. 基本概念介紹

對本文感興趣的人一般已經熟悉MySQL和Mybatis,在工程中也有大量應用,但是為了幫助讀者更順暢的閱讀,筆者還是介紹下本文涉及的相關基本概念。

(1)插入或更新SQL(簡稱InsertOrUpdate)

當我們往資料庫插入記錄時,如果資料庫原先不存在該記錄,那麼就正常插入(此時就是insert);如果資料庫原先存在該記錄,那麼就更新此記錄(此時就是update),用一條SQL語句完成上述要求就是所謂的InsertOrUpdate。

MySQL判斷記錄是否存在的依據是主鍵或者唯一索引,insert在主鍵或者唯一索引已經存在的情況下會插入失敗,而InsertOrUpdate在主鍵或者唯一索引已經存在的情況下就變成了根據主鍵或唯一索引update的操作。

有人會問原先有多個記錄同時與插入的記錄相同,會發生什麼?答案是MySQL會更新第一個匹配的記錄,其餘的則不更新(網上看到的,筆者沒有實驗過),本質上這是由於單表中有多個唯一索引並存,所以有人會推薦一張表最好只建一個唯一索引。

MySQL實現InsertOrUpdate的語句有兩種:一種就是本文要實驗的Insert into values on duplicate key update語句。另一種是replace into values語句。insert on duplicate key update在發現記錄已經存在時就地更新,或者說和update行為一致。replace into在發現記錄已經存在時,先把原先的記錄刪除,然後再插入新的記錄,相當於delete+insert操作。兩者具體的細節可以參考下其他文章。

這裡說明下為什麼選取insert on duplicate key on而非replace into做實驗:目前後端開發的資料表設計,比較流行用自增主鍵而不是自己選定欄位做主鍵(這樣做有諸多好處,可以參考MySQL的底層資料結構),並且也不依賴資料庫提供的外來鍵功能,而是在程式邏輯中保證資料一致性關係。這樣一來replace into的功能就非常坑,它在記錄已經存在時會改變主鍵,帶來資料不一致的風險。實戰中筆者建議除非特殊需求否則不要用replace into,即便當下沒問題,將來擴充套件起來也容易埋坑。即便insert on duplicate key能真正做到Insert和Update一體,筆者基本也只用它來做批量更新用,不會使用其不存在插入、存在則更新的特性——寧願在程式邏輯裡先查一遍,區分需要insert和需要update的記錄,該insert的insert,該update的update——因為MySQL的語言和Java等命令型語言不一樣,它是有直譯器的,開發者輸入的SQL語句具體怎麼執行不是由開發者決定的而是由MySQL決定的,所以功能越多的、語句越複雜的SQL執行的結果越不可控,也很難除錯和維護。比如InsertOrUpdate在遇到多個相同的記錄時只會更新第一個相同的記錄就是不可控性的表現,非常坑,但是如果在應用程式中處理這些邏輯就很方便、很確定。

無論是insert on duplicate key update還是replace into,在插入時MySQL返回的影響行數就是插入的記錄數,但是在更新時返回的影響行數時更新行數*2。所以筆者只會在批量更新時放心的使用insert on duplicate key update,這樣根據返回值是否是引數大小的2倍就能判斷是否只進行了更新操作。

筆者推崇開發中把資料庫當作單純的資料儲存服務,減少其業務邏輯的負擔,返璞歸真。資料庫語句應該儘量單表、單crud操作,不要動則幾十上百行SQL,這樣也符合當前微服務的趨勢。筆者剛入行時經常聽聞某某大神把幾分鐘的SQL優化到幾秒,非常佩服,後來才發現本質上這是個比爛大賽,開發人員偷懶用重SQL語句彌補開發效率,一個聯表查詢就能從地球聯到火星,留下了一大堆低效、無擴充套件性、難閱讀、無從除錯的程式碼,只能藉助於SQL大神擦屁股,這種屁股擦起來其實對人也沒啥長進(求職時看到職責描述有“能書寫複雜SQL或者優化複雜SQL”的,親們最好留個心眼...)。還是要做好系統架構、引導好團隊的開發習慣、合理設計表和索引、細緻負責的規劃梳理業務,避免出現SQL大神才好。

題外話說的太多了,既然本文是Mybatis返回主鍵的實驗,replace into這種會改變主鍵而且實戰中也不建議使用的語句當然不予考慮。

(2)selectKey和useGeneratedKeys的異同

關於兩者介紹的文章也很多。概括來說selectKey用於單個記錄返回主鍵,useGeneratedKeys單記錄多記錄都能返回主鍵。所以單記錄插入時實驗會對比selectKey和useGeneratedKeys,而批量插入時只會採用useGeneratedKeys。

另外useGeneratedKeys=true需要資料庫的支援,mysql可以使用,但是oracle不支援。selectKey實用的last_insert_id()的適應範圍更廣。

這裡根據後面的實驗先提前下個結論:在資料庫相容的情況下,返回主鍵的方式用useGeneratedKeys是最佳實踐,selectKey在某些情況下(單記錄)不會返回主鍵。

(3)@Param和parameterType的異同

基本異同也請參考其他文章。簡單來說有個資料庫對映實體Boy,其屬性id對映資料庫自增主鍵。如果dao中入參用@Param("entity")標註,那麼在Mybatis的對映xml檔案裡,引用boy.id要寫成#{entity.id}。如果在對映xml檔案用parameterType="全路徑名.Boy",那麼引用boy.id只用寫成#{id}。

@Param在替換dao的入參實體時可能免去修改對映檔案工作,而且多引數情況下只能用@Param。parameterType會讓對映檔案中引用物件屬性寫法變得簡單,但是隻能適應單引數情況。根據後面的實驗結果,這裡提前給結論:在需要返回主鍵場景(插入、批量插入、插入或更新,批量插入或更新)裡,parameterType比@Param適應性更好,@param在某些場景下不會返回主鍵。

3. 實驗工程

(1)實驗環境

只要mysql和mybatis的版本不至於太低都能復現。具體引數如下:

OS:mac

MySQL:5.7.20

SpringBoot:1.5.9

MyBatis:3.4.6

(2)資料表

一張boy表,自增主鍵id,name(varchar)唯一索引,created_time和modified_time記錄建立和修改時間。

CREATE TABLE `boy` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL,
  `created_time` timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '記錄建立時間',
  `modified_time` timestamp NULL DEFAULT NULL COMMENT '記錄修改時間',
  PRIMARY KEY (`id`),
  UNIQUE `unq_name` USING BTREE (`name`) comment ''
)

(3)資料庫實體

就一個實體 BoyEntity

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BoyEntity {

     private Long id;
     private String name;
     private Date createdTime;
     private Date modifiedTime;
}

(4)Dao和MyBatis對映檔案

public interface BoyDao {

    int insertSelectKeyParam(@Param("entity") BoyEntity entity);

    int insertUseGeneratedKeysParam(@Param("entity") BoyEntity entity);

    int insertSelectKey(BoyEntity entity);

    int insertUseGeneratedKeys(BoyEntity entity);

    int insertBatchParam(@Param("entityList") List<BoyEntity> entityList);

    int insertBatch(List<BoyEntity> entityList);

    int insertOrUpdateSelectKeyParam(@Param("entity") BoyEntity entity);

    int insertOrUpdateUseGeneratedKeysParam(@Param("entity") BoyEntity entity);

    int insertOrUpdateSelectKey(BoyEntity entity);

    int insertOrUpdateUseGeneratedKeys(BoyEntity entity);

    int insertOrUpdateBatchParam(@Param("entityList") List<BoyEntity> entityList);

    int insertOrUpdateBatch(List<BoyEntity> entityList);

}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.jxshen.mybatis.test.dao.BoyDao">
    <resultMap id="BaseResultMap" type="com.jxshen.mybatis.test.entity.BoyEntity">
    </resultMap>

    <sql id="foreachSql">
        <foreach collection="itemList" item="item" separator="," open="(" close=")">
            #{item}
        </foreach>
    </sql>

    <sql id="insertForeachEntitySql">
        <foreach collection="entityList" item="entity" index= "index" separator =",">
            <include refid="insertEntitySql" />
        </foreach>
    </sql>

    <sql id="insertForeachSql">
        <foreach collection="list" item="entity" index= "index" separator =",">
            <include refid="insertEntitySql" />
        </foreach>
    </sql>

    <sql id="insertTableSql">
        (
            `name`,
            created_time,
            modified_time
        )
    </sql>

    <sql id="insertEntitySql">
        (
            #{entity.name},
            now(),
            now()
        )
    </sql>

    <sql id="insertSql">
        (
            #{name},
            now(),
            now()
        )
    </sql>

    <sql id="onDuplicateKeyUpdate">
        modified_time = now()
    </sql>

    <insert id="insertSelectKeyParam">
        <selectKey resultType="java.lang.Long" keyProperty="entity.id" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into boy <include refid="insertTableSql"/>
        values <include refid="insertEntitySql"/>
    </insert>

    <insert id="insertUseGeneratedKeysParam" useGeneratedKeys="true" keyProperty="entity.id">
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertEntitySql" />
    </insert>

    <insert id="insertSelectKey" parameterType="com.jxshen.mybatis.test.entity.BoyEntity">
        <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into boy <include refid="insertTableSql"/>
        values <include refid="insertSql"/>
    </insert>

    <insert id="insertUseGeneratedKeys" parameterType="com.jxshen.mybatis.test.entity.BoyEntity" useGeneratedKeys="true" keyProperty="id">
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertSql" />
    </insert>

    <insert id="insertBatchParam" useGeneratedKeys="true" keyProperty="entity.id">
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertForeachEntitySql" />
    </insert>

    <insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertForeachSql" />
    </insert>

    <insert id="insertOrUpdateSelectKeyParam">
        <selectKey resultType="java.lang.Long" keyProperty="entity.id" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertEntitySql" />
        on duplicate key update <include refid="onDuplicateKeyUpdate" />
    </insert>

    <insert id="insertOrUpdateUseGeneratedKeysParam" useGeneratedKeys="true" keyProperty="id">
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertEntitySql" />
        on duplicate key update <include refid="onDuplicateKeyUpdate" />
    </insert>

    <insert id="insertOrUpdateSelectKey" parameterType="com.jxshen.mybatis.test.entity.BoyEntity">
        <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertSql" />
        on duplicate key update <include refid="onDuplicateKeyUpdate" />
    </insert>

    <insert id="insertOrUpdateUseGeneratedKeys" parameterType="com.jxshen.mybatis.test.entity.BoyEntity" useGeneratedKeys="true" keyProperty="id">
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertSql" />
        on duplicate key update <include refid="onDuplicateKeyUpdate" />
    </insert>

    <insert id="insertOrUpdateBatchParam" useGeneratedKeys="true" keyProperty="id">
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertForeachEntitySql" />
        on duplicate key update <include refid="onDuplicateKeyUpdate" />
    </insert>

    <insert id="insertOrUpdateBatch" useGeneratedKeys="true" keyProperty="id">
        insert into boy <include refid="insertTableSql" />
        values <include refid="insertForeachSql" />
        on duplicate key update <include refid="onDuplicateKeyUpdate" />
    </insert>

</mapper>

12個SQL語句用來組合條件下返回主鍵的結果。函式的命名錶明瞭不同條件,命名規則從左到右為:

insert / insertOrUpdate:表示語句是插入還是插入或更新

batch:帶有batch的表示是批量操作,沒有batch是單個操作

selectKey / useGeneratedKeys:表示返回主鍵用的何種方式

param:帶有param表示是入參用@Param標註,否則在對映檔案中用parameterType

keyProperty:在入參用@Param標註時keyProperty有id和entity.id兩種寫法,測試在@Param標註傳參時不同keyProperty的寫法能否成功返回主鍵。但是keyProperty的不同取值在dao函式中未做區分,直接寫定在Mybatis對映檔案中。實驗時需要自己手動修改keyProperty來複現實驗結果。

基本上看函式名就能明白組合的條件,另外再補充幾點說明:

(4-1)@Param統一標註入參實體為entity(單個)或者entityList(批量)。

(4-2)為了複用,對映檔案裡用了很多<sql>,看對映語句時注意<sql>具體對應的片段,有些名字很相近的容易混淆。

(4-3)批量插入時如果用@Param標註入參,那麼批量插入的foreach語句裡collection引用的就是entityList,item為entity。如果批量插入用的parameterType入參,那麼批量插入時foreach的語句裡collection就是list物件,但是item仍取別名為entity。

(5)請求引數實體

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BoyInsertParam {

    private BoyEntity boy;
    private List<BoyEntity> boys;
    private String method;
}

method:代表需要呼叫的dao方法

boy:如果dao方法入參是單個實體,則賦值給boy欄位。boy.id必須為空,讓資料庫自增生成。

boys:如果 dao方法入參是批量實體,,則賦值給boys欄位。每個boy.id必須為空,讓資料庫自增生成

(6)響應實體

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BoyInsertRO {

    private BoyEntity boy;
    private List<BoyEntity> boys;
    private Integer num;
}

boy:如果是單個插入(插入或更新),dao操作後的BoyEntity賦值給boy欄位,從boy.id能看出是否成功返回主鍵,以及返回主鍵是否是對應記錄的。

boys:如果是批量插入(批量插入或更新),dao操作後List<BoyEntity>賦值給boys欄位,從每個boy.id能看出是否成功返回主鍵,以及返回主鍵是否是對應記錄的。

num:dao操作影響的行數。如果是插入,就是成功插入的數量,如果是更新,就是成功更新數量的兩倍。如果是插入或者更新混合,則取決於插入和更新的記錄數分別有多少。

(7)介面

@RestController
public class BoyController {

    @Autowired
    private BoyDao boyDao;

    @RequestMapping("/boy/insert")
    public BoyInsertRO insertOrUpdateBoy(@RequestBody BoyInsertParam param) throws InvocationTargetException, IllegalAccessException {
        int num = 0;

        for (Method method : boyDao.getClass().getDeclaredMethods()) {
            if (method.getName().equals(param.getMethod())) {
                method.setAccessible(true);
                if (method.getParameterTypes()[0].equals(BoyEntity.class)) {
                    num = (int) method.invoke(boyDao, param.getBoy());
                }
                else {
                    num = (int) method.invoke(boyDao, param.getBoys());
                }
            }
        }

        return BoyInsertRO.builder()
                .boy(param.getBoy())
                .boys(param.getBoys())
                .num(num)
                .build();
    }
}

採用json格式互動,介面中用反射找到具體要操作的dao函式。

4. 實驗結果

依次呼叫12個dao函式,在必要時變更keyProperty的取值,一共進行30組實驗。在最後彙總結果的表格裡,首行用英文縮寫表示不同的條件組合,現在說明如下:

sql:insert / insertOrUpdate。表示語句是insert into values還是insert into values on duplicate key update

actual aciton:insert / update / insertAndUpdate。對於insert sql的話只有insert一種情況;對於insertOrUpdate sql,可能資料是全部插入(insert)、全部更新(update)、部分插入部分更新(insertAndUpdate)。

key method:selectKey / useGeneratedKeys。Mybatis返回主鍵的方法。

entry param:param annotation / parameterType。入參形式。@Param標註入參(param annotation)或者parameterType指定引數型別(parameterType)。

isBatch:single / batch。是否批量操作。

keyProperty:id / entity.id。keyProperty的取值方式。在entry param為parameterType時只能取值id;在entry param為param annotation時可以為id或者entity.id。

return key:yes / no。返回的BoyEntity的id欄位上是否有主鍵值。

method name:對應的dao函式名字。

remark:額外說明。比如return key是否就是BoyEntity對應的資料庫記錄主鍵。

原始的boy表為空,沒有任何記錄,自增主鍵從1開始。

下面展示每組實驗結果(尤其是最後3組實驗的結果非常有意思):

(1)

條件:

sql actual action key method entry param isBatch keyProperty return key method name
insert insert selectKey param annotation single entity.id yes insertSelectKeyParam

用PostMan進行輸入和輸出:

資料庫:

結論:

MyBatis進行單個insert操作,採用selectKey返回主鍵的方式,用@Param標註入參,keyProperty="@Param的入參名.主鍵屬性名”,能夠成功返回正確的自增主鍵。

(2)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insert insert selectKey param annotation single id no insertSelectKeyParam

輸入輸出:

資料庫:

結論:

MyBatis進行單個insert操作,採用selectKey返回主鍵的方式,用@Param標註入參,keyProperty="主鍵屬性名”,不能返回自增主鍵。

(3)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insert insert useGeneratedKeys param annotation single entity.id yes insertUseGeneratedKeysParam

輸入輸出:

資料庫:

結論:

MyBatis進行單個insert操作,採用useGeneratedKeys返回主鍵的方式,用@Param標註入參,keyProperty="@Param的入參名.主鍵屬性名”,成功返回自增主鍵。

(4)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insert insert useGeneratedKeys param annotation single id no insertUseGeneratedKeysParam

輸入輸出:

資料庫:

結論:

MyBatis進行單個insert操作,採用useGeneratedKeys返回主鍵的方式,用@Param標註入參,keyProperty="主鍵屬性名”,不能返回自增主鍵。

(5)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insert insert selectKey parameterType single id yes insertSelectKey

輸入輸出:

資料庫:

結論:

MyBatis進行單個insert操作,採用selectKey返回主鍵的方式,用parameterType引用入參,keyProperty="主鍵屬性名”,成功返回自增主鍵。

(6)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insert insert useGeneratedKeys parameterType single id yes insertUseGeneratedKeys

輸入輸出:

資料庫:

結論:

MyBatis進行單個insert操作,採用useGeneratedKeys返回主鍵的方式,用parameterType引用入參,keyProperty=“主鍵屬性名”,成功返回自增主鍵。

(7)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insert insert useGeneratedKeys param annotation batch entity.id no insertBatchParam 批量插入用@Param註解入參是沒有辦法返回主鍵的

輸入輸出:

資料庫:

結論:

MyBatis進行批量insert操作,採用useGeneratedKeys返回主鍵的方式,用@Param標註入參,keyProperty=“@Param的入參名.主鍵屬性名”,無法自增主鍵。

(8)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insert insert useGeneratedKeys param annotation batch id no insertBatchParam 批量插入用@Param註解入參是沒有辦法返回主鍵的

輸入輸出:

資料庫:

結論:

MyBatis進行批量insert操作,採用useGeneratedKeys返回主鍵的方式,用@Param標註入參,keyProperty=“主鍵屬性名”,無法自增主鍵。結合(7)、(8)可以得出Mybatis在批量insert時,如果用@Param標註了入參,是無法返回主鍵的

(9)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insert insert useGeneratedKeys parameterType batch id yes insertBatch

輸入輸出:

資料庫:

結論:

MyBatis進行批量insert操作,採用useGeneratedKeys返回主鍵的方式,用parameterType引用入參,keyProperty=“主鍵屬性名”,成功返回自增主鍵。批量插入時為了返回主鍵必須用parameterType方式入參

(10)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insertOrUpdate insert selectKey param annotation single entity.id yes insertOrUpdateSelectKeyParam

輸入輸出:

資料庫:

結論:

MyBatis進行單個insertOrUpdate操作,實際為insert操作,採用selectKey返回主鍵的方式,用@Param標註入參,keyProperty=“@Param的入參名.主鍵屬性名”,成功返回自增主鍵。

(11)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insertOrUpdate update selectKey param annotation single entity.id yes insertOrUpdateSelectKeyParam 返回的主鍵值是該connection範圍內最近一次自增主鍵值,並不是更新記錄對應的主鍵值(因為更新操作沒有自增主鍵),如果connection上次呼叫last_insert_id()返回是null,則這個dao函式返回的主鍵是0

輸入輸出:

資料庫:

結論:

MyBatis進行單個insertOrUpdate操作,實際為update操作,採用selectKey返回主鍵的方式,用@Param標註入參,keyProperty=“@Param的入參名.主鍵屬性名”,返回自增主鍵。但是返回的主鍵值是上一次操作(10)曾經返回的主鍵值19。具體原因請參考selectKey使用select last_insert_id()來返回主鍵的原理(文章 :https://blog.csdn.net/slvher/article/details/42298355 有詳細說明)。所以insertOrUpdate函式在單個update操作時使用selectKey是無法正常返回主鍵的,除非用insert on duplicate key update id = last_insert_id(id)的辦法替代,通過update時強制設定更新記錄的id作為last_insert_id()函式的入參,然後將select last_insert_id()返回

(12)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insertOrUpdate insert selectKey param annotation single id no insertOrUpdateSelectKeyParam

輸入輸出:

資料庫:

結論:

MyBatis進行單個insertOrUpdate操作,實際為insert操作,採用selectKey返回主鍵的方式,用@Param標註入參,keyProperty=“主鍵屬性名”,無法返回自增主鍵。

(13)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insertOrUpdate update selectKey param annotation single id no insertOrUpdateSelectKeyParam

輸入輸出:

資料庫:

結論:

MyBatis進行單個insertOrUpdate操作,實際為update操作,採用selectKey返回主鍵的方式,用@Param標註入參,keyProperty=“主鍵屬性名”,無法返回自增主鍵。

(14)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insertOrUpdate insert useGeneratedKeys param annotation single entity.id yes insertOrUpdateUseGeneratedKeysParam

輸入輸出:

資料庫:

結論:

MyBatis進行單個insertOrUpdate操作,實際為insert操作,採用useGeneratedKeys返回主鍵的方式,用@Param標註入參,keyProperty=“@Param的入參名.主鍵屬性名”,成功返回自增主鍵。

(15)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insertOrUpdate update useGeneratedKeys param annotation single entity.id yes insertOrUpdateUseGeneratedKeysParam 返回的主鍵值就是更新記錄對應的主鍵值。所以單個記錄insertOrUpdate時useGeneratedKeys比selectKey更好的適用性

輸入輸出:

資料庫:

結論:

MyBatis進行單個insertOrUpdate操作,實際為update操作,採用useGeneratedKeys返回主鍵的方式,用@Param標註入參,keyProperty=“@Param的入參名.主鍵屬性名”,成功返回自增主鍵。所以單個InsertOrUpdate操作時,在返回主鍵的功能上useGeneratedKeys比selectKey有更廣泛的適應

(16)

條件:

sql actual action key method entry param isBatch keyProperty return key method name remark
insertOrUpdate insert useGeneratedKeys param annotation

相關推薦

詳盡MyBatis返回實驗包括插入更新SQL語句insert on duplicate key update返回情況

目錄 (7)介面 5. 總結 本篇文章對MyBatis操作MySQL時自增主鍵返回情況進行詳細的實驗,給出不同情況下Mybatis返回自增主鍵的不同行為,僅基於實驗結果,不做原始碼分

Mysql 插入資料存在時執行update操作ON DUPLICATE KEY UPDATE

-- 建立表:test: CREATE TABLE `test` (   `objId` int(10) NOT NULL,   `orgId` int(10) NOT NULL,   `objName` varchar(50) NOT NULL,   PRIMARY KE

mysqlon duplicate key update與replace into

在往表裡面插入資料的時候,經常需要:a.先判斷資料是否存在於庫裡面;b.不存在則插入;c.存在則更新 一、replace into   前提:資料庫裡面必須有主鍵或唯一索引,不然replace into 會直接插入新資料,導致資料表裡面有重複資料   執行時先嚐試插入資

MybatisON DUPLICATE KEY UPDATE與useGeneratedKeys混用陷阱

最近專案中遇到想要在唯一索引欄位相同的情況下更新表中資料,並且返回主鍵ID的場景,此時我的寫法是: <insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id

Spring cloud Jpa 的三種複合查詢實體類操作,sql語句裡操作

    今天剛好有個sql查詢,查出來的時候發現數據重複(可能說是覆蓋了更為準確些)了,條數是對的,再去debug一遍,發現是jpa自定義的實體類那裡出了問題,主鍵id只有一個,因為查出來的資料是複合主鍵的,所以只找到了實體類中定義的一個ID,可能就導致了資料覆蓋,但是條數是正確

記錄mysql中的case when|on duplicate key update|重複插入返回的用法

在平時的開發中不免接觸到資料庫,這裡記錄一些平時開發中遇到的細節問題,與大家共勉。 mysql中的條件控制:case函式 在操作資料庫的開發中不免遇到一些類似if else的判斷,這時候就用到了Case函式,首先我們用網上用了好多次的例子來看看它的用法:

mybatis中使用replace into和 insert INTO … ON DUPLICATE KEY UPDATE批量操作

一、replace into <insert id=“a" useGeneratedKeys="true"> REPLACE INTO table_name (product_id,departs_date,price_value) VALUES

將eclipse java程式打包成jar的總結包括工程中沒有引用外部jar包和有引用外部jar包兩種情況

一.當eclispe java工程中沒有引用外部jar包時: 選中工程---->右鍵,Export...--->Java--->JAR file--->next-->填寫jar file的路徑及名稱-->next-->next-

衝突的話就更新否則插入 ON DUPLICATE KEY UPDATE

 INSERT INTO user_tag_exp (uid,tag,detail)VALUES(?,?,?) ON DUPLICATE KEY UPDATE detail=? 看程式竟然發現Mysql有這個功能! 以前寫的程式呀…………………… MySQL 自

用 Python 做資料處理12 個使效率倍增的 Pandas 技巧上下

http://datartisan.com/article/detail/81.html 導語 Python正迅速成為資料科學家偏愛的語言,這合情合理。它擁有作為一種程式語言廣闊的生態環境以及眾多優秀的科學計算庫。如果你剛開始學習Python,可以先了解一下Python的學習路線。 在眾多的科學計算庫中

用 Python 做資料處理12 個使效率倍增的 Pandas 技巧

導語 Python正迅速成為資料科學家偏愛的語言,這合情合理。它擁有作為一種程式語言廣闊的生態環境以及眾多優秀的科學計算庫。如果你剛開始學習Python,可以先了解一下Python的學習路線。在眾多的科學計算庫中,我認為Pandas對資料科學運算最有用。Pandas,

小白c語言細節整理1

C語言細節整理 這篇文章是用於自己本身對c語言進行的一些整理,如果對大家有幫助的話,記得轉發出去讓更多人瞭解哦。 這是我對c primer plus這本書的整理,所以可能和一些書籍順序會不同,本文章和這本書一起使用最好,我也會對一些書中重要的方面批註頁碼的。 第

用 Python 做資料處理12 個使效率倍增的 Pandas 技巧

7 – 資料框合併 當我們有收集自不同來源的資料時,合併資料框就變得至關重要。假設對於不同的房產型別,我們有不同的房屋均價資料。讓我們定義這樣一個數據框: prop_rates = pd.DataFrame([1000, 5000, 12000], index

SEO編輯撰寫搜索引擎喜愛的標題

多重 寶寶樹 有時 查詢 長尾關鍵詞 兒童 共同點 北京 佳能 導讀:非常有幹貨,百度站長平臺剛發布了這篇篇文章,文章建議:1,標題字數控制在65個字節內,2,重要內容放在標題的最前面,3,添加與網頁內容最相關的、用戶更常用的、滿足用戶明確需求的、體現時效性、關鍵詞、直擊

Linux使用者29個必須掌握的常用

Linux使用者必看:29個必須掌握的常用命令 雖然Linux發行版支援各種各樣的GUI(graphical user interfaces),但在某些情況下,Linux的命令行介面(bash)仍然是簡單快速的。Bash和Linux She

小白新手document.getElementById("demo").innerHTML 全面分析

假設程式碼為: <h1>我的第一個 Web 頁面</h1>        <p id="demo">一個段落。</p><script>function myFunction(){ document.getE

小白新手document.getElementById("demo").innerHTML 全面分析

頁面 tel 意思 lena 註意 php style spa 定義 假設代碼為: <h1>我的第一個 Web 頁面</h1> <p id="demo">一個段落。</p><script>functi

吃貨如何用爬蟲工具快速獲取當地1000+5星網紅餐廳資訊?

人們都說,認識一座城,從認識它的美食開始。 作為一名妥妥的吃貨,小八做旅行攻略時,最最最愛的就是美食PART啦! 小八最常用的是大眾點評網,簡直吃貨神器。(小八木有收廣告費哦) 話說做攻略也是很花時間的哦,需要一個個點選進去,篩選,記錄,一晃神2個小

Swift 3Error與NSError的關係

在學習Swift 3的過程中整理了一些筆記,如果想看其他相關文章可前往《Swift 3必看》系列目錄 在之前的版本中,Swift中Error與OC中NSError的關係就像上海的南京路與南京的上海路關係一樣,那就是沒有關係。 我們先來看兩者的區別。 Error是一個實現Erro

資料分析入門3個選擇方向及技能要求!

 每天不少新人加入我們大聖眾包資料交流群,一部分是統計、計算機相關專業的學生,想進一步瞭解資料分析發展,為以後工作準備;而一部分是初步涉入資料的朋友(包括轉行)前來諮詢,沒有相關專業知識可不可以學習資料分析等等問題!下面我們大聖眾包小編帶大家看看不同知識背景的朋友該如何選