亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

mybatis插入與批量插入返回ID的原理詳解

 更新時(shí)間:2019年07月12日 11:02:06   作者:陳晨_軟件五千言  
這篇文章主要給大家介紹了關(guān)于mybatis插入與批量插入返回ID的原理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用mybatis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

背景

最近正在整理之前基于mybatis的半ORM框架。原本的框架底層類(lèi)ORM操作是通過(guò)StringBuilder的append拼接的,這次打算用JsqlParser重寫(xiě)一遍,一來(lái)底層不會(huì)存在太多的文本拼接,二來(lái)基于其他開(kāi)源包維護(hù)難度會(huì)小一些,最后還可以整理一下原有的冗余方法。

這兩天整理insert相關(guān)的方法,在將對(duì)象插入數(shù)據(jù)庫(kù)后,期望是要返回完整對(duì)象,并且包含實(shí)際的數(shù)據(jù)庫(kù)id。

基礎(chǔ)相關(guān)框架為:spring、mybatis、hikari。

底層調(diào)用方法

最底層的做法實(shí)際上很直白,就是利用mybatis執(zhí)行最簡(jiǎn)單的sql語(yǔ)句,給上代碼。

@Repository("baseDao")
public class BaseDao extends SqlSessionDaoSupport {

 private Logger logger = LoggerFactory.getLogger(this.getClass());

 /**
  * 最大的單次批量插入的數(shù)量
  */
 private static final int MAX_BATCH_SIZE = 10000;

 @Override
 @Autowired
 public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
  super.setSqlSessionFactory(sqlSessionFactory);
 }


 /**
  * 根據(jù)sql方法名稱(chēng)和對(duì)象插入數(shù)據(jù)庫(kù)
  */
 public Object insert(String sqlName, Object obj) throws SQLException {
  return getSqlSession().insert(sqlName, obj); // 此處直接執(zhí)行傳入的xml中對(duì)應(yīng)的sql id,以及參數(shù)

 }
}

單個(gè)對(duì)象插入

java代碼

 /**
  * 簡(jiǎn)單插入實(shí)體對(duì)象
  *
  * @param entity 實(shí)體對(duì)象
  * @throws SQLException
  */
 public <T extends BaseEntity> T insertEntity(T entity) throws SQLException {
  Insert insert = new Insert();
  insert.setTable(new Table(entity.getClass().getSimpleName()));
  insert.setColumns(JsqlUtils.getColumnNameFromEntity(entity.getClass()));
  insert.setItemsList(JsqlUtils.getAllColumnValueFromEntity(entity,insert.getColumns()));

  Map<String, Object> param = new HashMap<>();
  param.put("baseSql", insert.toString());
  param.put("entity", entity);
  this.insert("BaseDao.insertEntity", param);

  return entity;
 }

xml代碼

 <insert id="insertEntity" parameterType="java.util.Map" useGeneratedKeys="true" keyProperty="entity.id">
 ${baseSql}
 </insert>

其他的就不多說(shuō)了,這里針對(duì)如何返回已經(jīng)入庫(kù)的id給個(gè)說(shuō)明。

在xml的 insert 標(biāo)簽中,設(shè)置 keyProperty 為 對(duì)應(yīng)對(duì)象的id字段,和 insert(sqlName, obj) 這個(gè)方法中的 obj 是對(duì)應(yīng)的。

這里一般有兩種情況:

直接保存實(shí)體的對(duì)象作為參數(shù)傳入(給偽代碼示例)

SaveObject saveObject = new SaveObject(); // SaveObject中包含字段soid,作為自增id
saveObject.setName("my name");
saveObject.setNums(2);

getSqlSession().insert("saveObject.insert",saveObject);

這種情況實(shí)際就是傳入了待保存的對(duì)象。這時(shí)候我們的xml應(yīng)該這樣

 <insert id="insert" parameterType="SaveObject " useGeneratedKeys="true" keyProperty="soid">
 insert into save_object (`name`,nums) values (#{names},#{nums})
 </insert>

這里我們傳入了SaveObject實(shí)體對(duì)象作為參數(shù),所以我們的 keyProperty 就是parameter的id對(duì)應(yīng)的字段,在這里就是 soid 。

多個(gè)對(duì)象,實(shí)體對(duì)象作為其中一個(gè)對(duì)象傳入

    Map<String, Object> param = new HashMap<>();
    param.put("baseSql", insert.toString());
    param.put("entity", entity); // 此處對(duì)應(yīng)實(shí)體作為map的第二個(gè)參數(shù)傳入
    this.insert("BaseDao.insertEntity", param);
 <insert id="insertEntity" parameterType="java.util.Map" useGeneratedKeys="true" keyProperty="entity.id">
 ${baseSql}
 </insert>

這里也是比較容易理解,當(dāng)傳入?yún)?shù)是Map時(shí),我們的 keyProperty 對(duì)應(yīng)方式就是先從Map中讀出對(duì)應(yīng)value,再指向 value中的id字段。

列表批量插入

批量插入數(shù)據(jù)有兩種做法,一種是多次調(diào)用單個(gè)insert方法,這種效率較低就不說(shuō)了。另外一種是 insert into table (cols) values (val1),(val2),(val3) 這樣批量插入。

到mybatis中,也是分為兩種

直接保存實(shí)體的對(duì)象作為參數(shù)傳入(給偽代碼示例)

SaveObject saveObject1 = new SaveObject(); // SaveObject中包含字段soid,作為自增id
saveObject1.setName("my name");
saveObject1.setNums(2);

SaveObject saveObject2 = new SaveObject(); // SaveObject中包含字段soid,作為自增id
saveObject2.setName("my name");
saveObject2.setNums(2);

List<SaveObject> saveObjects = new ArrayList<SaveObject>();
saveObjects.add(saveObjects1);
saveObjects.add(saveObjects2);

getSqlSession().insert("saveObject.insertList",saveObjects);

這種情況實(shí)際就是傳入了待保存的對(duì)象。這時(shí)候我們的xml應(yīng)該這樣

 <insert id="insertList" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="soid">
    insert into save_object (`name`,nums) values
    <foreach collection="list" index="index" item="saveObject" separator=","> 
      (#{saveObject.numsnames}, #{saveObject.nums}) 
    </foreach>
 </insert>

多個(gè)對(duì)象,實(shí)體對(duì)象作為其中一個(gè)對(duì)象傳入

本文的重點(diǎn)來(lái)了,我自己卡在這里很久,反復(fù)調(diào)試才摸清邏輯。接下來(lái)就順著mybatis的思路來(lái)講,只會(huì)講id生成相關(guān)的,其他的流程就不多說(shuō)了。

先看這個(gè)類(lèi):org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator (很多代碼我用...代替了,不是特別重要,放在還占地方)

 /**
  * 這個(gè)方法是在執(zhí)行完插入語(yǔ)句之后處理的,兩個(gè)關(guān)鍵參數(shù) 
  * 1. MappedStatement ms 里面包含了我們的 keyProperty
  * 2. Object parameter 就是我們inser方法傳入的參數(shù)
  */
 @Override
 public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
  processBatch(ms, stmt, parameter);
 }

 public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
  final String[] keyProperties = ms.getKeyProperties();
  if (keyProperties == null || keyProperties.length == 0) {
   return;
  }
  try (ResultSet rs = stmt.getGeneratedKeys()) {
   final Configuration configuration = ms.getConfiguration();
   if (rs.getMetaData().getColumnCount() >= keyProperties.length) {
    Object soleParam = getSoleParameter(parameter);
    if (soleParam != null) {
     assignKeysToParam(configuration, rs, keyProperties, soleParam);
    } else {
     assignKeysToOneOfParams(configuration, rs, keyProperties, (Map<?, ?>) parameter);
    }
   }
  } catch (Exception e) {
   ...
  }
 }

 protected void assignKeysToOneOfParams(final Configuration configuration, ResultSet rs, final String[] keyProperties,
   Map<?, ?> paramMap) throws SQLException {
  // Assuming 'keyProperty' includes the parameter name. e.g. 'param.id'.
  int firstDot = keyProperties[0].indexOf('.');
  if (firstDot == -1) {
   ...
  }
  String paramName = keyProperties[0].substring(0, firstDot);
  Object param;
  if (paramMap.containsKey(paramName)) {
   param = paramMap.get(paramName);
  } else {
   ...
  }
  ...
  assignKeysToParam(configuration, rs, modifiedKeyProperties, param);
 }

 private void assignKeysToParam(final Configuration configuration, ResultSet rs, final String[] keyProperties,
   Object param)
   throws SQLException {
  final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  final ResultSetMetaData rsmd = rs.getMetaData();
  // Wrap the parameter in Collection to normalize the logic.
  Collection<?> paramAsCollection = null;
  if (param instanceof Object[]) {
   paramAsCollection = Arrays.asList((Object[]) param);
  } else if (!(param instanceof Collection)) {
   paramAsCollection = Arrays.asList(param);
  } else {
   paramAsCollection = (Collection<?>) param;
  }
  TypeHandler<?>[] typeHandlers = null;
  for (Object obj : paramAsCollection) {
   if (!rs.next()) {
    break;
   }
   MetaObject metaParam = configuration.newMetaObject(obj);
   if (typeHandlers == null) {
    typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);
   }
   populateKeys(rs, metaParam, keyProperties, typeHandlers);
  }
 }

利用這個(gè)代碼先解釋一下上一節(jié) 直接保存實(shí)體的對(duì)象作為參數(shù)傳入 為什么id會(huì)被更新至實(shí)體內(nèi)的soid字段。

上一節(jié)的是 keyProperty="soid"

我們來(lái)看19行的代碼Object soleParam = getSoleParameter(parameter); ,當(dāng)我們傳入的對(duì)象是List的時(shí)候 soleParam != null,所以 直接執(zhí)行 assignKeysToParam 方法。

注意64和65行

for (Object obj : paramAsCollection) {
if (!rs.next()) {

paramAsCollection 是將我們傳入的轉(zhuǎn)換為 Collection 類(lèi)型,所以這里是循環(huán)我們的給定實(shí)體列表參數(shù)。

rs就是ResultSet,就是插入之后的結(jié)果集。 rs.next()就是指針指向下一條記錄,所以實(shí)際上這里是同步循環(huán),將結(jié)果集中的id直接設(shè)置到我們給的實(shí)體列表中

我們現(xiàn)在來(lái)看看多參數(shù)插入是會(huì)有什么問(wèn)題。

Java方法:

  /**
   * 簡(jiǎn)單批量插入實(shí)體對(duì)象
   *
   * @param entitys
   * @throws SQLException
   */
  public List insertEntityList(List<? extends BaseEntity> entitys) throws SQLException {
    if (entitys == null || entitys.size() == 0) {
      return null;
    }

    Insert insert = new Insert();
    insert.setTable(new Table(entitys.get(0).getClass().getSimpleName()));
    insert.setColumns(JsqlUtils.getColumnNameFromEntity(entitys.get(0).getClass()));
    MultiExpressionList multiExpressionList = new MultiExpressionList();
    entitys.stream().map(e -> JsqlUtils.getAllColumnValueFromEntity(e,insert.getColumns())).forEach(e -> multiExpressionList.addExpressionList(e));
    insert.setItemsList(multiExpressionList);

    Map<String, Object> param = new HashMap<>();
    param.put("baseSql", insert.toString());
    param.put("list", entitys);
    this.insert("BaseDao.insertEntityList", param);
    return entitys;
  }

Xml:

 <insert id="insertEntityList" parameterType="java.util.Map" useGeneratedKeys="true" keyProperty="id">
 ${baseSql}
 </insert>

會(huì)有什么問(wèn)題??根據(jù)這樣的xml,最后的結(jié)果是我們傳入的map中會(huì)多一個(gè)key 叫 “id”,里面存的是一個(gè)插入的實(shí)體的id。

因?yàn)楦鶕?jù)源碼 Map并非 Collection 類(lèi)型,所以會(huì)做為只有一個(gè)元素的數(shù)組傳入,在剛才同步循環(huán)的地方就只會(huì)循環(huán)一次,把結(jié)果集中第一條數(shù)據(jù)的id放進(jìn)map中,循環(huán)就結(jié)束了。

怎么解決呢??

解決的方法就在 assignKeysToOneOfParams 這個(gè)方法,方法名其實(shí)已經(jīng)說(shuō)了,將主鍵賦給其中一個(gè)參數(shù),這里確實(shí)也是取了其中的一個(gè)參數(shù)進(jìn)行賦值主鍵。所以我們只要能夠跳轉(zhuǎn)到這個(gè)方法就好。所以需要滿(mǎn)足 getSoleParameter(parameter) == null ,點(diǎn)進(jìn)代碼看

private Object getSoleParameter(Object parameter) {
  if (!(parameter instanceof ParamMap || parameter instanceof StrictMap)) {
   return parameter;
  }
  Object soleParam = null;
  for (Object paramValue : ((Map<?, ?>) parameter).values()) {
   if (soleParam == null) {
    soleParam = paramValue;
   } else if (soleParam != paramValue) {
    soleParam = null;
    break;
   }
  }
  return soleParam;
 }

要返回null,條件是這樣:

  • 參數(shù)是ParamMap或者 StrictMap
  • 參數(shù)大于兩個(gè),且第一個(gè)和后面任意一個(gè)不相等

所以解決方案出爐,很簡(jiǎn)單,只需要改動(dòng)代碼兩個(gè)地方即可。

  /**
   * 簡(jiǎn)單批量插入實(shí)體對(duì)象
   *
   * @param entitys
   * @throws SQLException
   */
  public List insertEntityList(List<? extends BaseEntity> entitys) throws SQLException {
    if (entitys == null || entitys.size() == 0) {
      return null;
    }

    Insert insert = new Insert();
    insert.setTable(new Table(entitys.get(0).getClass().getSimpleName()));
    insert.setColumns(JsqlUtils.getColumnNameFromEntity(entitys.get(0).getClass()));
    MultiExpressionList multiExpressionList = new MultiExpressionList();
    entitys.stream().map(e -> JsqlUtils.getAllColumnValueFromEntity(e,insert.getColumns())).forEach(e -> multiExpressionList.addExpressionList(e));
    insert.setItemsList(multiExpressionList);

    Map<String, Object> param = new MapperMethod.ParamMap<>(); // 這里替換為 MapperMethod.ParamMap 類(lèi)型
    param.put("baseSql", insert.toString());
    param.put("list", entitys);
    this.insert("BaseDao.insertEntityList", param);
    return entitys;
  }

Xml:

 <insert id="insertEntityList" parameterType="java.util.Map" useGeneratedKeys="true" keyProperty="list.id"> <!-- 這里是map中的key.實(shí)體id -->
 ${baseSql}
 </insert>

完成

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • java+selenium實(shí)現(xiàn)滑塊驗(yàn)證

    java+selenium實(shí)現(xiàn)滑塊驗(yàn)證

    現(xiàn)在越來(lái)越多的網(wǎng)站都使用采用滑塊驗(yàn)證來(lái)作為驗(yàn)證機(jī)制,用于判斷用戶(hù)是否為人類(lèi)而不是機(jī)器人,本文就將利用java和selenium實(shí)現(xiàn)滑塊驗(yàn)證,希望對(duì)大家有所幫助
    2023-12-12
  • Java數(shù)據(jù)結(jié)構(gòu)最清晰圖解二叉樹(shù)前 中 后序遍歷

    Java數(shù)據(jù)結(jié)構(gòu)最清晰圖解二叉樹(shù)前 中 后序遍歷

    樹(shù)是一種重要的非線(xiàn)性數(shù)據(jù)結(jié)構(gòu),直觀地看,它是數(shù)據(jù)元素(在樹(shù)中稱(chēng)為結(jié)點(diǎn))按分支關(guān)系組織起來(lái)的結(jié)構(gòu),很象自然界中的樹(shù)那樣。樹(shù)結(jié)構(gòu)在客觀世界中廣泛存在,如人類(lèi)社會(huì)的族譜和各種社會(huì)組織機(jī)構(gòu)都可用樹(shù)形象表示
    2022-01-01
  • MyBatis Log 插件無(wú)法顯示SQL語(yǔ)句的原因解析

    MyBatis Log 插件無(wú)法顯示SQL語(yǔ)句的原因解析

    MyBatis Log是IDEA一款下載量非常高的插件,該插件可以對(duì)控制臺(tái)打印的日志進(jìn)行解析,然后將對(duì)應(yīng)的SQL語(yǔ)句整理并拼接好對(duì)應(yīng)的參數(shù),非常方便。這篇文章給大家介紹MyBatis Log 插件無(wú)法顯示SQL語(yǔ)句的原因,感興趣的朋友跟隨小編一起看看吧
    2020-09-09
  • 淺談java中math類(lèi)中三種取整函數(shù)的區(qū)別

    淺談java中math類(lèi)中三種取整函數(shù)的區(qū)別

    下面小編就為大家?guī)?lái)一篇淺談java中math類(lèi)中三種取整函數(shù)的區(qū)別。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-11-11
  • Java快速入門(mén)掌握類(lèi)與對(duì)象及變量的使用

    Java快速入門(mén)掌握類(lèi)與對(duì)象及變量的使用

    類(lèi)和對(duì)象是兩種以計(jì)算機(jī)為載體的計(jì)算機(jī)語(yǔ)言的合稱(chēng)。對(duì)象是對(duì)客觀事物的抽象,類(lèi)是對(duì)對(duì)象的抽象。類(lèi)是一種抽象的數(shù)據(jù)類(lèi)型;變量就是可以變化的量,存儲(chǔ)在內(nèi)存中—個(gè)可以擁有在某個(gè)范圍內(nèi)的可變存儲(chǔ)區(qū)域
    2022-04-04
  • Springboot中攔截GET請(qǐng)求獲取請(qǐng)求參數(shù)驗(yàn)證合法性核心方法

    Springboot中攔截GET請(qǐng)求獲取請(qǐng)求參數(shù)驗(yàn)證合法性核心方法

    這篇文章主要介紹了Springboot中攔截GET請(qǐng)求獲取請(qǐng)求參數(shù)驗(yàn)證合法性,在Springboot中創(chuàng)建攔截器攔截所有GET類(lèi)型請(qǐng)求,獲取請(qǐng)求參數(shù)驗(yàn)證內(nèi)容合法性防止SQL注入,這種方法適用攔截get類(lèi)型請(qǐng)求,需要的朋友可以參考下
    2023-08-08
  • Java生成讀取條形碼和二維碼的簡(jiǎn)單示例

    Java生成讀取條形碼和二維碼的簡(jiǎn)單示例

    條形碼(barcode)是將寬度不等的多個(gè)黑條和空白,按照一定的規(guī)則排列,用來(lái)表示一組信息的圖形標(biāo)識(shí)符,而二維碼大家應(yīng)該都很熟悉了,這篇文章主要給大家介紹了關(guān)于Java生成讀取條形碼和二維碼的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • Java中Integer的parseInt和valueOf的區(qū)別詳解

    Java中Integer的parseInt和valueOf的區(qū)別詳解

    這篇文章主要介紹了Java中Integer的parseInt和valueOf的區(qū)別詳解,nteger.parseInt(s)是把字符串解析成int基本類(lèi)型,Integer.valueOf(s)是把字符串解析成Integer對(duì)象類(lèi)型,其實(shí)int就是Integer解包裝,Integer就是int的包裝,需要的朋友可以參考下
    2023-11-11
  • java獲取版本號(hào)及字節(jié)碼編譯版本方法示例

    java獲取版本號(hào)及字節(jié)碼編譯版本方法示例

    這篇文章主要給大家介紹了關(guān)于java獲得版本號(hào)及字節(jié)碼編譯版本的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • Spring Boot REST國(guó)際化的實(shí)現(xiàn)代碼

    Spring Boot REST國(guó)際化的實(shí)現(xiàn)代碼

    本文我們將討論如何在現(xiàn)有的Spring Boot項(xiàng)目中添加國(guó)際化。只需幾個(gè)簡(jiǎn)單的步驟即可實(shí)現(xiàn)Spring Boot應(yīng)用的國(guó)際化,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-10-10

最新評(píng)論