Scénario de génération d'exception et informations d'exception
La semaine dernière, une exception s'est produite parce que la classe générique qui implémente l'interface MAP.Entry a été utilisée dans la méthode d'interface du mappeur de MyBatis, et l'instruction SQL correspondant à cette méthode a également utilisé la balise foreach. Ce qui suit est les informations d'exception:
org.apache.ibatis.exceptions.persistenceException: ### Error Upding Database. Cause: org.apache.ibatis.reflection.ReflectionException: il n'y a pas de getter pour la propriété nommée 'Key' dans 'class java.lang.integer' ### L'erreur peut impliquer org.guojing.test.spring.server.GoodSroomnight30Daysmapper.insertbatch-in-in-in-infig bourse_roomnight_30days (biens_id, checkin_room_night_30days) VALEURS (?,?), (?,?), (?,?), (?,?), (?,?) ### Cause: org.apache.ibatis.reflection.reflectionxception: il n'y a pas de getter pour la propriété nom org.apache.ibatis.exceptions.exceptionfactory.wrapexception (exceptionfactory.java:30) sur org.apache.ibatis.session.defaults.defaultsqlSession.update (defaultSqlSession.java:200) à l'affaire org.apache.ibatis.session.defaults.defaultsqlSession.insert (defaultSqlSession.java:185) à org.apache.ibatis.binding.mappermethod.execute (MapperMethod.java:57) at org.apache.ibatis.binding.mapperproxy.invoke (mapperproxy.java:53) sur com.sun.proxy. $ proxy4.insertbatch (source inconnue) sur org.guojing.test.spring.server.goodsroomnight30days.Test (biens Sun.Reflect.NativeMethodAccessOrimpl.invoke0 (méthode native) à Sun.reflect.NativeMethodAccessOrimp.invoke (Nativemethodaccessorimpempl.java:57) à Sun.Reflect.delegatingMethodaccessorimp.invoke (DelégatingMethodAccessorImml java.lang.reflect.method.invoke (method.java:606) sur org.junit.runners.model.frameworkMethod 1 $ org.junit.internal.runners.model.reflectivecallable.run (réflevisalable.java:12) à org.junit.runners.model.frameworkmethod.invokeExplosively (frameworkmethod.java:44) à at org.junit.internal.runners.statements.invokemethod.evaluate (invokemethod.java:17) à org.junit.internal.runners.statements.runbefores.evaluate (Runbefes.java:26) à l'adresse 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) at org.junit.runners.parentrunner 3,Run (parentrunner.java:238) sur org.junit.runners.parentrunner $. org.junit.runners.parentrunner.access $ 000 (parentrunner.java:53) sur org.junit.runners.parentrunner $ evaluate (parentrunner.run (229) à org.junit.runners.parentrunner.run (parintrunner.java:309) sur org.junit.runner.junitcore.run (Junitcore.java:160) à com.intellij.junit4.junit4ideatestrunner.startrunnerwithargs (Junit4Ideatestrunner.java:68) à la com.intellij.rt.execution.junit.ideatestrunner $ repeater.startrunnerwithargs (ideatestrunner.java:51) à com.intellij.rt.execution.junit.junitstarter.prearestreamsandstart (JunitStarter.java:237) at com.intellij.rt.execution.junit.junitstarter.main (Junitstarter.java:70) Cause par: org.apache.ibatis.reflection.reflectionException: il n'y a pas de gette org.apache.ibatis.reflection.reflector.getGetInvoker (réflector.java:409) sur org.apache.ibatis.reflection.Metaclass.getgetInvoker (metaclass.java:164) sur org.apache.ibatis.reflection.wrupper.BeanWrapper.getBeanproperty (beanwapper.java:Bapper.Beanwrapper.getBeanproperty (Beanwapper.Java:162) 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 (boussql.java:75) sur org.apache.ibatis.scripting.defaults.defaultParametHandler.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) à org.apache.ibatis.executor.simpleexecutor.doupDate (SimpleExecutor.Java:49) à AT at org.apache.ibatis.executor.baseexecutor.update (BaseExEcutor.java:117) sur org.apache.ibatis.session.defaults.defaultsqlSession.update (DefaultsqlSession.java:198) ... 29 plus
Parce que je ne sais pas grand-chose sur Mybatis, il m'a fallu beaucoup de temps pour déboguer et découvrir la cause spécifique de l'anomalie.
Ensuite, je reproduire l'exception et analyserai les causes de l'exception.
L'exception réapparaît
Afin de reproduire l'exception ci-dessus, une démo a été écrite. Les codes pertinents sont les suivants:
Structure de la table de la base de données:
Créer un tableau `bourse_roomnight_30days` (` biens_id` bigint (20) pas null, `checkin_room_night_30days` int (11) pas null '' 0 'comment' Night de 30 jours ', clé primaire (` bourse_id`)) moteur = innondb default charset = utf8 comment =' woods 'nuit de 30 jours' '
Classe de paramètres keyvalue.java:
classe publique KeyValue <k, v> implémente map.entry <k, v> {key private k; valeur en V privée; public keyValue () {} public keyValue (k key, v valeur) {this.key = key; this.value = valeur; } @Override public k getkey () {return key; } @Override public v getValue () {return Value; } @Override public v setValue (V valeur) {v OldValue = this.Value; this.value = valeur; Retour OldValue; } public jsonObject tojsonObject () {return reportJSonObject.newObject (). APPEND (String.ValueOf (key), valeur); } @Override public String toString () {return tojsonObject (). TojSontring (); }}Dao Class Goodsroomnight30Daysmapper.java
Interface publique Goodsroomnight30DaySmapper {int DeleteByExample (Exemple de Goodsroomnight30DaySexample); Liste <Goodsroomnight30Days> SelectByExample (Exemple de Goodsroomnight 30DaySexample); <K, v> int insertbatch (list <keyvalue <k, v >> enregistre);}Fichier 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-3config.dtd"> <configuration> <paramètres> </ Settings> <! - Après l'intégration avec Spring, la configuration des environnements sera abolie et remise à Spring Management -> <Environments Default = "Development"> <Environment ID = "Development"> <! - Utilisez JDBC Transaction Management -> <TransactionManager Type = "JDBC" /> <! name = "driver" value = "com.mysql.jdbc.driver" /> <propriété name = "url" value = "jdbc: mysql: // localhost: 3306 / hotel_report? CaractoCcoding = utf-8" /> <propriété name = "username" value = "test_user" /> <propriété name = "mot de passe" value = "user123" /> <daton> </ Environment> </ Environments> <Mappers> <Mapper Resource = "MyBatis / Goodsroomnight30DaySmapper.xml" /> </mappers> </FIGIGRAGE>
Le contenu principal du fichier de biens de Goodsroomnight30daysmapper.xml:
<insert id = "insertbatch" ParameterType = "list"> Remplacer en valeurs de biens_roomnight_30days (boyages_id, checkin_room_night_30days) <foreach collection = "list" index = "index" item = "item" séparateur = ","> (# {item.key}, # {item.value}) <! null "> (# {item.key}, 0) </ quand> -> <! - <quand test =" item.value! = null "> (# {item.key}, # {item.value}) </ when> -> <! - </ Choose> -> </ ForEach> </ insert>Ce qui précède est le code principal pour reproduire cette exception. Le code complet peut être consulté sur github: https://github.com/misterzhou/java-demo/tree/master/test-spring/spring-server
Code de test qui reproduit l'exception Goodsroomnight30DayStrest.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.util.utillist; import; java.util.list; / ** * Créé au: 2016-12-24 * * @author guojing * / public class goodsroomnight30daystest {SqlSessionFactory sqlSessionFactory; SQLSESSE SQLSESSION; Goodsroomnight30DaySmapper Goodsroomnight30DaySmapper; @Before public void init () lève ioException {String Resource = "MyBatis / Mybatis-Config.xml"; InputStream inputStream = Resources.getResourceSstream (ressource); sqlSessionFactory = new SQLSessionFactoryBuilder (). Build (InputStream); sqlSession = sqlSessionFactory.OpenSession (true); Goodsroomnight30DaysMapper = SQLSESSESS.GetMapper (Goodsroomnight30DaySmapper.class); } @Test public void test () {list <keyValue <long, Integer >> record = new ArrayList <> (); disques.add (new KeyValue <long, entier> (1725L, 5)); disques.add (new KeyValue <long, entier> (1728l, 3)); Records.Add (new KeyValue <long, entier> (1730L, null)); disques.add (new KeyValue <long, entier> (1758l, null)); int supprimé = Goodsroomnight30DaySmapper.DeleteByExample (new Goodsroomnight30DaySexample ()); System.out.println ("----- Taille de la ligne supprimée:" + supprimé); int row = gootroomnight30daysmapper.insertbatch (disques); System.out.println ("----- Row affecté:" + ligne); Liste <Goodsroomnight30Days> Résultat = Goodsroomnight30DaySmapper.SelectByExample (new Goodsroomnight30DaySexample ()); pour (Goodsroomnight30Days Article: Résultat) {System.out.println (item.toString ()); }} @After public void after () {if (sqlSession! = Null) {sqlSession.close (); }}}Gardons-le secret, ne regardez pas d'abord, réfléchissez aux causes de l'anomalie (les étudiants qui compétent pour utiliser le tag foreach devraient être en mesure de voir les indices).
Processus d'exception et analyse des exceptions
Dans le projet, puisque la classe de paramètres et la classe de résultats de retour de la méthode DAO contiennent souvent une valeur correspondant à la clé et à la clé, afin d'éviter de définir à plusieurs reprises la classe, j'ai défini une classe générique KeyValue qui implémente l'interface Map.Entry. Pour plus de détails, veuillez consulter la section précédente.
GoodsRoomnight30daysMapper.insertBatch() utilise cette classe générique. Après l'exécution, les informations d'exception mentionnées au début de cet article sont lancées.
Après avoir vu les informations d'exception, je me suis concentré sur la question de savoir si le support de Mybatis pour les génériques n'est pas suffisant. J'ai demandé à mon collègue (@shengnan), et mon collègue l'a essayé sur sa machine et j'ai découvert qu'il n'y avait pas d'anomalie. C'est étrange. Après un examen plus approfondi du code, j'ai trouvé que la différence est que ma classe générique KeyValue implémente l'interface map.entry. À l'heure actuelle, je ne connais pas la description de la balise Foreach sur le site officiel de MyBatis:
Tout objet itérable (tel que les listes, les collections, etc.) et tout dictionnaire ou objet Array peuvent être transmis à ForEach comme paramètres de collecte. Lorsque vous utilisez un objet ou un tableau itérable, l'index est le nombre de fois itéré et la valeur de l'élément est l'élément obtenu dans cette itération. Lorsque vous utilisez un dictionnaire (ou une collection d'objets Map.Entry), l'index est la clé et l'élément est la valeur.
Ensuite, vérifiez les raisons spécifiques de l'exception via le débogage. J'ai donc d'abord utilisé le code de la classe KeyValue qui implémente l'interface map.entry pour déboguer. Grâce au journal d'exception, je peux voir que l'exception est lancée en ligne defaultSqlSession.java:200, donc j'ai appuyé sur le point d'arrêt pour la ligne defaultSqlSession.java:197, puis je l'ai exécuté étape par étape. Lorsque je l'ai exécuté pour tapisser foreachsqlnode.java:73, je l'ai finalement réalisé. Jetons un coup d'œil au diagramme de la chaîne d'appels de débogage d'abord:
Jetons un coup d'œil au code spécifique foreachsqlnode.java:73 (cette classe est la classe de nœud de l'objet ForEach Tag):
À l'heure actuelle, la cause spécifique de l'exception est très évidente. La classe à laquelle appartient l'objet O ici est la classe KeyValue. Étant donné que la classe KeyValue implémente l'interface Map.Entry, o Instance Map.Entry est vrai, MyBatis attribue la valeur de clé à l'attribut d'index de ForEach et attribue la valeur à l'attribut d'élément. Ici, l'objet entier avec une valeur de 5 est attribué à l'attribut d'élément. Par conséquent, l'objet correspondant correspondant à l'attribut d'article de la balise de sélection avec InsertBatch dans Goodsroomnight30Daysmapper.xml n'a pas les attributs de l'élément.key et item.Value, qui est la cause ultime de l'exception.
<insert id = "insertbatch" ParameterType = "list"> Remplacer en valeurs de biens_roomnight_30days (biens_id, checkin_hroom_night_30days) <foreach collection = "list" index = "index" item = "item" séparateur = ","> (# {itet.key}, # {item.value}) </ofoeach> </ insert>Ce qui précède est une brève analyse des raisons pour lesquelles la balise MyBatis Foreach est mal utilisée, et j'espère que cela vous sera utile. Si vous avez des questions, veuillez me laisser un message et l'éditeur vous répondra à temps. Merci beaucoup pour votre soutien au site Web Wulin.com!