Escenario de generación de excepciones e información de excepción
La semana pasada, se produjo una excepción porque la clase genérica que implementa la interfaz MAP.Entry se utilizó en el método de la interfaz mapper de MyBatis, y la instrucción SQL correspondiente a este método también utilizó la etiqueta foreach. La siguiente es la información de excepción:
org.apache.ibatis.exceptions.PersistenceException: ### Error de actualización de la base de datos. Causa: org.apache.ibatis.Reflection.ReflectionException: No hay un getter para la propiedad llamado 'clave' en 'clase java.lang.integer' ### El error puede involucrar org.guojing.test.spring.server.goodsroomnight30daysmapper.insertbatch-inline ### el error ocurrido mientras se establece los parámetros###################### ## ## ## ##### ##### ##### ###### ## ######### ######### ##### ##### ##### ## 3 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 (excepcionFactory.java:30) en org.apache.ibatis.session.default.defaultsqlsession.update (defaultsqlsession.java:200) AT org.apache.ibatis.session.default.defaultsqlsession.insert (defaultsqlsession.java:185) en org.apache.ibatis.binding.mappermethod.execute (mappermethod.java:57) AT org.apache.ibatis.binding.mapperproxy.invoke (mapperproxy.java:53) en com.sun.proxy. $ proxy4.insertbatch (fuente desconocida) Sun.Reflect.nativemethodacCessorImpl.invoke0 (método nativo) en Sun.reflect.nativemethodaccessorImpl.invoke (nativemethodaccessorImpl.Java:57) en Sun.reflect.delegatingMethodaccessImpl.invoke (delegando a Methodaccessorsimpul.Java:43) java.lang.reflect.method.invoke (método.java:606) en org.junit.runners.model.frameworkmethod $ 1.runreflectivecall (frameworkmethod.java:47) en org.junit.internal.runners.model.reflectivecallable.run (reflectivecallable.java:12) en org.junit.runners.model.frameworkmethod.invokeExplossInty (frameworkMethod.Java:44) en org.junit.internal.runners.statements.invokemethod.evaluate (invokemethod.java:17) en org.junit.internal.runners.statements.runbefefefefefefefefefores org.junit.internal.runners.statements.runafters.evaluate (ranafters.java:27) en org.junit.runners.parentrunner.runleaf (parentrunner.java:271) en org.junit.runners.blockjunit4classrunner.runchild (blockjunit4classrunner.java:70) en org.junit.runners.blockjunit4classrunner.runchild (blockjunit4classrunner.java:50) en org.junit.runners.parentrunner $ 3.run (parentrunner.java:238) en org.junit.runners.parentrunner $ 1.schedule (parentrunner.java:63) en org.junit.runners.parentrunner.runchildren (parentrunner.java:236) org.junit.runners.parentrunner.access $ 000 (parentrunner.java:53) en org.junit.runners.parentrunner $ 2.evaluate (parentrunner.java:229) en org.junit.runners.parentrunner.run (parentrunner.java:309) AT org.junit.runner.junitcore.run (Junitcore.java:160) en com.intellij.junit4.junit4ideAtreatestrunner.startrunnerwithargs (Junit4ideAtestrunner.Java:68) AT com.intellij.rt.execution.junit.ideAteatestRunner $ repitador.startrunnerwithargs (ideatestrunner.java:51) en com.intellij.rt.execution.junit.junitstarter.preparestreamsandstart (junitstarter.Java:237) AT com.intellij.rt.execution.junit.junitstarter.main (junitstarter.java:70) causado por: org.apache.ibatis.reflection.reflectionException: no hay un captador para la propiedad llamado 'clave' en la clase java.lang.lang.integer 'at d. org.apache.ibatis.reflection.reflector.getgetInvoker (reflector.java:409) en org.apache.ibatis.reflection.metaclass.getgetinvoker (metaclass.java:164) en org.apache.ibatis.reflection.wrapper.beanwrapper.getBeanProperty (beanwrapper.java:162) en org.apache.ibatis.reflection.wrapper.beanwrapper.get (beanwrapper.java:49) AT org.apache.iBatis.Reflamection.MetaObject.getValue (metaObject.Java:122) en org.apache.Ensflection.MetaObject.getValue (metaObject.Java:119) en org.apache.ibatis.mapping.BoundSql.getadditionParameter (límites (límite) org.apache.ibatis.scripting.default.defaultparameterhandler.setParameters (defaultParameterHandler.Java:72) en org.apache.ibatis.executor.statement.PreparedStatementHandler.Parameterize (preparado StatementHandler.Java:93) en org.apache.ibatis.executor.statement.routingStatementHandler.Parameterize (RoutingStatementHandler.Java:64) en org.apache.ibatis.executor.simpleexecutor.preparestatement (simplizexecutor.java:86) en org.apache.ibatis.executor.simpleexecutor.doupdate (simplizexecutor.java:49) en org.apache.ibatis.executor.baseExecutor.update (baseexecutor.java:117) a org.apache.ibatis.session.defaults.defaultsqlsession.update (defaultsqlsession.java:198) ... 29 más
Debido a que no sé mucho sobre mybatis, me llevó mucho tiempo depurar y descubrir la causa específica de la anormalidad.
A continuación, reproduciré la excepción y analizaré las causas de la excepción.
Reaparece la excepción
Para reproducir la excepción anterior, se escribió una demostración. Los códigos relevantes son los siguientes:
Estructura de la tabla de la base de datos:
Crear tabla `Goods_roomnight_30days` (` Goods_id` bigint (20) No nulo, `checkin_room_night_30days` int (11) no nulo predeterminado '0' comentario 'Último noche de 30 días', clave principal (` gots_id`)) motor = innodb predeterminado charset = utf8 comment = 'bets' Última noche de 30 días '"
KeyValue.java Parameter Clase:
clase pública KeyValue <k, v> implementa map.entry <k, v> {key private k key; valor v privado; public keyValue () {} public keyValue (k key, v value) {this.key = key; this.Value = value; } @Override public k getKey () {Key de retorno; } @Override public v getValue () {Valor de retorno; } @Override public v setValue (V value) {v OldValue = this.Value; this.Value = value; devolver OldValue; } public jsonObject toJsonObject () {return ReportJsonObject.NewObject (). Append (String.ValueOf (Key), Value); } @Override public string toString () {return tOjsonObject (). TJSonstring (); }}DAO Class Goodsroom Night30daySmapper.Java
Interfaz pública Goodsroom Nightnight30daySmapper {int deletyByExample (Goodsroom Night30daySexample Ejemplo); Lista <Goodsroom Night30days> SelectByExample (Goodsroom Night30daySexample Ejemplo); <K, v> int InsertBatch (List <KeyValue <K, V >> registros);}MyBatis-Config.xml Archivo:
<? xml versión = "1.0" encoding = "utf-8"?> < </etenchings> <!-Después de la integración con Spring, la configuración de los entornos se abolirá y se entregará a Spring Management-> <entornos predeterminados = "Desarrollo"> <entorno ID = "Desarrollo"> <!-Usar JDBC Transaction Management-> <TRANSACTIONMANAGER TIPO = "JDBC" /> <!-Detabase de conexión, después de la integración de la integración de la tercera pareja, los grupos de conexión de terceros se usan- <Datoin <Datoin ". name = "Driver" value = "com.mysql.jdbc.driver" /> <propiedad name = "url" value = "jdbc: mysql: // localhost: 3306 /hotel_rePort? caracterSencoding = utf-8" /> <propiedad = "username" value = "test_user" /> <name de propiedad = "name de propiedad =" user = "user123" "" "" </entorno> </entornos> <mappers> <mapper resource = "mybatis/getsroom nocturno30daysmapper.xml"/> </mappers> </figuration>
El principal contenido de Goodsroom Night30daySmapper.xml Archivo:
<insert id = "insertbatch" parametertype = "list"> reemplazar en Goods_roomnight_30days (Goods_id, checkin_room_night_30days) valores <foreach colección = "list" index = "index" item = "item" separator = ","> ( #{item.key}, #{item.value}) <! null "> (#{item.key}, 0) </when>-> <!-<when test =" item.value! = null "> (#{item.key},#{item.value}) </when>-> <!-</elección>-> </foreach> </sert>Lo anterior es el código principal para reproducir esta excepción. El código completo se puede ver en GitHub: https://github.com/misterzhou/java-demo/tree/master/test-spring/spring-server
Código de prueba que reproduce la excepción de Goodsroom Night30daystest.java:
paquete org.guojing.test.spring.server; import org.apache.ibatis.io.resources; import org.apache.ibatis.session.sqlsession; import org.apache.ibatis.session.sqlsession; import org.apache.ibatis.session.sqlsession factory; importación; importación; org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.guojing.spring.commons.keyValue; importar org.junit.after; importar org.junit.bebore; importar org.junit.test; import java.io.ioexception; import java.iO.inputStream; import jaavil.util.util.util.util.util.Ut; java.util.list;/** * Creado en: 2016-12-24 * * @author GuoJing */public class Goodsroomnight30dayStest {sqlSessionFactory sqlsessionFactory; Sqlsession sqlsession; Goodsroom Night30 Daysmapper Goodsroom Night30 Daysmapper; @Bebore public void init () lanza IOException {String Resource = "MyBatis/MyBatis-Config.xml"; InputStream InputStream = Resources.GetResourCeASStream (recurso); sqlSessionFactory = new SQLSessionFactoryBuilder (). Build (InputStream); sqlsession = sqlSessionFactory.opensession (verdadero); Goodsroomnight30daySmapper = SQLSession.getMapper (Goodsroom Night30daySmapper.Class); } @Test public void test () {list <keyValue <long, integer >> registros = new ArrayList <> (); Records.Add (nuevo KeyValue <Long, Integer> (1725L, 5)); Records.Add (nuevo KeyValue <Long, Integer> (1728l, 3)); Records.Add (nuevo KeyValue <Long, Integer> (1730L, NULL)); Records.Add (nuevo KeyValue <Long, Integer> (1758L, NULL)); int deleted = Goodsroomnight30daysMapper.deleteByExample (New Goodsroom Night30daySexample ()); System.out.println ("----- Tamaño de la fila eliminada:" + eliminado); int row = Goodsroomnight30daysmapper.insertBatch (registros); System.out.println ("----- fila afectada:" + fila); Lista <Goodsroom Night30days> Result = Goodsroomnight30 Daysmapper.SelectByExample (New Goodsroom Night30daySexample ()); para (Goodsroom Night30days item: resultado) {System.out.println (item.ToString ()); }} @After public void after () {if (sqlsession! = Null) {sqlsession.close (); }}}Veamos en secreto, no mires primero, pienses en las causas de la anormalidad (los estudiantes que son competentes en el uso de la etiqueta foreach deberían poder ver las pistas).
Proceso de excepción y análisis de excepción
En el proyecto, dado que la clase de la clase de parámetros y el resultado de retorno del método DAO a menudo contienen un valor correspondiente a la clave y la clave, para evitar definir repetidamente la clase, definí una clase genérica de keyvalue que implementa la interfaz MAP.Entry. Para más detalles, consulte la sección anterior.
GoodsRoomnight30daysMapper.insertBatch() utiliza esta clase genérica. Después de ejecutar, se lanza la información de excepción mencionada al comienzo de este artículo.
Después de ver la información de excepción, me concentré en si el soporte de mybatis para genéricos no es lo suficientemente bueno. Le pregunté a mi colega (@shengnan), y mi colega lo probó en su máquina y descubrí que no había anormalidad. Esto es extraño. Después de una mirada más cercana al código, descubrí que la diferencia es que mi clase genérica KeyValue implementa la interfaz MAP.Entry. En este momento, no sé la descripción de la etiqueta foreach en el sitio web oficial de MyBatis:
Cualquier objeto Iterable (como listas, colecciones, etc.) y cualquier diccionario o objeto de matriz se puede pasar a ForEach como parámetros de colección. Cuando se usa un objeto o matriz iterable, el índice es el número de veces iterado, y el valor del elemento es el elemento obtenido en esta iteración. Cuando se usa un diccionario (o una colección de objetos MAP.Entry), el índice es la clave y el elemento es el valor.
A continuación, verifique las razones específicas de la excepción a través de la depuración. Por lo tanto, usé el código de la clase KeyValue que implementa la interfaz MAP.Entry para depurar. A través del registro de excepciones, puedo ver que la excepción se lanza en línea defaultsqlsession.java:200, por lo que presioné el punto de interrupción para línea defaultsqlsession.java:197, y luego lo ejecuté paso a paso. Cuando lo ejecuté para alinear foreachsqlnode.java:73, finalmente me di cuenta. Echemos un vistazo al diagrama de la cadena de llamadas de depuración primero:
Echemos un vistazo al código específico foreachsqlnode.java:73 (esta clase es la clase de nodo del objeto de etiqueta foreach)::
En este momento, la causa específica de la excepción es muy obvia. La clase a la que pertenece el objeto O aquí es la clase KeyValue. Dado que la clase KeyValue implementa la interfaz MAP.Entry, o instancia map.entry es verdadera, myBatis asigna el valor clave al atributo de índice de foreach y asigna el valor al atributo del elemento. Aquí, el objeto entero con un valor de 5 se asigna al atributo del elemento. Por lo tanto, el objeto correspondiente correspondiente al atributo de elemento de la etiqueta Seleccionar con InsertBatch en Goodsroom Night30daySmapper.xml no tiene los atributos de la tecla y el elemento.
<Insert id = "insertbatch" parametertype = "list"> reemplazar en Goods_roomnight_30days (Goods_id, checkin_room_night_30days) valores <foreach colección = "list" index = "index" item = "item" separator = ","> ( #{item.key}, #{item.value}) </ foreach> </sert>Lo anterior es un breve análisis de las razones por las cuales la etiqueta mybatis foreach se usa incorrectamente, y espero que sea útil para usted. Si tiene alguna pregunta, déjame un mensaje y el editor le responderá a tiempo. ¡Muchas gracias por su apoyo al sitio web de Wulin.com!