[Seasar-user:6144] 【KuinaDao】バッチ更新について

T.N [E-MAIL ADDRESS DELETED]
2007年 2月 13日 (火) 19:47:43 JST


 お世話になります。
中村友則です。

 KuinaDaoで複数の行を一度に更新しようとするとエラーが発生しました。

使用しているjarは
s2-framework-2.4.8.jar
s2hibernate-jpa-1.0.0-rc1.jar
kuina-dao-1.0.0.-rc1.jar
等です。
 DBはMySQLになります。

 テーブルの構造は
create table DtItem (id bigint not null, studentId bigint not null,
schoolId bigint not null, size VARCHAR(20), num bigint not null,
constraint  primary key (id, studentId, schoolId));
となっており、
複合プライマリーキーになっています。

そしてEntityは
@Entity
public class DtItem {
    @Id
    private Long id;
    private Long studentid;
    private Long schoolid;
    private String size;
    private Long num;
// アクセサメソッドは省略
}
KuinaDaoが複合プライマリーキーに対応していないようなので
ソース上は単独プライマリーキーとして運用しています。

 そしてServiceImplクラスの中で
@Stateless
public class StudentServiceImpl implements StudentService {

    @Resource
    private DtItemDao dtItemDao;

    public void update(OrderDto[] orderDtos) {

        DtItem dtItem = new DtItem();

        dtItem.setSchoolid(dtStudent.getSchoolid());
        dtItem.setStudentid(dtStudent.getStudentid());

        // 更新対象のDtItemsを検索
        List<DtItem> dtItems = this.dtItemDao.findByParameter(dtItem);

        for (int i = 0; i < orderDtos.length; i++) {
            // DtItemに更新後のサイズと数を設定し更新
            dtItems.get(i).setSize(orderDtos[i].getSize());
            dtItems.get(i).setNum(orderDtos[i].getNumber().longValue());
            this.dtItemDao.merge(dtItems.get(i));
        }
    }
}
という風にDtItemを更新しています。
(本来はmerge部分はforの外ですが簡単にするために内部に書いてあります)

 この結果エラーが発生し、以下のようなスタックトレースが表示されます。
2007-02-13 18:39:54,171 [http-8080-Processor24] WARN
org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
2007-02-13 18:39:54,171 [http-8080-Processor24] ERROR
org.hibernate.util.JDBCExceptionReporter - [ESSR0072]SQLで例外(SQL=
[update DtItem set studentid=?, schoolid=?, size=?, num=? where id=?],
ErrorCode=1062, SQLState=23000)が発生しました
2007-02-13 18:39:54,171 [http-8080-Processor24] WARN
org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
2007-02-13 18:39:54,171 [http-8080-Processor24] ERROR
org.hibernate.util.JDBCExceptionReporter - Duplicate entry '0-48-0' for
key 1
2007-02-13 18:39:54,171 [http-8080-Processor24] ERROR
org.hibernate.event.def.AbstractFlushingEventListener - Could not
synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute
JDBC batch update
	at
org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
	at
org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
	at
org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:249)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
	at
org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
	at
org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
	at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
	at
org.hibernate.ejb.AbstractEntityManagerImpl$1.beforeCompletion(AbstractEntityManagerImpl.java:515)
	at
org.seasar.extension.jta.TransactionImpl.beforeCompletion(TransactionImpl.java:188)
	at
org.seasar.extension.jta.TransactionImpl.commit(TransactionImpl.java:152)
	at
org.seasar.extension.jta.TransactionManagerImpl.commit(TransactionManagerImpl.java:57)
	at
org.seasar.extension.tx.AbstractTxInterceptor.end(AbstractTxInterceptor.java:69)
	at
org.seasar.framework.ejb.tx.EJB3RequiredInterceptor.invoke(EJB3RequiredInterceptor.java:39)
	at
jp.co.school.web.impl.StudentServiceImpl$$EnhancedByS2AOP$$d5b111$$MethodInvocation$$update0.proceed(MethodInvocationClassGenerator.java)
	at
org.seasar.framework.aop.interceptors.TraceInterceptor.invoke(TraceInterceptor.java:59)
	at
jp.co.school.web.impl.StudentServiceImpl$$EnhancedByS2AOP$$d5b111$$MethodInvocation$$update0.proceed(MethodInvocationClassGenerator.java)
	at
jp.co.school.web.impl.StudentServiceImpl$$EnhancedByS2AOP$$d5b111.update(StudentServiceImpl$$EnhancedByS2AOP$$d5b111.java)
	at
jp.co.school.web.student.StudentChangeAction.doChange(StudentChangeAction.java:31)
	at
jp.co.school.web.student.StudentChangeAction$$EnhancedByS2AOP$$1703457.$$doChange$$invokeSuperMethod$$(StudentChangeAction$$EnhancedByS2AOP$$1703457.java)
	at
jp.co.school.web.student.StudentChangeAction$$EnhancedByS2AOP$$1703457$$MethodInvocation$$doChange0.proceed(MethodInvocationClassGenerator.java)
	at
org.seasar.teeda.extension.interceptor.ActionSupportInterceptor.invoke(ActionSupportInterceptor.java:47)
	at
jp.co.school.web.student.StudentChangeAction$$EnhancedByS2AOP$$1703457$$MethodInvocation$$doChange0.proceed(MethodInvocationClassGenerator.java)
	at
org.seasar.framework.aop.interceptors.TraceInterceptor.invoke(TraceInterceptor.java:59)
	at
jp.co.school.web.student.StudentChangeAction$$EnhancedByS2AOP$$1703457$$MethodInvocation$$doChange0.proceed(MethodInvocationClassGenerator.java)
	at
jp.co.school.web.student.StudentChangeAction$$EnhancedByS2AOP$$1703457.doChange(StudentChangeAction$$EnhancedByS2AOP$$1703457.java)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at
org.seasar.teeda.core.el.impl.MethodBindingImpl.invoke(MethodBindingImpl.java:70)
	at
org.seasar.teeda.core.util.MethodBindingUtil.invoke(MethodBindingUtil.java:31)
	at
org.seasar.teeda.core.application.ActionListenerImpl.processAction(ActionListenerImpl.java:56)
	at javax.faces.component.UICommand.broadcast(UICommand.java:149)
	at
org.seasar.teeda.extension.component.html.THtmlCommandButton.broadcast(THtmlCommandButton.java:50)
	at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:192)
	at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:117)
	at
org.seasar.teeda.core.lifecycle.impl.InvokeApplicationPhase.executePhase(InvokeApplicationPhase.java:29)
	at
org.seasar.teeda.core.lifecycle.AbstractPhase.execute(AbstractPhase.java:55)
	at
org.seasar.teeda.core.lifecycle.impl.InvokeApplicationPhase$$EnhancedByS2AOP$$9021e3.$$execute$$invokeSuperMethod$$(InvokeApplicationPhase$$EnhancedByS2AOP$$9021e3.java)
	at
org.seasar.teeda.core.lifecycle.impl.InvokeApplicationPhase$$EnhancedByS2AOP$$9021e3$$MethodInvocation$$execute0.proceed(MethodInvocationClassGenerator.java)
	at
org.seasar.teeda.core.interceptor.MeasurementInterceptor.invoke(MeasurementInterceptor.java:46)
	at
org.seasar.teeda.core.lifecycle.impl.InvokeApplicationPhase$$EnhancedByS2AOP$$9021e3$$MethodInvocation$$execute0.proceed(MethodInvocationClassGenerator.java)
	at
org.seasar.teeda.core.lifecycle.impl.InvokeApplicationPhase$$EnhancedByS2AOP$$9021e3.execute(InvokeApplicationPhase$$EnhancedByS2AOP$$9021e3.java)
	at
org.seasar.teeda.core.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:93)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:87)
	at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
	at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
	at
org.seasar.extension.filter.EncodingFilter.doFilter(EncodingFilter.java:62)
	at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
	at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
	at
org.seasar.framework.container.hotdeploy.HotdeployFilter.doFilter(HotdeployFilter.java:63)
	at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
	at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
	at
org.seasar.framework.container.filter.S2ContainerFilter.doFilter(S2ContainerFilter.java:63)
	at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
	at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
	at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
	at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
	at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
	at
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
	at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
	at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
	at
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869)
	at
org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664)
	at
org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
	at
org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80)
	at
org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
	at java.lang.Thread.run(Unknown Source)
Caused by: org.seasar.framework.exception.SSQLException: [ESSR0072]SQLで
例外(SQL=[update DtItem set studentid=?, schoolid=?, size=?, num=? where
id=?], ErrorCode=1062, SQLState=23000)が発生しました
	at
org.seasar.extension.jdbc.impl.PreparedStatementWrapper.wrapException(PreparedStatementWrapper.java:62)
	at
org.seasar.extension.jdbc.impl.PreparedStatementWrapper.wrapException(PreparedStatementWrapper.java:57)
	at
org.seasar.extension.jdbc.impl.PreparedStatementWrapper.executeBatch(PreparedStatementWrapper.java:181)
	at
org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
	at
org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
	... 67 more
Caused by: java.sql.BatchUpdateException: Duplicate entry '0-48-0' for key 1
	at
com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:665)
	at
org.seasar.extension.jdbc.impl.PreparedStatementWrapper.executeBatch(PreparedStatementWrapper.java:179)
	... 69 more

 ただし、ブラウザ上ではエラーが表示されますがDBは更新されます。
また、デバッグでエラーが発生するタイミングを探ってみたところ、
merge()を実行するところでは発生せずに、
前述のupdate()を抜けた後に発生しています。
 また、 Duplicate entry '0-48-0' for は複合プライマリキーの内容が
出力されているようです。

 とりあえずパフォーマンスを要求されるアプリではないので、
1行を更新するごとにselect→updateを発行するようにしようと思っていますが、
エラーを回避できる方法、
またはバッチ更新をするための正しい方法がありませんでしょうか?

 不足している情報がありましたら追って報告させていただきます。

 以上よろしくお願いします。

中村友則



Seasar-user メーリングリストの案内