欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis foreach標簽使用不當導致異常的原因淺析

 更新時間:2016年12月29日 09:56:31   作者:Gen_zhou  
這篇文章主要介紹了Mybatis foreach標簽使用不當導致異常的原因探究,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

異常產(chǎn)生場景及異常信息

上周,由于在Mybatis的Mapper接口方法中使用實現(xiàn)了Map.Entry接口的泛型類,同時此方法對應的sql語句也使用了foreach標簽,導致出現(xiàn)了異常。如下為異常信息:

org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'key' in 'class java.lang.Integer'
### The error may involve org.guojing.test.spring.server.GoodsRoomnight30daysMapper.insertBatch-Inline
### The error occurred while setting parameters
### SQL: REPLACE INTO goods_roomnight_30days (goods_id, checkin_room_night_30days) VALUES (?, ?) , (?, ?), (?, ?), (?, ?)
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'key' in 'class java.lang.Integer'
 at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
 at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:200)
 at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185)
 at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:57)
 at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
 at com.sun.proxy.$Proxy4.insertBatch(Unknown Source)
 at org.guojing.test.spring.server.GoodsRoomnight30daysTest.test(GoodsRoomnight30daysTest.java:47)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
 at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
 at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
 at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
 at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
 at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
 at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
 at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'key' in 'class java.lang.Integer'
 at org.apache.ibatis.reflection.Reflector.getGetInvoker(Reflector.java:409)
 at org.apache.ibatis.reflection.MetaClass.getGetInvoker(MetaClass.java:164)
 at org.apache.ibatis.reflection.wrapper.BeanWrapper.getBeanProperty(BeanWrapper.java:162)
 at org.apache.ibatis.reflection.wrapper.BeanWrapper.get(BeanWrapper.java:49)
 at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)
 at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:119)
 at org.apache.ibatis.mapping.BoundSql.getAdditionalParameter(BoundSql.java:75)
 at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:72)
 at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:93)
 at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:64)
 at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:86)
 at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
 at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
 at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)
 ... 29 more

由于本人對Mybatis還不是非常的了解,導致用了很長的時間去debug找具體的異常原因。

接下來介紹我將重現(xiàn)異常并分析導致異常的原因。

異常重現(xiàn)

為了重現(xiàn)上面的異常,寫了demo。相關(guān)代碼如下:

數(shù)據(jù)庫表結(jié)構(gòu):

CREATE TABLE `goods_roomnight_30days` (
 `goods_id` bigint(20) NOT NULL,
 `checkin_room_night_30days` int(11) NOT NULL DEFAULT '0' COMMENT '近30天消費間夜',
 PRIMARY KEY (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='goods的近30天消費間夜'

KeyValue.java 參數(shù)類:

public class KeyValue<K, V> implements Map.Entry<K, V> {
 private K key;
 private V value;
 public KeyValue() {
 }
 public KeyValue(K key, V value) {
 this.key = key;
 this.value = value;
 }
 @Override
 public K getKey() {
 return key;
 }
 @Override
 public V getValue() {
 return value;
 }
 @Override
 public V setValue(V value) {
 V oldValue = this.value;
 this.value = value;
 return oldValue;
 }
 public JSONObject toJSONObject() {
 return ReportJSONObject.newObject().append(String.valueOf(key), value);
 }
 @Override
 public String toString() {
 return toJSONObject().toJSONString();
 }
}

DAO類GoodsRoomnight30daysMapper.java

public interface GoodsRoomnight30daysMapper {
 int deleteByExample(GoodsRoomnight30daysExample example);
 List<GoodsRoomnight30days> selectByExample(GoodsRoomnight30daysExample example);
 <K, V> int insertBatch(List<KeyValue<K, V>> records);
}

mybatis-config.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
 <settings>
 <setting name="cacheEnabled" value="false"/>
 </settings>
 <!-- 和spring整合后 environments配置將廢除,交給spring管理-->
 <environments default="development">
 <environment id="development">
  <!-- 使用jdbc事務管理-->
  <transactionManager type="JDBC" />
  <!-- 數(shù)據(jù)庫連接池,整合后一般使用第三方的連接池-->
  <dataSource type="POOLED">
  <property name="driver" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/hotel_report?characterEncoding=utf-8" />
  <property name="username" value="test_user" />
  <property name="password" value="user123" />
  </dataSource>
 </environment>
 </environments>
 <mappers>
 <mapper resource="mybatis/GoodsRoomnight30daysMapper.xml"/>
 </mappers>
</configuration>

GoodsRoomnight30daysMapper.xml文件的主要內(nèi)容:

<insert id="insertBatch" parameterType="list">
 REPLACE INTO goods_roomnight_30days (goods_id, checkin_room_night_30days)
 VALUES
 <foreach collection="list" index="index" item="item" separator=",">
  (#{item.key}, #{item.value})
  <!--<choose>-->
  <!--<when test="item.value == null">(#{item.key}, 0)</when>-->
  <!--<when test="item.value != null">(#{item.key}, #{item.value})</when>-->
  <!--</choose>-->
 </foreach>
 </insert>

以上為重現(xiàn)此異常的主要代碼,完整代碼可在Github查看:https://github.com/misterzhou/java-demo/tree/master/test-spring/spring-server

重現(xiàn)異常的測試代碼 GoodsRoomnight30daysTest.java:

package org.guojing.test.spring.server;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.guojing.spring.commons.KeyValue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
 * Created at: 2016-12-24
 *
 * @author guojing
 */
public class GoodsRoomnight30daysTest {
 SqlSessionFactory sqlSessionFactory;
 SqlSession sqlSession;
 GoodsRoomnight30daysMapper goodsRoomnight30daysMapper;
 @Before
 public void init() throws IOException {
 String resource = "mybatis/mybatis-config.xml";
 InputStream inputStream = Resources.getResourceAsStream(resource);
 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 sqlSession = sqlSessionFactory.openSession(true);
 goodsRoomnight30daysMapper = sqlSession.getMapper(GoodsRoomnight30daysMapper.class);
 }
 @Test
 public void test() {
 List<KeyValue<Long, Integer>> records = new ArrayList<>();
 records.add(new KeyValue<Long, Integer>(1725L, 5));
 records.add(new KeyValue<Long, Integer>(1728L, 3));
 records.add(new KeyValue<Long, Integer>(1730L, null));
 records.add(new KeyValue<Long, Integer>(1758L, null));
 int deleted = goodsRoomnight30daysMapper.deleteByExample(new GoodsRoomnight30daysExample());
 System.out.println("----- deleted row size: " + deleted);
 int row = goodsRoomnight30daysMapper.insertBatch(records);
 System.out.println("----- affected row: " + row);
 List<GoodsRoomnight30days> result = goodsRoomnight30daysMapper.selectByExample(new GoodsRoomnight30daysExample());
 for (GoodsRoomnight30days item : result) {
  System.out.println(item.toString());
 }
 }
 @After
 public void after() {
 if (sqlSession != null) {
  sqlSession.close();
 }
 }
}

賣個關(guān)子,大家先不要往下看,想想導致異常的原因(熟練使用foreach標簽的同學應該能看出端倪)。

查找異常過程及異常分析

在項目中,由于DAO方法的參數(shù)類和返回結(jié)果類經(jīng)常會包含一個鍵和鍵對應的值,為了避免重復定義類,我定義了一個實現(xiàn)了Map.Entry接口的KeyValue泛型類,具體請查看上節(jié)。

GoodsRoomnight30daysMapper.insertBatch()方法就使用了此泛型類。在運行之后就拋出了本文開始提到的異常信息。

看到異常信息后,就把重點放到了是不是Mybatis對泛型的支持不夠好上。問了下同事(@勝男),同事在自己的機器上試了下,發(fā)現(xiàn)沒有異常。這就奇怪了。仔細看了下代碼,發(fā)現(xiàn)不同之處就是我的KeyValue泛型類實現(xiàn)了Map.Entry接口。 此時還不知道m(xù)ybatis官網(wǎng)對于foreach標簽的說明:

可以將任何可迭代對象(如列表、集合等)和任何的字典或者數(shù)組對象傳遞給foreach作為集合參數(shù)。當使用可迭代對象或者數(shù)組時,index是當前迭代的次數(shù),item的值是本次迭代獲取的元素。當使用字典(或者Map.Entry對象的集合)時,index是鍵,item是值。

接下來就是通過debug看看,異常產(chǎn)生的具體原因。于是就先用實現(xiàn)了Map.Entry接口的KeyValue類的代碼進行debug,通過異常日志可以看到異常是在 DefaultSqlSession.java:200 行拋出,那么將斷點打到 DefaultSqlSession.java:197行,然后一步步向下執(zhí)行,當執(zhí)行到 ForEachSqlNode.java:73行時,終于煥然大悟。先看下debug調(diào)用鏈圖:

再看看具體的代碼 ForEachSqlNode.java:73(此類就是foreach標簽對象的Node類):

此時具體的異常原因就很明顯了,此處的 o 對象的所屬的類就是KeyValue類,由于KeyValue類實現(xiàn)了Map.Entry接口,所以 o instance Map.Entry 為true,mybatis就把key值賦給了foreach的index屬性,而把value值賦給了item屬性,此處也就是把值為5的Integer對象賦給了item屬性。所以 GoodsRoomnight30daysMapper.xml 中id為 insertBatch 的select標簽的item屬性對應的對象也就沒有 item.key 和 item.value 屬性,這就是最終導致異常的原因。

<insert id="insertBatch" parameterType="list">
 REPLACE INTO goods_roomnight_30days (goods_id, checkin_room_night_30days)
 VALUES
 <foreach collection="list" index="index" item="item" separator=",">
 (#{item.key}, #{item.value})
 </foreach>
</insert>

以上所述是小編給大家介紹的Mybatis foreach標簽使用不當導致異常的原因淺析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Java常見踩坑記錄之異常處理

    Java常見踩坑記錄之異常處理

    程序運行時發(fā)生的不被期望的事件,它阻止了程序按照程序員的預期正常執(zhí)行,這就是異常,下面這篇文章主要給大家介紹了關(guān)于Java常見踩坑記錄之異常處理的相關(guān)資料,需要的朋友可以參考下
    2022-01-01
  • JavaGUI常用窗體組件與面板使用詳解

    JavaGUI常用窗體組件與面板使用詳解

    GUI即圖形用戶界面,它是基于圖形的界面,windows就是一個圖形用戶界面的操作系統(tǒng),而DOS是基于命令提示符的操作系統(tǒng),GUI編程就是編出一個圖形用戶界面的軟件,它使用圖形的方式,以菜單、按鈕、表示、圖文框等標準界面元素組成的用戶操作界面
    2023-03-03
  • Java中ArrayList和LinkedList之間的區(qū)別_動力節(jié)點Java學院整理

    Java中ArrayList和LinkedList之間的區(qū)別_動力節(jié)點Java學院整理

    這篇文章主要為大家詳細介紹了Java中ArrayList和LinkedList之間的區(qū)別,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • SpringMVC 使用JSR-303進行校驗 @Valid示例

    SpringMVC 使用JSR-303進行校驗 @Valid示例

    本篇文章主要介紹了SpringMVC 使用JSR-303進行校驗 @Valid示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • java通過實例了解值傳遞和引用傳遞

    java通過實例了解值傳遞和引用傳遞

    這篇文章主要介紹了java通過實例了解值傳遞和引用傳遞,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • 如何利用反射批量修改java類某一屬性的代碼詳解

    如何利用反射批量修改java類某一屬性的代碼詳解

    這篇文章主要介紹了如何利用反射批量修改java類某一屬性,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • Mybatis框架搭建與簡單查詢詳解

    Mybatis框架搭建與簡單查詢詳解

    本文主要介紹了Mybatis框架搭建與簡單查詢的相關(guān)知識,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • JavaCV實現(xiàn)圖片中人臉檢測的示例代碼

    JavaCV實現(xiàn)圖片中人臉檢測的示例代碼

    這篇文章主要介紹了如何利用JavaCV實現(xiàn)圖片中人臉檢測的功能,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的可以了解一下
    2022-11-11
  • Java實現(xiàn)LRU緩存的代碼詳解

    Java實現(xiàn)LRU緩存的代碼詳解

    LRU緩存是一種緩存替換策略,當緩存容量達到上限時,LRU 會淘汰掉最近最少使用的緩存項,在 Java 中,我們可以使用 LinkedHashMap 來實現(xiàn)一個簡單的 LRU 緩存,所以本文給大家介紹了Java實現(xiàn)LRU緩存的方法,需要的朋友可以參考下
    2025-03-03
  • springboot整合mybatis plus與druid詳情

    springboot整合mybatis plus與druid詳情

    這篇文章主要介紹了springboot整合mybatis plus與druid詳情,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的下伙伴可以參考一下
    2022-09-09

最新評論