Mybatis foreach 라벨 의 부적 절 한 사용 으로 인 한 이상 원인 에 대한 분석
지난주 에 Mybatis 의 Mapper 인터페이스 방법 에서 Map.Entry 인 터 페 이 스 를 실현 하 는 범 형 류 를 사 용 했 고 이 방법 에 대응 하 는 sql 문구 도 foreach 라벨 을 사용 하여 이상 이 발생 했다.다음 이상 정보:
org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'key' in 'class java.lang.Integer'
### The error may involve org.guojing.test.spring.server.GoodsRoomnight30daysMapper.insertBatch-Inline
### The error occurred while setting parameters
### SQL: REPLACE INTO 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(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 org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
at com.sun.proxy.$Proxy4.insertBatch(Unknown Source)
at org.guojing.test.spring.server.GoodsRoomnight30daysTest.test(GoodsRoomnight30daysTest.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at 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(ReflectiveCallable.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)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at 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.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at 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(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)
Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'key' in 'class java.lang.Integer'
at org.apache.ibatis.reflection.Reflector.getGetInvoker(Reflector.java:409)
at org.apache.ibatis.reflection.MetaClass.getGetInvoker(MetaClass.java:164)
at org.apache.ibatis.reflection.wrapper.BeanWrapper.getBeanProperty(BeanWrapper.java:162)
at 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(BoundSql.java:75)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.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)
at 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 more
본인 이 Mybatis 에 대해 잘 모 르 기 때문에 debug 에 가서 구체 적 인 이상 원인 을 찾 는 데 오 랜 시간 이 걸 렸 습 니 다.다음은 이상 을 재현 하고 이상 을 일 으 키 는 원인 을 분석 하 겠 습 니 다.
이상 재현
위의 이상 을 재현 하기 위해 demo 를 썼 습 니 다.관련 코드 는 다음 과 같 습 니 다.
데이터베이스 테이블 구조:
CREATE TABLE `goods_roomnight_30days` (
`goods_id` bigint(20) NOT NULL,
`checkin_room_night_30days` int(11) NOT NULL DEFAULT '0' COMMENT ' 30 ',
PRIMARY KEY (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='goods 30 '
KeyValue.java 매개 변수 클래스:
public class KeyValue<K, V> implements Map.Entry<K, V> {
private K key;
private V value;
public KeyValue() {
}
public KeyValue(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
public JSONObject toJSONObject() {
return ReportJSONObject.newObject().append(String.valueOf(key), value);
}
@Override
public String toString() {
return toJSONObject().toJSONString();
}
}
DAO 류 GoodsRoomnight 30days Mapper.java
public interface GoodsRoomnight30daysMapper {
int deleteByExample(GoodsRoomnight30daysExample example);
List<GoodsRoomnight30days> selectByExample(GoodsRoomnight30daysExample example);
<K, V> int insertBatch(List<KeyValue<K, V>> records);
}
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-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="false"/>
</settings>
<!-- spring environments , spring -->
<environments default="development">
<environment id="development">
<!-- jdbc -->
<transactionManager type="JDBC" />
<!-- , -->
<dataSource type="POOLED">
<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" />
<property name="password" value="user123" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mybatis/GoodsRoomnight30daysMapper.xml"/>
</mappers>
</configuration>
GoodsRoomnight 30days Mapper.xml 파일 의 주요 내용:
<insert id="insertBatch" parameterType="list">
REPLACE INTO goods_roomnight_30days (goods_id, checkin_room_night_30days)
VALUES
<foreach collection="list" index="index" item="item" separator=",">
(#{item.key}, #{item.value})
<!--<choose>-->
<!--<when test="item.value == null">(#{item.key}, 0)</when>-->
<!--<when test="item.value != null">(#{item.key}, #{item.value})</when>-->
<!--</choose>-->
</foreach>
</insert>
이상 은 이 이상 을 재현 하 는 주요 코드 입 니 다.전체 코드 는 Github 에서 볼 수 있 습 니 다https://github.com/misterzhou/java-demo/tree/master/test-spring/spring-server이상 한 테스트 코드 재현 GoodsRoomnight 30days Test.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.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Created at: 2016-12-24
*
* @author guojing
*/
public class GoodsRoomnight30daysTest {
SqlSessionFactory sqlSessionFactory;
SqlSession sqlSession;
GoodsRoomnight30daysMapper goodsRoomnight30daysMapper;
@Before
public void init() throws IOException {
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
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 GoodsRoomnight30daysExample());
System.out.println("----- deleted row size: " + deleted);
int row = goodsRoomnight30daysMapper.insertBatch(records);
System.out.println("----- affected row: " + row);
List<GoodsRoomnight30days> result = goodsRoomnight30daysMapper.selectByExample(new GoodsRoomnight30daysExample());
for (GoodsRoomnight30days item : result) {
System.out.println(item.toString());
}
}
@After
public void after() {
if (sqlSession != null) {
sqlSession.close();
}
}
}
뜸 을 들 여 다 보지 말고 이상 한 원인 을 생각해 보 세 요.이상 과정 및 이상 분석 찾기
프로젝트 에서 DAO 방법의 매개 변수 클래스 와 반환 결과 클래스 는 키 와 키 에 대응 하 는 값 을 자주 포함 하기 때문에 중복 정의 클래스 를 피하 기 위해 저 는 Map.Entry 인 터 페 이 스 를 실현 하 는 KeyValue 범 형 클래스 를 정 의 했 습 니 다.구체 적 으로 상단 을 보십시오.
4.567914.방법 은 이 범 형 류 를 사용 했다.운행 후에 본문 에서 언급 하기 시작 한 이상 한 정 보 를 던 졌 다.
이상 한 정 보 를 보고 Mybatis 가 팬 형 에 대한 지원 이 좋 지 않 은 지 에 중점 을 두 었 다.동료(@승 남)에 게 물 어 보 니 동료 가 자신의 기계 에서 시험 해 보 니 이상 이 없 었 다.이상 하 네.코드 를 자세히 살 펴 보 니 다른 점 은 바로 나의 KeyValue 범 형 류 가 Map.Entry 인 터 페 이 스 를 실현 한 것 이다.이 때 는 my batis 홈 페이지 에서 foreach 태그 에 대한 설명 을 모 릅 니 다.
모든 교체 가능 한 대상(예 를 들 어 목록,집합 등)과 그 어떠한 사전 이나 배열 대상 을 foreach 에 집합 매개 변수 로 전달 할 수 있 습 니 다.교체 가능 한 대상 이나 배열 을 사용 할 때 index 는 현재 교체 횟수 이 고 item 의 값 은 이번 교체 에서 얻 은 요소 입 니 다.사전(또는 Map.Entry 대상 의 집합)을 사용 할 때 index 는 키 이 고 item 은 값 입 니 다.
다음은 debug 를 통 해 이상 이 발생 하 는 구체 적 인 원인 을 살 펴 보 자.그래서 맵.Entry 인 터 페 이 스 를 실현 한 KeyValue 류 의 코드 로 debug 를 진행 합 니 다.이상 로 그 를 통 해 이상 이 DefaultSqlSession.java:200 줄 에 던 져 진 것 을 볼 수 있 습 니 다.그러면 단점 을 DefaultSqlSession.java:197 줄 에 맞 추고 한 걸음 한 걸음 아래로 실행 합 니 다.ForEachSqlNode.java:73 줄 에 실 행 했 을 때 깨 달 았 습 니 다.debug 호출 체인 그림 을 먼저 보 세 요:
구체 적 인 코드 를 보십시오.ForEachSqlNode.java:73(이것 이 바로 foreach 태그 대상 의 Node 클래스 입 니 다):
이때 구체 적 인 이상 원인 이 뚜렷 합 니 다.이곳 의 o 대상 이 속 한 클래스 는 바로 KeyValue 류 입 니 다.KeyValue 류 는 Map.Entry 인 터 페 이 스 를 실 현 했 기 때문에 o instance Map.Entry 는 true 이 고 my batis 는 key 값 을 foreach 의 index 속성 에 부 여 했 습 니 다.value 값 을 item 속성 에 부 여 했 습 니 다.여기 서 값 이 5 인 Integer 대상 을 item 속성 에 부 여 했 습 니 다.그래서 GoodsRoomnight 30days Mapper.xml 에서 id 가 insertBatch 인 select 태그 의 item 속성 에 대응 하 는 대상 도 item.key 와 item.value 속성 이 없 는데 이것 이 최종 적 으로 이상 을 초래 하 는 원인 입 니 다.
<insert id="insertBatch" parameterType="list">
REPLACE INTO goods_roomnight_30days (goods_id, checkin_room_night_30days)
VALUES
<foreach collection="list" index="index" item="item" separator=",">
(#{item.key}, #{item.value})
</foreach>
</insert>
위 에서 말 한 것 은 편집장 님 께 서 소개 해 주신 Mybatis foreach 라벨 의 사용 이 부적 절하 여 이상 을 초래 한 원인 에 대한 분석 입 니 다.여러분 께 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 저 에 게 메 시 지 를 남 겨 주세요.편집장 님 께 서 제때에 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
MySQL/마이바티스 | 동적 쿼리 사용A라는 서비스에 해당하는 테이블을 조인하고 조회하는 데 사용됩니다. 나중에 공통화를 위해 B 및 C 서비스도 추가됩니다. A, B, C 서비스는 모두 단일 쿼리에서 작동할 수 있도록 공통화되어야 합니다. 테이블에 각...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.