복붙노트

[SPRING] MyBatis / Spring을 사용하여 배치 작업을 구현하는 방법은 무엇입니까?

SPRING

MyBatis / Spring을 사용하여 배치 작업을 구현하는 방법은 무엇입니까?

내 삽입 문을 MyBatis 3 & Spring 3를 사용하여 일괄 처리 작업을 구현하는 방법이 궁금합니다.

예를 들어, 현재 수행중인 작업은 다음과 같습니다.

spring.xml :

<bean id="jndiTemplateDatasource" class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
      <props>
        <prop key="java.naming.factory.initial">${context.factory}</prop>
      </props>
    </property>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiTemplate" ref="jndiTemplateDatasource"/>
  <property name="jndiName" value="${connectionpool.jndi}"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="com.test" />
</bean>

MyService.xml :

<insert id="insertMyRecord" parameterType="com.test.MyRecord"  >
   insert into ... // code removed
</insert> 

MyService.java:

public interface MyService {

  public void insertMyRecord (MyRecord);
}

MyController.java:

@Controller
public class MyController {

  @Autowired
  private MyService myService;

  @Transactional
  @RequestMapping( .... )
  public void bulkUpload (@RequestBody List<MyRecord> myRecords) {
    for (MyRecord record : myRecords) {
      myService.insertMyRecord(record);
    }
  }
}

면책 조항 : 이는 데모 목적을위한 단지 의사 코드입니다.

그렇다면이를 배치 프로세스로 바꾸려면 어떻게해야합니까?

이상적으로는 코드에 "침입"을 최소화하거나 주석을 사용하는 것이 더 좋을 수 있기를 원하지만 가능하지 않은 경우 가장 좋은 것은 무엇입니까?

또한 프로젝트의 모든 항목이 아니라이 서비스에 대해서만 구성해야합니다.

해결법

  1. ==============================

    1.이것은 실행 및 테스트 예제입니다 ... 일괄 처리를 사용하여 여러 행 업데이트 (ibatis + java)

    이것은 실행 및 테스트 예제입니다 ... 일괄 처리를 사용하여 여러 행 업데이트 (ibatis + java)

    이 예에서. 나는 파티원과 관련된 테이블에서 카운트를 업데이트하고 있습니다.

    public static int updateBatch(List<MyModel> attendingUsrList) {
        SqlSession session = ConnectionBuilderAction.getSqlSession();
        PartyDao partyDao = session.getMapper(PartyDao.class);
        try {
            if (attendingUsrList.size() > 0) {
                partyDao.updateAttendingCountForParties(attendingUsrList);
            }
            session.commit();
        } catch (Throwable t) {
            session.rollback();
            logger.error("Exception occurred during updateBatch : ", t);
            throw new PersistenceException(t);
        } finally {
            session.close();
        }
    }
    

    변수가 정의 된 모델 클래스 :

    public class MyModel  {
    
        private long attending_count;
        private String eid;
    
        public String getEid() {
            return eid;
        }
    
        public void setEid(String eid) {
            this.eid = eid;
        }
    
        public long getAttending_count() {
            return attending_count;
        }
    
        public void setAttending_count(long attending_count) {
            this.attending_count = attending_count;
        }
    
    
    }
    

    party.xml 코드

    일괄 처리가 실행되는 실제 쿼리

    <foreach collection="attendingUsrList" item="model"  separator=";">
        UPDATE parties SET attending_user_count = #{model.attending_count}
        WHERE  fb_party_id = #{model.eid}  
    </foreach>
    

    여기에 인터페이스 코드

    public interface PartyDao {
        int updateAttendingCountForParties (@Param("attendingUsrList") List<FBEventModel>attendingUsrList);
    }
    

    내 배치 세션 코드는 다음과 같습니다.

    public static synchronized SqlSession getSqlBatchSession() {
        ConnectionBuilderAction connection = new ConnectionBuilderAction();
        sf = connection.getConnection();
        SqlSession session = sf.openSession(ExecutorType.BATCH);
        return session;
    }
    
    SqlSession session = ConnectionBuilderAction.getSqlSession();
    
  2. ==============================

    2.위의 대답은 실제로 MyBatis에 대한 일괄 처리 모드를 제공하지 않습니다. ExecutorType.BATCH를 통해 적절한 Executor를 선택해야합니다. 이는 표준 MyBatis API의 SqlSession.openSession에 대한 매개 변수로 전달되거나 MyBatis-Spring을 사용하는 경우 SqlSessionTemplate에 대한 옵션으로 전달됩니다. 그건을 통해 이루어집니다 :

    위의 대답은 실제로 MyBatis에 대한 일괄 처리 모드를 제공하지 않습니다. ExecutorType.BATCH를 통해 적절한 Executor를 선택해야합니다. 이는 표준 MyBatis API의 SqlSession.openSession에 대한 매개 변수로 전달되거나 MyBatis-Spring을 사용하는 경우 SqlSessionTemplate에 대한 옵션으로 전달됩니다. 그건을 통해 이루어집니다 :

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
        <constructor-arg index="1" value="BATCH" />
    </bean>
    

    끝내야 할 다른 것은 없습니다.

  3. ==============================

    3.나는이 질문을 완전히 이해하고 있는지 확신 할 수 없지만, 나는 당신에게 내 생각을 전달하려고 노력할 것이다.

    나는이 질문을 완전히 이해하고 있는지 확신 할 수 없지만, 나는 당신에게 내 생각을 전달하려고 노력할 것이다.

    단일 서비스를 만들기 위해 서비스 인터페이스를 생성하는 것이 좋습니다.

    public void bulkUpload (@RequestBody List<T> myRecords) 
    

    그런 다음 객체의 유형을 확인하고 Propper Mapper 저장소를 호출 할 수 있습니다.

    그런 다음 공통 인터페이스를 만들어 더 많이 생성 할 수 있습니다.

    public interface Creator<T> {
        void create(T object);
    }
    

    매퍼 인터페이스로 확장 할 수 있습니다.

    public interface MyService extends Creator<MyRecord>{}
    

    이제 가장 복잡한 단계 : 특정 유형의 객체를 가져 와서 정확한 매퍼가 (Java 리플렉션 API를 사용하여)이 클래스의 작성자 인터페이스를 구현하고 특정 메소드를 호출하는지 확인하십시오.

    이제는 내 프로젝트 중 하나에서 사용하는 코드를 제공합니다.

    package com.mydomain.repository;
    
    //imports ...
    import org.reflections.Reflections;
    
    @Repository(value = "dao")
    public class MyBatisDao {
    
        private static final Reflections REFLECTIONS = new Reflections("com.mydomain");
    
        @Autowired
        public SqlSessionManager sqlSessionManager;
    
        public void create(Object o) {
            Creator creator = getSpecialMapper(Creator.class, o);
            creator.create(o);
        }
    
        // other CRUD methods
    
        @SuppressWarnings("unchecked")
        private <T> T getSpecialMapper(Class<T> specialClass, Object parameterObject) {
            Class parameterClass = parameterObject.getClass();
            Class<T> mapperClass = getSubInterfaceParametrizedWith(specialClass, parameterClass);
            return sqlSessionManager.getMapper(mapperClass);
        }
    
        private static <T, P> Class<? extends T> getSubInterfaceParametrizedWith(Class<T> superInterface, Class<P> parameterType) {
            Set<Class<? extends T>> subInterfaces = REFLECTIONS.getSubTypesOf(superInterface);
            for (Class<? extends T> subInterface: subInterfaces) {
                for (Type genericInterface : subInterface.getGenericInterfaces()) {
                    if (!(genericInterface instanceof ParameterizedType)) continue;
                    ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
                    Type rawType = parameterizedType.getRawType();
                    if (rawType instanceof Class<?> && ((Class<?>) rawType).isAssignableFrom(superInterface)) {
                        for (Type type: parameterizedType.getActualTypeArguments()) {
                            if (type instanceof Class<?> && ((Class<?>) type).isAssignableFrom(parameterType)) {
                                return subInterface;
                            }
                        }
                    }
    
                }
            }
            throw new IllegalStateException(String.format("No extension of %s found for parametrized type %s ", superInterface, parameterType));
        }
    }
    

    경고! 이 접근 방식은 성능에 좋지 않은 영향을 미칠 수 있으므로 성능에 중요하지 않은 작업

    대량 삽입을 원한다면 여기에 설명 된대로 대량 삽입을 위해 mybatis foreach를 사용하는 것이 좋습니다.

    모든 유형의 객체에 대해 sql을 작성하지 않으려면 Hibernate 나 다른 고급 ORM을 사용하는 것이 좋습니다. MyBatis는 SQL 매핑 인터페이스 일뿐입니다.

  4. from https://stackoverflow.com/questions/17928799/how-to-implement-batch-operations-with-mybatis-spring by cc-by-sa and MIT license