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つのサービスに対してのみ構成する必要があります。
これは実行され、テストされた例です...バッチを使用して複数の行を更新します(ibatis + Java)
この元で。テーブルからの参加者数をそれぞれの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();
}
}
変数が定義されているモデルクラス:
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();
上記の受け入れられた答えは、実際にはMyBatisのバッチモードを取得しません。 ExecutorType.BATCHを介して適切なエグゼキューターを選択する必要があります。これは、標準の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>
他に必要なことはありません。
質問が完全に正しいとは思いませんが、私の考えをお伝えします。
単一のサービスを作成するために、サービスインターフェースを汎用化することをお勧めします。
public void bulkUpload (@RequestBody List<T> myRecords)
次に、オブジェクトのタイプを確認し、propper mapperリポジトリを呼び出すことができます。
次に、共通のインターフェースを作成することで、より一般化できます。
public interface Creator<T> {
void create(T object);
}
そしてあなたのマッパーインターフェースでそれを拡張してください:
public interface MyService extends Creator<MyRecord>{}
ここで最も複雑な手順:特定のタイプのオブジェクトを取得し、このクラスのCreatorインターフェースを実装している正確なマッパーを確認し(JavaリフレクションAPIを使用))、特定のメソッドを呼び出します。
ここで、私のプロジェクトの1つで使用するコードを提供します。
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));
}
}
警告!このアプローチはパフォーマンスに悪影響を及ぼす可能性があるため、パフォーマンスが重要でないアクションで使用します
一括挿入が必要な場合は、記述されている here のように、一括挿入にmybatis foreachを使用することをお勧めします。
すべてのタイプのオブジェクトに対してsqlを記述したくない場合は、Hibernateまたはその他の高度なORMを使用することをお勧めします。 MyBatisは単なるSQLマッピングインターフェイスです。