Mybatis 빅 데이터 양의 대량 insert 문 제 를 해결 합 니 다.

머리말
Mybatis 를 통 해 7000+데 이 터 를 대량으로 삽입 할 때 오류 가 발생 했 습 니 다.error log 는 다음 과 같 습 니 다.

 , 
('G61010352', 
'610103199208291214', 
'  52', 
'G61010350',
'610103199109920192',
'  50',
'07',
'01',
'0104',
' ',
,
' ',
' ',
current_timestamp,
current_timestamp
) 
중단 되 었 습 니 다.원인 을 얻 기 위해 getNextException 을 호출 하 십시오.

at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2743)
 at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:411)
 at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2892)
 at com.alibaba.druid.filter.FilterChainImpl.statement_executeBatch(FilterChainImpl.java:2596)
 at com.alibaba.druid.wall.WallFilter.statement_executeBatch(WallFilter.java:473)
 at com.alibaba.druid.filter.FilterChainImpl.statement_executeBatch(FilterChainImpl.java:2594)
 at com.alibaba.druid.filter.FilterAdapter.statement_executeBatch(FilterAdapter.java:2474)
 at com.alibaba.druid.filter.FilterEventAdapter.statement_executeBatch(FilterEventAdapter.java:279)
 at com.alibaba.druid.filter.FilterChainImpl.statement_executeBatch(FilterChainImpl.java:2594)
 at com.alibaba.druid.proxy.jdbc.StatementProxyImpl.executeBatch(StatementProxyImpl.java:192)
 at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeBatch(DruidPooledPreparedStatement.java:559)
 at org.apache.ibatis.executor.BatchExecutor.doFlushStatements(BatchExecutor.java:108)
 at org.apache.ibatis.executor.BaseExecutor.flushStatements(BaseExecutor.java:127)
 at org.apache.ibatis.executor.BaseExecutor.flushStatements(BaseExecutor.java:120)
 at org.apache.ibatis.executor.BaseExecutor.commit(BaseExecutor.java:235)
 at org.apache.ibatis.executor.CachingExecutor.commit(CachingExecutor.java:112)
 at org.apache.ibatis.session.defaults.DefaultSqlSession.commit(DefaultSqlSession.java:196)
 at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:390)
 ... 39 more
이러한 이상 은 포착 할 수 없습니다.druid 와 ibatis 의 원본 코드 만 이상 하 게 가리 키 는 것 을 볼 수 있 습 니 다.기본 적 인 SqlSession 이 이 수량 급 의 대량 작업 을 지원 하지 못 하기 때 문 이 라 고 초보 적 으로 추측 합 니 다.다음은 원본 코드 와 공식 문 서 를 결합 하여 구체 적 으로 보 겠 습 니 다.
소스 코드 분석
프로젝트 는 Spring+Mybatis 를 사용 합 니 다.Dao 층 에 서 는 Spring 에서 제공 하 는 SqlSession Template 를 통 해 SqlSession 을 가 져 옵 니 다.

@Resource(name = "sqlSessionTemplate")
private SqlSessionTemplate sqlSessionTemplate;
public SqlSessionTemplate getSqlSessionTemplate() 
{
 return sqlSessionTemplate;
}
검증 을 위해 SqlSesion 을 어떻게 제공 하 는 지,SqlSession Template 의 소스 코드 를 열 고 구조 방법 을 살 펴 보 세 요.

  /**
 * Constructs a Spring managed SqlSession with the {@code SqlSessionFactory}
 * provided as an argument.
 *
 * @param sqlSessionFactory
 */
 public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
 this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
 }
다음 에 getDefaultExecutor Type 을 누 르 십시오.

 public ExecutorType getDefaultExecutorType() {
 return defaultExecutorType;
 }
클래스 의 전역 변 수 를 직접 되 돌려 주 는 defaultExecutor Type 을 볼 수 있 습 니 다.클래스 의 머리 에서 이 변 수 를 찾 아 보 겠 습 니 다.

protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
찾 았 습 니 다.Spring 이 제공 하 는 기본 실행 기 유형 은 Simple 입 니 다.그 유형 은 모두 세 가지 가 있 습 니 다.

/**
 * @author Clinton Begin
 */
public enum ExecutorType {
 SIMPLE, REUSE, BATCH
}
자세히 살 펴 보 니 세 개의 매 거 진 유형 이 있 는데 그 중 하 나 는 BATCH 가 대량 작업 과 관련 이 있 습 니까?my batis 공식 문서 에서 이 세 가지 값 에 대한 설명 을 살 펴 보 겠 습 니 다.
-4ExecutorType.SIMPLE:이 실행 기 유형 은 특별한 일 을 하지 않 습 니 다.모든 문장의 실행 을 위해 새로운 예비 처리 문 구 를 만 듭 니 다.
-4ExecutorType.REUSE:이 실행 기 유형 은 예비 처리 문 구 를 재 활용 합 니 다.
-4ExecutorType.BATCH:이 실행 기 는 모든 업데이트 문 구 를 대량으로 실행 합 니 다.만약 에 SELECT 가 그들 사이 에서 실행 하면 그들 이 필요 하 다 고 표시 하여 간단 하고 이해 하기 쉬 운 행 위 를 보장 합 니 다.
제 가 사용 하 는 SIMPLE 는 모든 문 구 를 위해 새로운 예비 처리 문 구 를 만 들 것 입 니 다.즉,Prepared Statement 대상 을 만 드 는 것 입 니 다.druid 연결 풀 을 사용 하여 처리 하 더 라 도 매번 풀 에 put 하고 druid 의 cache 에 가입 합 니 다.이 효율 은 짐작 할 수 있 기 때문에 그 이상 은 insert timeout 으로 인해 대기 시간 이 데이터베이스 구동 의 최대 대기 치 를 초과 할 수도 있 습 니 다.
자,이미 해 결 된 문 제 를 위주 로 하고 분석 에 따라 저 희 는 BATCH 방식 으로 SqlSession 을 만 들 고 정부 에서 도 일련의 과부하 방법 을 제공 합 니 다.

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
주로 네 가지 매개 변수 유형 이 있 는데 그것 이 바로

- Connection connection
- ExecutorType execType
- TransactionIsolationLevel level
- boolean autoCommit
공식 문서 에서 이러한 매개 변수 에 대해 서도 상세 한 설명 이 있다.
SqlSession Factory 는 SqlSession 인 스 턴 스 를 만 드 는 데 6 가지 방법 이 있 습 니 다.일반적으로 어떻게 결정 하 는 지 는 다음 과 같은 방법 을 선택 할 때 이다.Transaction ( )session 에 사 무 를 사용 하거나 자동 으로 제출 하고 싶 습 니까?
4Connection ( ):MyBatis 가 설정 한 데이터 원본 의 연결 을 얻 고 싶 습 니까?아니면 자신 을 제공 하고 싶 습 니까?Execution ( )MyBatis 는 예비 처리 문구 와/또는 일괄 업데이트 문 구 를 재 활용 하고 싶 습 니까?(삽입 과 삭제 포함)
따라서 수요 에 따라 선택 하면 됩 니 다.우리 가 해 야 할 일 은 대량 insert 이기 때문에 SqlSession openSession(Executor Type exec Type,boolean autoCommit)을 선택 하 십시오.
참고 로 Transaction Isolation Level,즉 우리 가 자주 제기 하 는 사무 격 리 단계 에 대해 공식 문서 에서 도 잘 소개 되 었 습 니 다.
MyBatis 는 트 랜 잭 션 격 리 단계 에서 자바 매 거 진 포장 기 를 사용 합 니 다.Transaction IsolationLevel 이 라 고 합 니 다.그렇지 않 으 면 예상 한 방식 으로 작업 하고 JDBC 가 지원 하 는 5 단계 가 있 습 니 다.

NONE,
READ_UNCOMMITTED
READ_COMMITTED,
REPEATABLE_READ,
SERIALIZA BLE)
문 제 를 해결 하 다
본론 으로 돌아 가 문제 의 원인 을 초보 적 으로 찾 았 습 니 다.SqlSession 의 획득 방식 을 바 꾸 어 다시 시도 해 보 겠 습 니 다.

testing… 2minutes later…
불 행 히 도 똑 같은 오 류 를 보고 하 는 것 은 Executor Type 의 문제 만 이 아 닌 것 같 습 니 다.commt 의 데이터 양 이 너무 많아 서 응답 시간 이 너무 긴 것 은 아 닐 까요?위 에서 저도 이런 가능성 을 언급 했 습 니 다.그러면 다시 한 번 일괄 처리 해 보 겠 습 니 다.즉,같은 업무 범위 내 에서 commt insert batch 를 나 누 어 보 겠 습 니 다.Dao 층 의 코드 구현 을 구체 적 으로 살 펴 보 겠 습 니 다.

 @Override
 public boolean insertCrossEvaluation(List<CrossEvaluation> members)
   throws Exception {
  // TODO Auto-generated method stub
  int result = 1;
  SqlSession batchSqlSession = null;
  try {
   batchSqlSession = this.getSqlSessionTemplate()
     .getSqlSessionFactory()
     .openSession(ExecutorType.BATCH, false);//        sqlsession
   int batchCount = 1000;//   commit   
   int batchLastIndex = batchCount;//          
   for (int index = 0; index < members.size();) {
    if (batchLastIndex >= members.size()) {
     batchLastIndex = members.size();
     result = result * batchSqlSession.insert("MutualEvaluationMapper.insertCrossEvaluation",members.subList(index, batchLastIndex));
     batchSqlSession.commit();
     System.out.println("index:" + index+ " batchLastIndex:" + batchLastIndex);
     break;//       ,    
    } else {
     result = result * batchSqlSession.insert("MutualEvaluationMapper.insertCrossEvaluation",members.subList(index, batchLastIndex));
     batchSqlSession.commit();
     System.out.println("index:" + index+ " batchLastIndex:" + batchLastIndex);
     index = batchLastIndex;//        
     batchLastIndex = index + (batchCount - 1);
    }
   }
   batchSqlSession.commit();
  } 
  finally {
   batchSqlSession.close();
  }
  return Tools.getBoolean(result);
 }
재 테스트,프로그램 에 이상 이 없습니다.총 7728 개의 데이터 insert 시간 은 약 10s 정도 입 니 다.아래 그림 과 같 습 니 다.

총결산
Mybatis 대량 insert 빅 데이터 양 데 이 터 를 간단하게 기록 하 는 솔 루 션 입 니 다.참고 로 Tne End.
추가:my batis 대량 삽입 오류:','근처에 오류 가 있 습 니 다.
my batis 대량 삽입 시 오류,오류 메시지','근처에 오류 가 있 습 니 다.

mapper.xml 의 작성 방법 은?

<insert id="insertByBatch">
  INSERT INTO USER_LOG (USER_ID, OP_TYPE, CONTENT, IP, OP_ID, OP_TIME) VALUES 
  <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
   (#{rateId}, #{opType}, #{content}, #{ipStr}, #{userId}, #{opTime},
  </foreach>
 </insert>
인쇄 된 sql 문장

INSERT INTO USER_LOG (USER_ID, OP_TYPE, CONTENT, IP, OP_ID, OP_TIME) VALUES  ( 
 (?, ?, ?, ?, ?, ?)  ,  (?, ?, ?, ?, ?, ?)  )
디 버 깅 할 때 도 sql 을 navicate 에 복사 하여 검 사 를 하 였 는데,위의 잘못 을 보고 하 였 다.이 잘못 은 갈 피 를 잡 을 수 없 는 것 처럼 보 였 고,그 다음 에 스스로 insert 문 구 를 다시 썼 는데,정확 한 문 구 는

INSERT INTO USER_LOG (USER_ID, OP_TYPE, CONTENT, IP, OP_ID, OP_TIME) VALUES   (?, ?, ?, ?, ?, ?)  ,  (?, ?, ?, ?, ?, ?)
이전 sql 보다 괄호 가 적 습 니 다.이 때 실행 에 성 공 했 기 때문에 mapper.xml 에 서 는 opern=(close=)을 삭제 하면 됩 니 다.
한 마디 만 더 하면 대량으로 삽입 할 때 삽입 할 데 이 터 를 List<실체>로 조립 할 수 있 습 니 다.이렇게 많은 인 자 를 전달 하지 않 아 도 됩 니 다.
이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.만약 잘못 이 있 거나 완전히 고려 하지 않 은 부분 이 있다 면 아낌없이 가르침 을 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기