例外生成シナリオと例外情報
先週、Map.entryインターフェイスを実装する汎用クラスがMyBatisのMapperインターフェイス法で使用され、この方法に対応するSQLステートメントがForeachタグも使用したため、例外が発生しました。以下は例外情報です。
org.apache.ibatis.exceptions.persistenceException:###エラーデータベースの更新エラー。原因:org.apache.ibatis.reflection.ReflectionException:「class java.lang.integer 'に「キー」という名前のプロパティのゲッターはありません。 goods_roomnight_30days(goods_id、checkin_room_night_30days)values(?、?)、(?、(?)、(?、)、(?)、(?)、(?、)### cause:org.apache.ibatis.reflection.ReflectionExcept 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 com.sun.proxy sun.reflt.nativemethodaccessimpl.invoke0(ネイティブメソッド)sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:57)at sun.refllect.delegatingmethodaccessorimpl.invoke(delegatingmethaccessimpl.javapl.43)at java.lang.reflt.method.invoke(method.java:606)at org.junit.runners.model.frameworkmethod $ 1.runreflectivecall(frameworkmethod.java:47)at org.junit.internal.runners.model.relectivecallable.run(refrectivecallable.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) org.junit.runners.blockjunit4classrunner.runchild(blockjunit4classrunner.java:70)at org.junit.runners.blockjunit4classrunner.runchild(blockjunit4classrunner.java:50) org.junit.runners.parentrunner $ 3.run(parentrunner.java:238)at org.junit.runners.parentrunner $ 1.schedule(parentrunner.java:63)at org.junit.runners.runners.parentrunner.runchildran(parentrunner.java:236) 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 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(junitster.java:237)at com.intellij.rt.execution.junit.junitstarter.main(junitstarter.java:70)原因:org.apache.ibatis.ibatis.ReflectionException:「key」という名前のプロパティのgetter for 'class java.lang.integer' at a for property for " org.apache.ibatis.reflection.Reflector.getInvoker(Reflector.java:409)at org.apache.ibatis.reflection.metaclass.getInvoker(metaclass.java:164)at org.apache.ibatis.reflection.wrapper.beanwrapper.getbeanproperty(beanwrapper.java:162)at org.apache.ibatis.reflection.wrapper.get(beanwrapper.java:49)at org.apache.ibatis.reflection.metaobject.getvalue(metaobject.java:119)at org.apache.ibatis.mapping.mapping.boundsql.getadddddddddddddddddddddditionalparamtr.java(boundsqlamation) org.apache.ibatis.scripting.defaults.defaultparameterhandler.setParameters(defaultParameterhandler.java:72)at org.apache.ibatis.executor.statement.preparedStatementementementementementmenter.parameterize(preatedstatementhandler.java:93)at org.apache.ibatis.executor.statement.routingstatementhandler.parameterize(routingstatementhandler.java:64)at org.apache.ibatis.executor.simplexecutor.preparestatement(SimpleExecutor.java:86) 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詳細
MyBatisについてはあまり知らないので、異常の特定の原因をデバッグして見つけるのに長い時間がかかりました。
次に、例外を再現し、例外の原因を分析します。
例外が再び現れます
上記の例外を再現するために、デモが書かれました。関連するコードは次のとおりです。
データベーステーブル構造:
テーブルの作成 `goods_roomnight_30days`(` goods_id` bigint(20)not null、 `checkin_room_night_30days` int(11)not null default '' ''コメント '最後の30日間の夜'、プライマリキー(` goods_id`))エンジン= innodbデフォルトcharset = utf8コメント
keyvalue.javaパラメータークラス:
public class keyvalue <k、v>はmap.entry <k、v> {private k key;プライベートV値。 publy keyvalue(){} public keyvalue(k key、v value){this.key = key; this.value = value; } @Override public k getKey(){return key; } @Override public v getValue(){return値; } @Override public v setValue(v value){v oldvalue = this.value; this.value = value; OldValueを返します。 } public jsonobject tojsonobject(){Reportjsonobject.newobject()。append(string.valueof(key)、value); } @Override public string toString(){return tojsonobject()。tojsonstring(); }}DAOクラスGoodsmightroomnight30daysmapper.java
パブリックインターフェイスgoodsoomnightmightmapper {int deletebyexample(goodsroomnight30daysexampleの例);リスト<Goodsoormignynight30days> selectbyexample(goodsroomnight30daysexampleの例); <k、v> int insertBatch(list <keyvalue <k、v >>レコード);}mybatis-config.xmlファイル:
<?xml version = "1.0" encoding = "utf-8"?> <!doctype構成public " - // mybatis.org//dtd config 3.0 // en" "http://mybatis.org/dtd/mybatis-3-config.dtd" < /settings> <! - springと統合した後、環境構成は廃止され、spring管理に引き渡されます - > <環境デフォルト= "開発"> <環境id = "development"> <! - JDBCトランザクション管理 - > <トランザクションマネージャータイプ= "JDBC" /> <! - データベース接続プールname = "driver" value = "com.mysql.jdbc.driver" /> <プロパティname = "url" value = "jdbc:" jdbc:// localhost:3306 /hotel_report?charaterencoding = utf-8 " /> <プロパティname =" username "value =" test_user " /> <propation =" fallic </Environment> </環境> <マッパーズ> <マッパーリソース= "mybatis/goodsroomnight30daysmapper.xml"/> </mappers> </configuration>
goodsroomnight30daysmapper.xmlファイルの主なコンテンツ:
<ID = "INSERT INSERTBATCH" parameterType = "list"> goods_roomnight_30days(goods_room_night_30days)値に置き換えます< null ">(#{item.key}、0)</when> - > <! - <when test =" item.value!= null ">(#{item.key}、#{item.value})</> <!上記は、この例外を再現するための主要なコードです。完全なコードは、github:https://github.com/misterzhou/java-demo/tree/master/test-spring/spring-serverで表示できます
例外を再現するテストコードgoodsoomnight30daystest.java:
Package org.guojing.test.spring.server; Import org.apache.ibatis.io.iosources; import org.apache.ibatis.session.squache.ibatis.session.session.ssession.sclsessionFactory; import org.apache.ibatis.session.session.session.session.session.session.session.session.session.session.session.session.session.session.session. 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.ioexception; Import.Intio.io.io.io.io.io.io.io.io.io.io.io.io.io.io.io.io.io.ioputeexction java.util.list;/** *作成:2016-12-24 * * @author guojing */public class goodsoomnight30daystest {sqlsessionfactory sqlsessionfactory; sqlsession sqlsession; Goodsroomnight30daysmapper goodsroomnightmightmapper; @before public void init()throws ioexception {string resource = "mybatis/mybatis-config.xml"; inputstream inputstream = resources.getResourceasStream(リソース); 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 goodsoomnight30daysexample()); System.out.println( "-----削除された行サイズ:" +削除); int row = goodsroomnight30daysmapper.insertbatch(レコード); System.out.println( "-----影響を受けた行:" + row); List <GoodsionMight30Days> result = goodsroomnight30daysmapper.selectbyexample(new goodsoomnight30daysexample()); for(goodsroomnight30days item:result){system.out.println(item.tostring()); }} @after public void after(){if(sqlsession!= null){sqlsession.close(); }}}それを秘密にしておき、最初に見下ろさないで、異常の原因について考えてみましょう(Foreachタグを使用するのに熟練している学生は、手がかりを見ることができるはずです)。
例外プロセスと例外分析
プロジェクトでは、DAOメソッドのパラメータークラスとリターンの結果クラスには、キーとキーに対応する値が含まれているため、クラスを繰り返し定義することを避けるために、Map.entryインターフェイスを実装するキーValue genericクラスを定義しました。詳細については、前のセクションをご覧ください。
GoodsRoomnight30daysMapper.insertBatch()メソッドは、この汎用クラスを使用します。実行後、この記事の冒頭で言及された例外情報がスローされます。
例外情報を確認した後、Mybatisのジェネリックに対するサポートが十分ではないかどうかに焦点を当てました。私は同僚(@shengnan)に尋ねました、そして、私の同僚は彼のマシンでそれを試してみて、異常がないことを発見しました。これは奇妙です。コードを詳しく見た後、違いは私のキーValue汎用クラスがMap.entryインターフェイスを実装することであることがわかりました。現時点では、MyBatisの公式WebサイトでのForeachタグの説明がわかりません。
反復可能なオブジェクト(リスト、コレクションなど)および辞書または配列オブジェクトは、コレクションパラメーターとしてforeachに渡すことができます。反復可能なオブジェクトまたは配列を使用する場合、インデックスは反復回数であり、アイテムの値はこの反復で得られる要素です。辞書(またはmap.entryオブジェクトのコレクション)を使用する場合、インデックスはキーであり、アイテムは値です。
次に、デバッグを使用して例外の特定の理由を確認します。そこで、最初にMap.entryインターフェイスをデバッグするために実装するKeyValueクラスのコードを使用しました。例外ログを介して、例外がdefaultsqlsession.java:200にスローされていることがわかります。したがって、ブレークポイントを押してdefaultsqlsession.java:197をラインし、段階的に実行しました。 foreachsqlnode.java:73をforeachsqlnode.java:73に実行したとき、私は最終的にそれを実現しました。最初にデバッグコールチェーン図を見てみましょう。
eachsqlnode.java:73の特定のコードを見てみましょう(このクラスは、foreachタグオブジェクトのノードクラスです):
現時点では、例外の特定の原因は非常に明白です。ここのOオブジェクトが属するクラスは、KeyValueクラスです。 KeyValueクラスはMap.Entryインターフェイスを実装するため、oインスタンスMap.entryがTrueであるため、MyBatisはforeachのインデックス属性にキー値を割り当て、値をアイテム属性に割り当てます。ここで、値が5の整数オブジェクトがアイテム属性に割り当てられます。したがって、Goodsroomnightnight30daysmapper.xmlの挿入バッチを使用したselectタグのアイテム属性に対応する対応するオブジェクトには、item.keyとitem.value属性がありません。これは例外の最終的な原因です。
<ID = "INSERT INSERTBATCH" PARAMETERTYPE = "LIST"> goods_roomnight_30days(goods_room_night_30days)値に置き換えます<
上記は、Mybatis foreachタグが不適切に使用されている理由の簡単な分析であり、それがあなたに役立つことを願っています。ご質問がある場合は、メッセージを残してください。編集者は時間内に返信します。 wulin.comのウェブサイトへのご支援ありがとうございます!