I use a biderectional many-to-many association and for the key i have set on-delete="cascade" on both ends.
What i expect is that the generated schema contains on delete cascade
foreign key definitions for both ends but this is missing for the invers end.
I want to get the behaviour that when an object of the invers end is deleted
the associations in the linktable are deleted too.
I want this behaviour independent if i delete the object of the invers
end via
Code:
session.delete(DAO)
or via bulk DML-style operation
Code:
Query q=session.createQuery("delete from ProfileDAO where name like :arg");
q.setString("arg","prof%");
q.executeUpdate();
Questions: Is this incomplete schema a bug or did i completely misunderstood the usage ?
Is there something to be considered when using DML-style operations
as described in chapter 14.4 in conjunction with on-delete="cascade"
foreign keys ?
When manipulating the DB with this bulk DML-style operations is the
caching of hibernate aware of this modifications ?
Hibernate version: 3.1 beta 2
Java 1.5.0_04, JBoss 4.0.3RC2
Generted schema (missing on delete cascade on inverse end):alter table SXUSER_SXPROFILE drop foreign key fk_profile2user;
alter table SXUSER_SXPROFILE drop foreign key fk_user2profile;
drop table if exists SXPROFILE;
drop table if exists SXUSER;
drop table if exists SXUSER_SXPROFILE;
create table SXPROFILE (id integer not null auto_increment, name varchar(30) not null unique, primary key (id)) type=InnoDB;
create table SXUSER (id integer not null auto_increment, loginname varchar(20) not null unique, firstname varchar(30), lastname varchar(40), locked bit not null, pwd varchar(30), primary key (id)) type=InnoDB;
create table SXUSER_SXPROFILE (profile_id integer not null, user_id integer not null, primary key (user_id, profile_id)) type=InnoDB;
alter table SXUSER_SXPROFILE add index fk_profile2user (user_id), add constraint fk_profile2user foreign key (user_id) references SXUSER (id) on delete cascade;
alter table SXUSER_SXPROFILE add index fk_user2profile (profile_id), add constraint fk_user2profile foreign key (profile_id) references SXPROFILE (id);
Attempt of correction:I have debugged the SchemaGeneration.
Following code change in the HbmBinder.java source (of hibernate3.jar)
solves the schema generation but on startup i got an
org.hibernate.MappingException: only inverse one-to-many associations may use on-delete="cascade"
HbmBinder.java Line 2309-2311
Code:
else if ( "many-to-many".equals( name ) ) {
ManyToOne element = new ManyToOne( collection.getCollectionTable() );
collection.setElement( element );
changed to
Code:
else if ( "many-to-many".equals( name ) ) {
ManyToOne element = new ManyToOne( collection.getCollectionTable() );
KeyValue keyVal = collection.getKey();
if ((keyVal != null) && (keyVal instanceof SimpleValue)) {
element.setCascadeDeleteEnabled(((SimpleValue)keyVal).isCascadeDeleteEnabled());
}
collection.setElement( element );
Mapping documents:
<hibernate-mapping
>
<class
name="demo.UserDataDAO"
table="SXUSER"
polymorphism="explicit"
dynamic-update="true"
>
<cache usage="read-write" />
<id
name="id"
column="id"
type="java.lang.Integer"
>
<generator class="native">
<param name="sequence">userdataid_seq</param>
</generator>
</id>
<property
name="loginname"
type="java.lang.String"
update="true"
insert="true"
column="loginname"
length="20"
not-null="true"
unique="true"
/>
<property
name="firstname"
type="java.lang.String"
update="true"
insert="true"
column="firstname"
length="30"
/>
<property
name="lastname"
type="java.lang.String"
update="true"
insert="true"
column="lastname"
length="40"
/>
<property
name="locked"
type="boolean"
update="true"
insert="true"
column="locked"
not-null="true"
/>
<property
name="pwd"
type="java.lang.String"
update="true"
insert="true"
column="pwd"
length="30"
/>
<set
name="profiles"
table="SXUSER_SXPROFILE"
lazy="true"
cascade="save-update"
sort="unsorted"
>
<key
column="user_id"
on-delete="cascade"
foreign-key="fk_profile2user"
>
</key>
<many-to-many
class="demo.ProfileDAO"
column="profile_id"
outer-join="auto"
foreign-key="fk_user2profile"
/>
</set>
</class>
<class
name="demo.ProfileDAO"
table="SXPROFILE"
polymorphism="explicit"
dynamic-update="true"
>
<cache usage="read-write" />
<id
name="id"
column="id"
type="java.lang.Integer"
>
<generator class="native">
<param name="sequence">profileid_seq</param>
</generator>
</id>
<property
name="name"
type="java.lang.String"
update="true"
insert="true"
column="name"
length="30"
not-null="true"
unique="true"
/>
<set
name="users"
table="SXUSER_SXPROFILE"
lazy="true"
inverse="true"
cascade="save-update"
sort="unsorted"
>
<key
column="profile_id"
on-delete="cascade"
foreign-key="fk_user2profile"
>
</key>
<many-to-many
class="demo.UserDataDAO"
column="user_id"
outer-join="auto"
foreign-key="fk_profile2user"
/>
</set>
</class>
</hibernate-mapping>
Name and version of the database you are using:
MySQL 4.1.5 with MySQLInnoDBDialect and Oracle10 with Oracle9Dialect
The generated SQL (show_sql=true):
Exception stackdump:
org.hibernate.MappingException: only inverse one-to-many associations may use on-delete="cascade": demo.ProfileDAO.users
at org.hibernate.mapping.Collection.validate(Collection.java:238)
at org.hibernate.mapping.Set.validate(Set.java:19)
at org.hibernate.cfg.Configuration.validate(Configuration.java:935)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1095)
at org.jboss.hibernate.jmx.Hibernate.buildSessionFactory(Hibernate.java:239)
at org.jboss.hibernate.jmx.Hibernate.startService(Hibernate.java:130)
at org.jboss.system.ServiceMBeanSupport.jbossInternalStart(ServiceMBeanSupport.java:287)
at org.jboss.system.ServiceMBeanSupport.jbossInternalLifecycle(ServiceMBeanSupport.java:236)
at sun.reflect.GeneratedMethodAccessor22.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:245)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
at org.jboss.system.ServiceController$ServiceProxy.invoke(ServiceController.java:974)
at $Proxy0.start(Unknown Source)
at org.jboss.system.ServiceController.start(ServiceController.java:453)
at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)