简体   繁体   English

如何使用MyBatis / Spring实现批处理操作?

[英]How to implement batch operations with MyBatis/Spring?

I am wondering how to implement batch operations with my insert statements using MyBatis 3 & Spring 3? 我想知道如何使用MyBatis 3和Spring 3使用insert语句实现批处理操作?

For example, here is what is currently being done: 例如,以下是目前正在进行的操作:

spring.xml: 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: MyService.xml:

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

MyService.java: MyService.java:

public interface MyService {

  public void insertMyRecord (MyRecord);
}

MyController.java: 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);
    }
  }
}

Disclaimer: That is just pseudo code for demonstration purposes 免责声明:这只是用于演示目的的伪代码

So what can I do to turn that into a batch process? 那么我该怎么做才能把它变成一个批处理过程呢?

Ideally I want to be able to do it with least "intrusion" into code, ie use annotations more preferred, but if not possible what is the next best thing? 理想情况下,我希望能够以最少的“入侵”代码进行,即使用注释更优先,但如果不可能,那么下一个最好的东西是什么?

Also, this needs to be configured just for this one service, not for everything in the project. 此外,这需要仅针对此一项服务进行配置,而不是针对项目中的所有内容进行配置。

This is running and tested example ... Update multiple rows using batch (ibatis + java ) 这是运行和测试的示例...使用批处理更新多行(ibatis + java)

In this ex. 在这个前。 I am updating attending count from table with respective to partyid. 我正在更新表格中的参加计数与各自的党员。

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();
    }
}

Model class where variable is defined : 定义变量的模型类:

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 code party.xml代码

Actual query where batch execute 批处理执行的实际查询

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

Interface code here 接口代码在这里

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

Here is my batch session code 这是我的批处理会话代码

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

SqlSession session = ConnectionBuilderAction.getSqlSession();

The accepted answer above doesn't actually get you batch mode for MyBatis. 上面接受的答案实际上并没有为MyBatis提供批处理模式。 You need to choose the proper Executor via ExecutorType.BATCH. 您需要通过ExecutorType.BATCH选择正确的Executor。 That is either passed as a parameter to SqlSession.openSession in standard MyBatis API or, if using MyBatis-Spring, as an option to the SqlSessionTemplate. 这可以作为参数传递给标准MyBatis API中的SqlSession.openSession,或者,如果使用MyBatis-Spring,则作为SqlSessionTemplate的选项传递。 That is done via: 这是通过:

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

There is nothing else that needs to be done. 没有别的事情需要做。

I'm not sure I understand the question fully correct but I will try to give you my thoughts. 我不确定我完全理解这个问题,但我会尽力给你我的想法。

For making the single service I would recommend to generify the service interface: 为了制作单一服务,我建议生成服务接口:

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

Then you can check the type of the object and call the propper mapper repository. 然后,您可以检查对象的类型并调用propper映射器存储库。

Then you can generify it more by creating a common interface: 然后,您可以通过创建一个通用界面来更多地通用它:

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

and extend it by your mapper interface: 并通过mapper接口扩展它:

public interface MyService extends Creator<MyRecord>{}

Now the most complicated step: you get the object of a particular type, see what exact mapper implements the Creator interface for this class (using java reflection API) and invoke the particular method. 现在最复杂的一步是:获取特定类型的对象,查看确切的mapper实现此类的Creator接口(使用java反射API)并调用特定方法。

Now I give you the code I use in one of my projects: 现在我给你在我的一个项目中使用的代码:

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));
    }
}

Warning! 警告! This approach can have bad performance impact so use it in non-performance-critical actions 此方法可能会对性能产生不良影响,因此请将其用于非性能关键操作

If you want bulk insert I would recommend to use mybatis foreach for bulk insert as described here . 如果你想批量插入我会建议所描述使用的MyBatis的foreach用于批量插入这里

If you think you don't want to write sql for every type of objects you better use Hibernate or any other advanced ORM. 如果您认为不想为每种类型的对象编写sql,最好使用Hibernate或任何其他高级ORM。 MyBatis is just an SQL mapping interface. MyBatis只是一个SQL映射接口。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM