สถานการณ์การสร้างข้อยกเว้นและข้อมูลข้อยกเว้น
เมื่อสัปดาห์ที่แล้วมีข้อยกเว้นเกิดขึ้นเนื่องจากคลาสทั่วไปที่ใช้อินเทอร์เฟซ Map.entry ถูกใช้ในวิธีการแม็ปเปอร์อินเทอร์เฟซของ MyBatis และคำสั่ง SQL ที่สอดคล้องกับวิธีนี้ยังใช้แท็ก foreach ข้อมูลต่อไปนี้เป็นข้อมูลข้อยกเว้น:
org.apache.ibatis.exceptions.persistenceException: ### การอัพเดตข้อผิดพลาดฐานข้อมูล สาเหตุ: org.apache.ibatis.reflection.reflectionException: ไม่มี getter สำหรับทรัพย์สินชื่อ 'key' ใน 'คลาส java.lang.integer' ### ข้อผิดพลาดอาจเกี่ยวข้องกับ org.guojing.test.spring.server.goodsroomnight30 goods_roomnight_30days (goods_id, checkin_room_night_30days) ค่า (?,?), (?,?), (?,?), (?,?), (?,?) ### สาเหตุ: org.apache.ibatis.reflection.reflection org.apache.ibatis.exceptions.exceptionfactory.wrapexception (exctionfactory.java:30) ที่ org.apache.ibatis.session.defaults.defaultsqlsession.update (defaultsqlsession.java:200) ที่ org.apache.ibatis.session.defaults.defaultsqlsession.insert (defaultsqlsession.java:185) ที่ org.apache.ibatis.binding.mappermethod.execute org.apache.ibatis.binding.mapperproxy.invoke (mappreproxy.java:53) ที่ com.sun.proxy. $ proxy4.insertbatch (แหล่งที่ไม่รู้จัก) ที่ org.guojing.test.spring.server.goodsroomnight30 วัน sun.reflect.nativemethodaccessorimpl.invoke0 (วิธีการดั้งเดิม) ที่ sun.reflect.nativemethodaccessorimpl.invoke (nativemethodaccessorimpl.java:57) ที่ sun.reflect.delegatingmethodaccessorimpl.invoke java.lang.reflect.method.invoke (method.java:606) ที่ org.junit.runners.model.frameworkmethod $ 1.runreflectivecall (frameworkmethod.java:47) ที่ org.junit.internal.runners.model.reflectivecallable.run (Reflectivecallable.java:12) ที่ org.junit.runners.model.frameworkmethod.invokeexplosively (frameworkmethod.java:44) org.junit.internal.runners.statements.invokemethod.evaluate (invokemethod.java:17) ที่ org.junit.internal.runners.statements.runbefores.evaluate (runbefores.java:26) ที่ org.junit.internal.runners.statements.runafters.evaluate (runafters.java:27) ที่ org.junit.runners.parentrunner.runleaf (Parentrunner.java:271) ที่ org.junit.runners.blockjunit4classrunner.runchild (blockjunit4classrunner.java:70) ที่ org.junit.runners.blockjunit4classrunner.runchild (blockjunit4classrunner.java:50) org.junit.runners.parentrunner $ 3.run (Parentrunner.java:238) ที่ org.junit.runners.parentrunner $ 1.Schedule (ParentRunner.java:63) ที่ org.junit.runners.parentrunner org.junit.runners.parentrunner.access $ 000 (Parentrunner.java:53) ที่ org.junit.runners.parentrunner $ 2.Evaluate (Parentrunner.java:229) ที่ org.junit.runners.parentrunner.runner.runner. org.junit.runner.junitcore.run (junitcore.java:160) ที่ com.intellij.junit4.junit4ideStunner.startrunnerwithargs (junit4ideStunner.java:68) com.intellij.rt.execution.junit.ideatestrunner $ repeater.startrunnerwithargs (ideatestrunner.java:51) ที่ com.intellij.rt.execution.junit.junitstarter.preparestreamsandstart com.intellij.rt.execution.junit.junitstarter.main (junitstarter.java:70) เกิดจาก: org.apache.ibatis.reflection.reflectionException: ไม่มีการรับทรัพย์สินที่ชื่อว่า 'key' ใน 'Class Java.lang.integer' org.apache.ibatis.reflection.reftect org.apache.ibatis.reflection.wrapper.beanwrapper.getBeanProperty (beanwrapper.java:162) ที่ org.apache.ibatis.reflection.wrapper.beanwrapper.get (beanwrapper.java:49) ที่ org.apache.ibatis.reflection.metaobject.getValue (metaobject.java:122) ที่ org.apache.ibatis.reflection.metaobject.getValue (metaobject.java:119) org.apache.ibatis.scripting.defaults.defaultparameterhandler.setParameters (defaultParameterhandler.java:72) ที่ org.apache.ibatis.executor.statement.preparedstatementler.parameterize org.apache.ibatis.executor.statement.routingstatementhandler.parameterize (เส้นทาง StatementHandler.java:64) ที่ org.apache.ibatis.executor.simpleexecutor.prepareverestatement org.apache.ibatis.executor.simpleexecutor.doupdate (simpleexecutor.java:49) ที่ org.apache.ibatis.executor.baseexecutor.update (baseexecutor.java:117) ที่ org.apache.ibatis.session.defaults.defaultsqlsession.update (defaultsqlsession.java:198) ... 29 เพิ่มเติม
เพราะฉันไม่รู้มากเกี่ยวกับ mybatis ฉันใช้เวลานานในการดีบักและค้นหาสาเหตุเฉพาะของความผิดปกติ
ต่อไปฉันจะทำซ้ำข้อยกเว้นและวิเคราะห์สาเหตุของข้อยกเว้น
ข้อยกเว้นปรากฏขึ้นอีกครั้ง
เพื่อที่จะทำซ้ำข้อยกเว้นข้างต้นได้มีการเขียนตัวอย่าง รหัสที่เกี่ยวข้องมีดังนี้:
โครงสร้างตารางฐานข้อมูล:
สร้างตาราง `goods_roomnight_30days` (` goods_id` bigint (20) ไม่ใช่ null, `checkin_room_night_30days` int (11) ไม่ใช่ค่าเริ่มต้น '0' ความคิดเห็น '30 วันสุดท้าย', คีย์หลัก (` goods_id`)) เครื่องยนต์ = InnoDB เริ่มต้น
KeyValue.java พารามิเตอร์คลาส:
KeyValue ระดับสาธารณะ <k, v> ใช้ map.entry <k, v> {คีย์ส่วนตัว k; มูลค่า V ส่วนตัว; Public KeyValue () {} KeyValue สาธารณะ (k -key, v value) {this.key = key; this.value = ค่า; } @Override สาธารณะ k getKey () {คีย์ return; } @Override สาธารณะ v getValue () {ค่าคืน; } @Override สาธารณะ v setValue (ค่า V) {v oldValue = this.value; this.value = ค่า; กลับ OldValue; } สาธารณะ jsonObject toJSonObject () {return reportjsonobject.newobject (). ผนวก (string.valueof (คีย์), ค่า); } @Override สตริงสาธารณะ toString () {return tojsonObject (). toJSonstring (); -DAO Class Goodroomnight30daysmapper.java
อินเทอร์เฟซสาธารณะ Goodroomnight30daysmapper {int deleteByExample (ตัวอย่าง Goodroomnight30 วันที่ตัวอย่าง); รายการ <HotesRoomnight30 วัน> SelectByexample (ตัวอย่าง Goodroomnight30 วันที่ตัวอย่าง); <k, v> intsertbatch (รายการ <keyValue <k, v >> บันทึก);}ไฟล์ mybatis-config.xml:
<? xml version = "1.0" การเข้ารหัส = "utf-8"?> <! doctype การกำหนดค่าสาธารณะ "-// mybatis.org//dtd config 3.0 // en" "http://mybatis.org/dtd/mybatis-3-config.dtd </settings> <!-หลังจากการรวมกับฤดูใบไม้ผลิการกำหนดค่าสภาพแวดล้อมจะถูกยกเลิกและส่งมอบให้กับการจัดการฤดูใบไม้ผลิ-> <environment default = "การพัฒนา"> <environment id = "การพัฒนา"> <!-ใช้การจัดการธุรกรรม JDBC-> <transactionManager name = "driver" value = "com.mysql.jdbc.driver" /> <property name = "url" value = "jdbc: mysql: // localhost: 3306 /hotel_report? characterencoding = UTF-8" /> </environment> </environments> <mappers> <mapper resource = "mybatis/goodsroonnight30daysmapper.xml"/> </mappers> </การกำหนดค่า>
เนื้อหาหลักของไฟล์ Goodsroomnight30daysmapper.xml:
<insert id = "insertBatch" parameterType = "list"> แทนที่เป็น goods_roomnight_30days (goods_id, checkin_room_night_30days) ค่า <foreach collection = "list" {index rexid} item = "item>" null "> (#{item.key}, 0) </เมื่อ>-> <!-<เมื่อ test =" item.value! = null "> (#{item.key},#{item.value}) </เมื่อ>-> <!ข้างต้นเป็นรหัสหลักสำหรับการทำซ้ำข้อยกเว้นนี้ รหัสที่สมบูรณ์สามารถดูได้บน gitHub: https://github.com/misterzhou/java-demo/tree/master/test-spring/spring-server
รหัสทดสอบที่ทำซ้ำ Goodsroomnight30daystest.java:
แพ็คเกจ org.guojing.test.spring.server; นำเข้า org.apache.ibatis.io.resources; นำเข้า org.apache.ibatis.session.sqlsession; นำเข้า org.apache.ibatis.session.sqlsessionformatory; org.apache.ibatis.session.sqlsessionfactorybuilder; นำเข้า org.guojing.spring.commons.keyvalue; นำเข้า org.junit.after; นำเข้า org.junit.before; นำเข้า org.junit.test; java.util.list;/** * สร้างขึ้นที่: 2016-12-24 * * @author guojing */ชั้นเรียนสาธารณะ Goodsroomnight30daystest {sqlsessionfactory sqlsessionfactory; sqlsession sqlsession; Goodroomnight30daysmapper Goodsroomnight30daysmapper; @Before public void init () พ่น ioexception {string resource = "mybatis/mybatis-config.xml"; InputStream InputStream = Resources.getResourceasstream (ทรัพยากร); SQLSessionFactory = ใหม่ SQLSessionFactoryBuilder (). build (inputStream); sqlsession = sqlsessionfactory.opensession (จริง); goodroomnight30daysmapper = sqlsession.getMapper (Goodroomnight30daysmapper.class); } @Test Public Void Test Test () {list <keyValue <Long, Integer >> records = new ArrayList <> (); records.add (KeyValue ใหม่ <ยาว, จำนวนเต็ม> (1725L, 5)); records.add (KeyValue ใหม่ <ยาว, จำนวนเต็ม> (1728L, 3)); records.add (KeyValue ใหม่ <Long, Integer> (1730L, NULL)); records.add (KeyValue ใหม่ <Long, Integer> (1758L, NULL)); int deleted = goodroomnight30daysmapper.deleteByExample (ใหม่ Goodsroomnight30daysexample ()); System.out.println ("----- ขนาดแถวที่ถูกลบ:" + ลบ); int row = goodroomnight30daysmapper.insertbatch (บันทึก); System.out.println ("----- แถวที่ได้รับผลกระทบ:" + แถว); รายการ <HotesRoomnight30 วัน> ผลลัพธ์ = goodroomnight30daysmapper.selectByexample (ใหม่ Goodsroomnight30 วันที่ตัวอย่าง ()); สำหรับ (รายการ Goodroomnight30 วัน: ผลลัพธ์) {system.out.println (item.toString ()); }} @after โมฆะสาธารณะหลังจาก () {ถ้า (sqlsession! = null) {sqlsession.close (); -ขอให้เป็นความลับอย่ามองลงไปก่อนคิดเกี่ยวกับสาเหตุของความผิดปกติ (นักเรียนที่มีความเชี่ยวชาญในการใช้แท็ก foreach ควรจะเห็นเบาะแส)
กระบวนการยกเว้นและการวิเคราะห์ข้อยกเว้น
ในโครงการเนื่องจากคลาสพารามิเตอร์และคลาสผลลัพธ์ของเมธอด DAO มักจะมีค่าที่สอดคล้องกับคีย์และคีย์เพื่อหลีกเลี่ยงการกำหนดคลาสซ้ำ ๆ ฉันได้กำหนดคลาสทั่วไป KeyValue ที่ใช้อินเทอร์เฟซ map.entry สำหรับรายละเอียดโปรดดูส่วนก่อนหน้า
วิธี GoodsRoomnight30daysMapper.insertBatch() ใช้คลาสทั่วไปนี้ หลังจากทำงานแล้วข้อมูลข้อยกเว้นที่กล่าวถึงในตอนต้นของบทความนี้จะถูกโยนลงไป
หลังจากเห็นข้อมูลข้อยกเว้นฉันมุ่งเน้นไปที่การสนับสนุนของ MyBatis สำหรับยาชื่อสามัญนั้นไม่ดีพอหรือไม่ ฉันถามเพื่อนร่วมงานของฉัน (@shengnan) และเพื่อนร่วมงานของฉันลองใช้มันบนเครื่องของเขาและพบว่าไม่มีความผิดปกติ นี่เป็นเรื่องแปลก หลังจากดูรหัสอย่างใกล้ชิดฉันพบว่าความแตกต่างคือคลาส KeyValue Generic ของฉันใช้อินเทอร์เฟซ Map.entry ในเวลานี้ฉันไม่ทราบคำแนะนำในแท็ก foreach บนเว็บไซต์ทางการ MyBatis:
วัตถุที่ทำซ้ำใด ๆ (เช่นรายการคอลเลกชัน ฯลฯ ) และพจนานุกรมใด ๆ หรือวัตถุอาเรย์สามารถส่งผ่านไปยัง foreach เป็นพารามิเตอร์การรวบรวม เมื่อใช้วัตถุหรืออาร์เรย์ที่วนซ้ำดัชนีคือจำนวนครั้งที่วนซ้ำและค่าของรายการคือองค์ประกอบที่ได้รับในการทำซ้ำนี้ เมื่อใช้พจนานุกรม (หรือคอลเลกชันของวัตถุแผนที่) ดัชนีคือคีย์และรายการคือค่า
ถัดไปตรวจสอบเหตุผลเฉพาะสำหรับข้อยกเว้นผ่านการดีบัก ดังนั้นฉันจึงใช้รหัสของคลาส KeyValue ที่ใช้อินเตอร์เฟส map.entry เพื่อแก้ไขข้อบกพร่อง ผ่านบันทึกข้อยกเว้นฉันจะเห็นได้ว่าข้อยกเว้นถูกโยนลงในบรรทัดค่าเริ่มต้น qlsession.java:200 ดังนั้นฉันจึงไปที่จุดพักเพื่อเริ่มต้นบรรทัดค่าเริ่มต้น เมื่อฉันดำเนินการกับ Line Foreachsqlnode.java:73 ในที่สุดฉันก็รู้ว่ามัน มาดูแผนภาพโซ่เรียกดีบักก่อน:
ลองดูที่รหัสเฉพาะ foreachsqlnode.java:73 (คลาสนี้เป็นคลาสโหนดของวัตถุแท็ก foreach):
ในเวลานี้สาเหตุเฉพาะของข้อยกเว้นนั้นชัดเจนมาก คลาสที่วัตถุ O ที่นี่เป็นของคลาส KeyValue เนื่องจากคลาส KeyValue ใช้อินเทอร์เฟซ map.entry, o อินสแตนซ์ map.entry เป็นจริง mybatis จึงกำหนดค่าคีย์ให้กับแอตทริบิวต์ดัชนีของ foreach และกำหนดค่าให้กับแอตทริบิวต์รายการ ที่นี่วัตถุจำนวนเต็มที่มีค่า 5 ถูกกำหนดให้กับแอตทริบิวต์รายการ ดังนั้นวัตถุที่สอดคล้องกันที่สอดคล้องกับแอตทริบิวต์รายการของแท็กที่เลือกด้วย InsertBatch ใน Goodsroomnight30daysmapper.xml ไม่มี item.key และ item.value แอตทริบิวต์ซึ่งเป็นสาเหตุที่ดีที่สุดของข้อยกเว้น
<insert id = "InsertBatch" parameterType = "list"> แทนที่เป็น goods_roomnight_30days (goods_id, checkin_room_night_30days) ค่า <foreach collection = "list" {index rexid {item.value}ข้างต้นเป็นการวิเคราะห์สั้น ๆ เกี่ยวกับเหตุผลว่าทำไมแท็ก mybatis foreach ถูกใช้อย่างไม่เหมาะสมและฉันหวังว่ามันจะเป็นประโยชน์กับคุณ หากคุณมีคำถามใด ๆ โปรดฝากข้อความถึงฉันและบรรณาธิการจะตอบกลับคุณทันเวลา ขอบคุณมากสำหรับการสนับสนุนเว็บไซต์ Wulin.com!