예외 생성 시나리오 및 예외 정보
지난주 MAP.entry 인터페이스가 MyBatis의 Mapper 인터페이스 메소드에 사용 되었고이 메소드에 해당하는 SQL 문도 Foreach 태그를 사용했기 때문에 예외가 발생했습니다. 다음은 예외 정보입니다.
org.apache.ibatis.exceptions.persistenceexception : ### 오류 업데이트 데이터베이스. 원인 : org.apache.ibatis.reflection.reflectionexception : 'class java.lang.integer'에서 'key'라는 속성에 대한 getter가 없습니다. goods_roomnight_30days (goods_id, checkin_room_night_30days) 값 (?,?), (?), (?), (?), (?), (?),?),?),?) org.apache.ibatis.exceptions.exceptionfactory.wrapexception (org.apache.ibatis.session.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) org.apache.ibatis.binding.mapperproxy.invoke (com.sun.proxy. $ proxy4.insertbatch (알 수없는 출처) org.guojing.test.spring.server.goodsroomnightnightstest.test.test.java:47) sun.reflect.nativeMethodaccessorimpl.invoke0 (기본 메소드)에서 sun.reflect.nativeMethodaccessorimpl.invoke (nativeMeThodAccessorImpl.java:57)에서 sun.reflect.delegatingmethodaccessorimpl.invoke (at at at at at at at atmethodoccestorimpl.4) 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 (recintivecallable.java:12) at org.junit.runners.model.frameworkmethod.invokeExplosial (frameworkmethod.java:44) at org.junit.runners.statements.invokemethod.evaluate (invokemethod.java:17) at org.junit.internal.runners.runners.runbefores.evaluate (runbefores.java:26) at org.junit.internal.runners.statements.runafters.evaluate (runafters.java:27) at org.junit.runners.runlef (parentrunner.java:271) at org.junit.runners.blockjunit4classrunner.runchild (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.junits.runners.parentrunner.runchildren (Parentrunner.java:236) at org.junit.runners.ccacess $ 000 (parentrunner.java:53) at org.junit.runners.parentrunner $ 2.evaluate (parentrunner.java:229) at org.junit.runners.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) : org.apache.ibatis.reflection.reflectionexception : 'class java.lang.integer'에서 'key'라는 속성에 대한 getter는 없습니다. org.apache.ibatis.reflector.getgetInvoker (reclector.java:409) at org.apache.ibatis.reflection.reflection.getgetInvoker (metaclass.java:164) org.apache.abatis.wlector.beanwrapper.getBeanbean62) at Beanwloper.16) org.apache.ibatis.reflection.wrapper.beanwrapper.get (beanwrapper.java:49) at org.apache.ibatis.reflection.metaobject.getvalue (metaobject.java:122) at org.apache.ibatis.reflect.metaobulege (metaobject.java:119) at org.apache.ibatis.boundsql.getAdditionalParameter (boundsql.java:75) at org.apache.ibatis.scripting.defaults.defaultParameterAndler.setParameters (defaultParameterHandler.java:72) at org.apache.ibatis.executor.statement.preparedStatementhandler.preparedStatementhandler.parameterize (repaystatementhandler.java:93) at org.apache.ibatis.executor.statement.routingstatement handler.parameterize (loutingstatementhandler.java:64) at org.apache.ibatis.executor.simpleexecutor.preparestatement (simplexecutor.java:86) at org.apache.ibatis.executor.simpleexecutor.doupdate (simplexecutor.java:49) at org.apache.ibatis.executor.baseexecutor.update (org.apache.session.defaults.defaultsqlsession.update (defaultsqlsession.java:198) ... 29 more
Mybatis에 대해 많이 알지 못하기 때문에 비정상의 특정 원인을 디버깅하고 찾는 데 오랜 시간이 걸렸습니다.
다음으로 예외를 재현하고 예외의 원인을 분석하겠습니다.
예외가 다시 나타납니다
위의 예외를 재현하기 위해 데모가 작성되었습니다. 관련 코드는 다음과 같습니다.
데이터베이스 테이블 구조 :
테이블 만들기`goods_roomnight_30days` (`goods_id` bigint (20) null, null,`checkin_room_night_30days` int (11) not null default '0'댓글 '마지막 30 일 밤', 1 차 키 (`goods_id`))))) 엔진 = innodb 기본 charset = utf8 댓글 = 'worder' '
keyValue.java 매개 변수 클래스 :
공개 클래스 keyvalue <k, v> implements map.entry <k, v> {private k key; 개인 V 값; public keyvalue () {} public keyvalue (k key, v value) {this.key = key; this.value = value; } @override public k getkey () {return 키; } @override public v getValue () {return value; } @override public v setValue (v value) {v OldValue = this.value; this.value = value; OldValue를 반환하십시오. } public jsonobject tojsonobject () {return reportJsonObject.newObject (). Append (string.Valueof (키), 값); } @override public String toString () {return tojsonObject (). tojsonstring (); }}Dao Class goods Roomnight30daysmapper.java
공개 인터페이스 goods roomnight30daysmapper {int deletebyexample (goodsroomnight30daysexample 예); 목록 <goodsroomnight30days> selectByexample (goodsroomnight30daysexample 예); <k, v> int insertbatch (list <keyvalue <k, v >> 레코드);}mybatis-config.xml 파일 :
<? xml version = "1.0"alcoding = "utf-8"?> <! doctype configuration public "-// mybatis.org//dtd config 3.0 // en" "http://mybatis.org/dtd/dtd/mybatis-3-config.dtd "> configuration> <value ="sings> < /settings> <-스프링과의 통합 후 환경 구성이 폐지되어 스프링 관리에 폐지되어-> <환경 기본값 = "개발"> <환경 ID = "Development"> <!-JDBC 트랜잭션 관리-> <TransactionManager type = "JDBC" /> <!-데이터베이스 연결 풀이, 세 번째 파트 타이티에 사용됩니다. <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"= "user123" />>. </환경> </환경> <mappers> <Mapper Resource = "MyBatis/goodsroomnight30daysmapper.xml"/> </mappers> </configuration>
goodsroomnight30daysmapper.xml 파일의 주요 내용 :
<insertbatch "parametertype ="list "> goods_roomnight_30days (goods_id, checkin_room_night_30days) 값 <foreach collection ="list "index"item = "item"separator = ","> ( #{item.key}, #{item.value}) <!-<!-< ">-<선택. null "> (#{item.key}, 0) </shen>-> <!-<test ="item.value! = null "> (#{item.key},#{item.value}) </shen> <!-</선택>-> </foreach> </insert>위는이 예외를 재현하기위한 주요 코드입니다. 전체 코드는 github : https://github.com/misterzhou/java-demo/tree/master/test-spring/spring-server에서 볼 수 있습니다
예외 goodsroomnight30daystest.java를 재현하는 테스트 코드 :
Package org.guojing.spring.server; import org.apache.ibatis.io.resources; import org.apache.ibatis.session.sqlsession; import org.apache.ibatis.session.sqlsession org.apache.ssession.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.io exception; import java.io.inputstrem; java.util.list;/** * 생성 : 2016-12-24 * * @Author Guojing */public class roomnight3030daystest {sqlsessionFactory sqlsessionFactory; sqlsession sqlsession; goodsroomnight30daysmapper goodsroomnight30daysmapper; @public void init ()가 ioexception {string resource = "mybatis/mybatis-config.xml"; inputStream inputStream = resources.getResourCeasStream (resource); sqlsessionFactory = new SqlSessionFactoryBuilder (). 빌드 (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 ( "----- 삭제 된 행 크기 :" + 삭제); int row = goodsroomnight30daysmapper.insertbatch (레코드); System.out.println ( "----- 영향을받은 행 :" + 행); 목록 <goodsroomnight30days> result = goodsroomnight30daysmapper.selectbyexample (new goodsroomnight3030daysexample ()); for (goodsroomnight30days item : result) {system.out.println (item.toString ()); }} @after public void apont () {if (sqlsession! = null) {sqlsession.close (); }}}비밀을 유지하고 먼저 내려다 보지 말고 이상의 원인에 대해 생각해 보겠습니다 (Foreach 태그를 사용하는 데 능숙한 학생들은 단서를 볼 수 있어야합니다).
예외 프로세스 및 예외 분석
프로젝트에서 DAO 메소드의 매개 변수 클래스 및 리턴 결과 클래스에는 종종 클래스를 반복적으로 정의하지 않기 위해 키 및 키에 해당하는 값이 종종 포함되어 있으므로 맵. 엔트리 인터페이스를 구현하는 KeyValue 일반 클래스를 정의했습니다. 자세한 내용은 이전 섹션을 참조하십시오.
GoodsRoomnight30daysMapper.insertBatch() 메소드는이 일반 클래스를 사용합니다. 실행 후이 기사의 시작 부분에서 언급 된 예외 정보가 발생합니다.
예외 정보를 본 후, 나는 제네릭에 대한 Mybatis의 지원이 충분하지 않은지에 중점을 두었습니다. 나는 동료 (@shengnan)에게 물었고, 내 동료는 그의 기계에서 그것을 시도해 비정상이 없다는 것을 알았습니다. 이것은 이상합니다. 코드를 자세히 살펴본 후 차이점은 내 keyValue Generic 클래스가 MAP.entry 인터페이스를 구현한다는 것입니다. 현재 Mybatis 공식 웹 사이트의 Foreach 태그에 대한 지침을 모르겠습니다.
반복 가능한 객체 (예 : 목록, 컬렉션 등) 및 모든 사전 또는 배열 개체를 수집 매개 변수로 Foreach로 전달할 수 있습니다. 반복 가능한 객체 또는 배열을 사용하는 경우 색인은 반복 된 횟수이며 항목 값은이 반복에서 얻은 요소입니다. 사전 (또는지도 모음 모음)을 사용하는 경우 색인이 키이며 항목은 값입니다.
다음으로 디버그를 통해 예외의 특정 이유를 확인하십시오. 그래서 먼저 Map.entry 인터페이스를 구현하는 KeyValue 클래스의 코드를 디버그로 사용했습니다. 예외 로그를 통해 예외가 Defaultsqlsession.java:200으로 줄을 획득 한 것을 알 수 있으므로 Defaultsqlsession.java:197 라인으로 중단 점을 누른 다음 단계별로 실행했습니다. oreachsqlnode.java:73 라인으로 실행했을 때 마침내 그것을 깨달았습니다. 먼저 디버그 통화 체인 다이어그램을 살펴 보겠습니다.
특정 코드 foreachsqlnode.java:73을 살펴 보겠습니다 (이 클래스는 Foreach 태그 개체의 노드 클래스입니다) :
현재 예외의 특정 원인은 매우 분명합니다. 여기에 O 객체가 속한 클래스는 KeyValue 클래스입니다. keyValue 클래스는 맵을 구현하기 때문에 entry 인터페이스, o 인스턴스 맵. 엔트리는 true, myBatis는 key 값을 foreach의 인덱스 속성에 할당하고 값을 항목 속성에 할당합니다. 여기서 5 값의 정수 객체는 항목 속성에 할당됩니다. 따라서 goodsroomnight 30daysmapper.xml에 insertbatch가있는 선택 태그의 항목 속성에 해당하는 해당 객체에는 item.key 및 item.value 속성이 없습니다. 이는 예외의 궁극적 원인입니다.
<insertbatch "parametertype ="list "> goods_roomnight_30days로 바꾸기
위의 것은 Mybatis foreach 태그가 부적절하게 사용되는 이유에 대한 간단한 분석이며, 그것이 당신에게 도움이되기를 바랍니다. 궁금한 점이 있으면 메시지를 남겨 주시면 편집자가 제 시간에 답장을 드리겠습니다. Wulin.com 웹 사이트를 지원해 주셔서 대단히 감사합니다!