2013-07-29 47 views
11

我想知道如何使用MyBatis 3插入語句來實現批處理操作3 & Spring 3?如何用MyBatis/Spring實現批量操作?

例如,這裏是目前正在做什麼:

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

MyControl ler.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); 
    } 
    } 
} 

免責聲明:這是出於演示的目的只是僞代碼

所以我能做些什麼來把它轉換成一個批處理過程?

理想情況下,我希望能夠用最少的「入侵」來做到這一點,即使用更多的首選註釋,但如果不可能,下一個最好的東西是什麼?

另外,這需要爲這一項服務配置,而不是項目中的所有項目。

+0

爲了更清楚起見,我們的目標是在記錄插入中獲得一個JDBC批處理類型以便快速執行,而不是一個接一個的方法。 – Trant

回答

9

這是正在運行和測試的示例... 使用批量更新多行(ibatis + java)

在這個例子中,我正在更新從tableid到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(); 
+1

這裏缺少的主要信息是當您打開SqlSession時需要使用ExecutorType.BATCH。在標準的MyBatis API中,它是SqlSession類的openSession的一個參數。如果你使用MyBatis-Spring,你可以將它作爲參數傳遞給你的SqlSessionTemplate。即: <豆ID = 「SQLSESSION」 類= 「org.mybatis.spring.SqlSessionTemplate」> <構造精氨酸索引= 「0」 REF = 「SqlSessionFactory中」/> <構造精氨酸索引= 「1」 value =「BATCH」/> – jkratz

+0

在底部定義一個'getSqlBatchSession',但在代碼中調用'getSqlSession'。這是一個錯字嗎? – aioobe

+0

不應該使用foreach。看到這裏更有效的方式:http://stackoverflow.com/a/29264696/922348 – rimsky

1

我不確定我完全正確地理解了這個問題,但我會盡力給你我的想法。

爲了使單一的服務,我會建議泛型化服務接口:

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

然後你可以檢查對象的類型,並調用propper映射庫。

然後你就可以更多通過創建一個通用的接口泛型化它:

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

,並通過你的映射器接口進行擴展:

public interface MyService extends Creator<MyRecord>{} 

現在最複雜的一步:你得到的對象特定的類型,請參閱精確的映射器爲此類實現Creator接口(使用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進行批量插入,如here所述。

如果您認爲您不想爲每種類型的對象編寫sql,那麼最好使用Hibernate或任何其他高級的ORM。 MyBatis只是一個SQL映射接口。

+0

我可能最終使用的非常好的信息但它沒有解決實施批處理的願望。我想要的是,爲了性能的原因,我的循環插入被處理類似於JDBC批處理,而不是每個插入爲單個事務,因爲它現在被處理了。 – Trant

+0

@Trant所以這並不意味着您需要使用批量插入與mybatis foreach(我給出的鏈接)? – Nailgun

+0

但是,MyBatis是否將其轉化爲優化的批處理操作,還是繼續進行一次性插入? – Trant

8

接受的答案上面並沒有真正得到你批量模式MyBatis的。您需要通過ExecutorType.BATCH選擇適當的執行程序。這要麼作爲參數傳遞給標準MyBatis API中的SqlSession.openSession,要麼作爲SqlSessionTemplate的選項使用MyBatis-Spring。這是通過:

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

沒有什麼需要做。

+1

不知道,與這種XML配置,我的批量插入執行非常緩慢,沒有什麼區別從一個接一個。 –