hibernate one-to-manyカスケード削除問題


既存の2つのオブジェクト:DiaryとDiaryReply
表の構造は次のとおりです.

-- ----------------------------
-- Table structure for osblog_diary_reply
-- ----------------------------
CREATE TABLE `osblog_diary_reply` (
  `id` int(11) NOT NULL auto_increment,
  `diary_id` int(11) NOT NULL,
  `author` varchar(20) NOT NULL,
  `author_url` varchar(100) default NULL,
  `author_email` varchar(50) default NULL,
  `owner_only` int(11) default NULL,
  `content` text NOT NULL,
  `create_time` datetime NOT NULL,
  `last_modify_time` datetime default NULL,
  `last_update_time` timestamp NULL default NULL on update  CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  KEY `r_diary_reply_fk` (`diary_id`),
  CONSTRAINT `fk_r_diary_reply` FOREIGN KEY (`diary_id`) REFERENCES `osblog_diary` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for osblog_diary
-- ----------------------------
CREATE TABLE `osblog_diary` (
  `id` int(11) NOT NULL auto_increment,
  `blog_id` varchar(32) NOT NULL,
  `catalog_id` int(11) NOT NULL COMMENT '    ',
  `author` varchar(20) default NULL,
  `title` varchar(200) NOT NULL,
  `content` text NOT NULL,
  `diary_size` int(11) NOT NULL,
  `view_count` int(11) NOT NULL,
  `reply_count` int(11) NOT NULL,
  `refer` varchar(100) default NULL COMMENT '    ',
  `weather` varchar(20) default NULL,
  `mood_level` smallint(6) default NULL COMMENT '    ',
  `tags` varchar(100) default NULL,
  `last_read_time` datetime default NULL,
  `last_reply_time` datetime default NULL,
  `reply_notify` smallint(6) default NULL COMMENT '      ',
  `locked` smallint(6) default NULL COMMENT '    (  )',
  `create_time` datetime NOT NULL,
  `last_modify_time` datetime default NULL,
  `last_update_time` timestamp NULL default NULL on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  KEY `r_catalog_diary_fk` (`catalog_id`),
  KEY `r_blog_diary_fk` (`blog_id`),
  CONSTRAINT `fk_r_blog_diary` FOREIGN KEY (`blog_id`) REFERENCES `osblog_site_blog` (`blog_id`),
  CONSTRAINT `fk_r_catelog_diary` FOREIGN KEY (`catalog_id`) REFERENCES `osblog_diary_catalog` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


/*******diary.hbm.xml*******/
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="cn.edu.zjut.software.swordye.model">
	<class
		name="Diary"
		table="osblog_diary"
	>
		<meta attribute="sync-DAO">false</meta>
		<id
			name="id"
			type="integer"
			column="id"
		>
			<generator class="native"/>
		</id>

		<property
			name="author"
			column="author"
			type="string"
			not-null="false"
			length="20"
		/>
		<property
			name="title"
			column="title"
			type="string"
			not-null="true"
			length="200"
		/>
		<property
			name="content"
			column="content"
			type="string"
			not-null="true"
		/>
		<property
			name="diarySize"
			column="diary_size"
			type="integer"
			not-null="true"
			length="10"
		/>
		<property
			name="viewCount"
			column="view_count"
			type="integer"
			not-null="true"
			length="10"
		/>
		<property
			name="replyCount"
			column="reply_count"
			type="integer"
			not-null="true"
			length="10"
		/>
		<property
			name="refer"
			column="refer"
			type="string"
			not-null="false"
			length="100"
		/>
		<property
			name="weather"
			column="weather"
			type="string"
			not-null="false"
			length="20"
		/>
		<property
			name="moodLevel"
			column="mood_level"
			type="java.lang.Short"
			not-null="false"
			length="5"
		/>
		<property
			name="tags"
			column="tags"
			type="string"
			not-null="false"
			length="100"
		/>
		<property
			name="lastReadTime"
			column="last_read_time"
			type="timestamp"
			not-null="false"
		/>
		<property
			name="lastReplyTime"
			column="last_reply_time"
			type="timestamp"
			not-null="false"
		/>
		<property
			name="replyNotify"
			column="reply_notify"
			type="java.lang.Short"
			not-null="false"
			length="5"
		/>
		<property
			name="locked"
			column="locked"
			type="java.lang.Short"
			not-null="false"
			length="5"
		/>
		<property
			name="createTime"
			column="create_time"
			type="timestamp"
			not-null="true"
		/>
		<property
			name="lastModifyTime"
			column="last_modify_time"
			type="timestamp"
			not-null="false"
		/>

		<many-to-one
			name="blog"
			column="blog_id"
			class="SiteBlog"
			not-null="true"
		>
		</many-to-one>
		<many-to-one
			name="diaryCatalog"
			column="catalog_id"
			class="DiaryCatalog"
			not-null="true"
		>
		</many-to-one>
	

		<set name="diaryReplies" inverse="true"   cascade="delete" lazy="true">
			<key column="id"/>   //        ,        diary_id   
			<one-to-many class="DiaryReply"/>
		</set>


	</class>	
</hibernate-mapping>



////*****diaryReply.hbm.xml******////

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="cn.edu.zjut.software.swordye.model">
	<class
		name="DiaryReply"
		table="osblog_diary_reply"
	>
		<meta attribute="sync-DAO">false</meta>
		<id
			name="id"
			type="integer"
			column="id"
		>
			<generator class="native"/>
		</id>

		<property
			name="blogId"
			column="blog_id"
			type="string"
			not-null="true"
			length="32"
		/>
		<property
			name="authorName"
			column="author"
			type="string"
			not-null="true"
			length="20"
		/>
		<property
			name="authorUrl"
			column="author_url"
			type="string"
			not-null="false"
			length="100"
		/>
		<property
			name="authorEmail"
			column="author_email"
			type="string"
			not-null="false"
			length="50"
		/>
		<property
			name="ownerOnly"
			column="owner_only"
			type="integer"
			not-null="false"
			length="10"
		/>
		<property
			name="content"
			column="content"
			type="string"
			not-null="true"
		/>
		<property
			name="createTime"
			column="create_time"
			type="timestamp"
			not-null="true"
		/>
		<property
			name="lastModifyTime"
			column="last_modify_time"
			type="timestamp"
			not-null="false"
		/>
		<many-to-one
			name="diary"
			column="diary_id"
			class="Diary"
			not-null="true"
			cascade="none"
		>
		</many-to-one>


	</class>	
</hibernate-mapping>


今からDiaryを削除しようとすると、そのDiaryに関連付けられたDiaryReplyがカスケード削除されます.

public void deleteDiary(Diary diary) {
      getSession().delete(diary);		
}

次のエラーが発生しました.
org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
Caused by: 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:202)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:575)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:662)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:632)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:314)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:117)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:166)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy13.deleteDiary(Unknown Source)
at cn.edu.zjut.software.swordye.service.DiaryServiceTest.testDeleteDiary(DiaryServiceTest.java:114)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
at org.unitils.UnitilsJUnit4TestClassRunner$CustomTestMethodRunner.executeMethodBody(UnitilsJUnit4TestClassRunner.java:231)
at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66)
at org.unitils.UnitilsJUnit4TestClassRunner$CustomTestClassMethodsRunner.invokeTestMethod(UnitilsJUnit4TestClassRunner.java:155)
at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
at org.unitils.UnitilsJUnit4TestClassRunner.run(UnitilsJUnit4TestClassRunner.java:95)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`osblogdb_test/osblog_diary_reply`, CONSTRAINT `fk_r_diary_reply` FOREIGN KEY (`diary_id`) REFERENCES `osblog_diary` (`id`))
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1213)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:912)
at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
... 39 more
最初はone-to-manyでcascade構成エラーだと思っていましたが、いろいろな構成を試してみても同じようにこのエラーを報告しました.ネットで検索してやっと文章の中で読んだ.元はone-to-many端の配置エラーで、*.hbm.xmlファイルは私がHibernateSynchronizerで生成したもので、このkey値には気づかなかった.この値はmanyエンド・キーが存在する属性列名、すなわちDiaryReplyが対応するテーブルのdiary_であるべきである.id.
このHibernateSynchronizerツールにバグがあるようですね!この間違いを探すのにN時間以上かかりました.のこれからこの道具を使うときは鳥に気をつけてください.