Hi eggbert,
I recalled that situation from Hibernate. In Hibernate, at least up to a
year and a half ago :), a ManyToMany was not enforced, so deleting an
entity having ManyToMany relations simply removed the entity from the
lists of the other entities. At the same time, I agree with you that
this can be counterintuitive, but IMHO it seems to be coherent with
removing a child entity on a one<->many relationship. In a one<->many
relationship, removing the parent will cause a violation (cause will
leave orphans), same in a one<->one the violation will happen if the
mapped side of the relationship is deleted, but removing a child is not
an error. In a many<->many there is no such side, both are peers, so it
sounds right to me that no violation occurs and OpenJPA is removing
silently the mapping in the cross table.
Also, JPA explicitly states that a cascade=remove can be specified only
on one<->many and one<->one, and not on many<->many (page 24 of the
final version), so there is no special case when such a cascade is
specified. In fact, cascade remove can work properly only when there is
a clear parent->child relation.
I'm not an OpenJPA developer, maybe they can clarify further.
Simone
eggbert wrote:
> Thanks, Simone.
>
> You may be right about me misunderstanding ManyToMany, but I always thought
> that without a cascade remove, the row in the cross table wouldn't be
> deleted regardless of ManyToMany, ManyToOne, etc. This is how it works
> under Toplink. E.g,
>
> em.getTransaction().begin();
> em.remove(a); //remove an Address that has a Person
> em.getTransaction().commit(); //Toplink won't allow this
>
> I had a look at the tables OpenJPA created. After the Address is removed,
> the row in the corss table is indeed deleted, thereby leaving a dirty Person
> instance in RAM. However, if I attempt to manually delete an Address (having
> Persons) from the DB with "delete from TAMB.ADDRESS where ID = 51" the
> result is...
>
> "DELETE on table 'ADDRESS' caused a violation of foreign key constraint
> 'SQL081014154643790' for key (51). The statement has been rolled back."
>
> So, it would seem that the DB is enforcing the foreign key constraint, but
> OpenJPA isn't.
>
> I can't seem to figure out how to get OpenJPA to export the schema, so I
> dumped it myself. I've tested this with Derby and HSQL and the same thing
> occurs with both.
>
> CREATE TABLE TAMB.OPENJPA_SEQUENCE_TABLE (
> ID SMALLINT NOT NULL,
> SEQUENCE_VALUE BIGINT
> );
>
> CREATE TABLE TAMB.ADDRESS (
> ID BIGINT NOT NULL
> );
>
> CREATE TABLE TAMB.PERSON (
> ID BIGINT NOT NULL
> );
>
> CREATE TABLE TAMB.PERSON_ADDRESS (
> PERSON_ID BIGINT,
> ADDRESS_ID BIGINT
> );
>
> CREATE UNIQUE INDEX TAMB.SQL081014154105010 ON TAMB.ADDRESS (ID ASC);
>
> CREATE INDEX TAMB.SQL081014154105350 ON TAMB.PERSON_ADDRESS (PERSON_ID ASC);
>
> CREATE INDEX TAMB.SQL081014154105430 ON TAMB.PERSON_ADDRESS (ADDRESS_ID
> ASC);
>
> CREATE UNIQUE INDEX TAMB.SQL081014154105220 ON TAMB.PERSON (ID ASC);
>
> CREATE UNIQUE INDEX TAMB.SQL081014154105100 ON TAMB.OPENJPA_SEQUENCE_TABLE
> (ID ASC);
>
> ALTER TABLE TAMB.ADDRESS ADD CONSTRAINT SQL081014154105010 PRIMARY KEY (ID);
>
> ALTER TABLE TAMB.PERSON ADD CONSTRAINT SQL081014154105220 PRIMARY KEY (ID);
>
> ALTER TABLE TAMB.OPENJPA_SEQUENCE_TABLE ADD CONSTRAINT SQL081014154105100
> PRIMARY KEY (ID);
>
> ALTER TABLE TAMB.PERSON_ADDRESS ADD CONSTRAINT SQL081014154105350 FOREIGN
> KEY (PERSON_ID)
> REFERENCES TAMB.PERSON (ID);
>
> ALTER TABLE TAMB.PERSON_ADDRESS ADD CONSTRAINT SQL081014154105430 FOREIGN
> KEY (ADDRESS_ID)
> REFERENCES TAMB.ADDRESS (ID);
>
>
>
>
>
>
> Simone Gianni wrote:
>
>> Hi eggbert,
>> maybe I'm missing something as well, but being a many to many, doesn't
>> also the row on the cross table gets deleted? In that case, you are
>> effectively removing the person from the address and the address from
>> the person, except for the copy you have in RAM which remains dirty.
>>
>> The violation would occur if you have a many to one (One person having
>> many addresses, one address belonging to one Person), with reverse
>> mapping and without a cascade, and you delete the Person, leaving the
>> Address with a non existing PERSON_ID that would cause the error.
>>
>> Could you also post the resulting schema and inspect the content of the
>> cross table before and after the deletion?
>>
>> Simone
>>
>>
>>
>> eggbert wrote:
>>
>>> Hello,
>>>
>>> I am letting OpenJPA build the schema from the entities, and I have
>>> enabled
>>> "ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict"
>>> for
>>> the MappingDefaults in my persistence.xml file. However, the
>>> JoinForeignKeyDeleteAction appears to have no effect.
>>>
>>> For example, if I have the following two entities...
>>>
>>> @Entity
>>> public class Person {
>>> @Id
>>> @GeneratedValue(strategy = GenerationType.AUTO)
>>> private Long id;
>>>
>>> @ManyToMany
>>> @JoinTable(name = "person_address",
>>> joinColumns = [EMAIL PROTECTED](name = "person_id",
>>> referencedColumnName="ID")},
>>> inverseJoinColumns = [EMAIL PROTECTED](name = "address_id",
>>> referencedColumnName="ID")})
>>> private Set<Address> addresses = new HashSet<Address>();
>>>
>>> public Person() {
>>> }
>>>
>>> Collection<Address> getAddresses() {
>>> return addresses;
>>> }
>>> }
>>>
>>> and...
>>>
>>> @Entity
>>> public class Address {
>>> @Id
>>> @GeneratedValue(strategy = GenerationType.AUTO)
>>> private Long id;
>>>
>>> @ManyToMany(mappedBy="addresses")
>>> private Set<Person> people = new HashSet<Person>();
>>>
>>> public Address() {
>>> }
>>>
>>> Collection<Person> getPeople() {
>>> return people;
>>> }
>>> }
>>>
>>> Then the following code will NOT throw an exception complaining about a
>>> foreign key violation.
>>>
>>> Person p = new Person();
>>> Address a = new Address();
>>>
>>> em.getTransaction().begin();
>>> em.persist(p);
>>> em.persist(a);
>>> p.getAddresses().add(a);
>>> a.getPeople().add(p);
>>> em.getTransaction().commit();
>>>
>>> em.getTransaction().begin();
>>> em.remove(a); //remove an Address that has a Person
>>> em.getTransaction().commit(); //why does this commit?
>>>
>>>
>>> Shouldn't the JoinForignKeyDeleteAction=restrict prevent this, (i.e,
>>> preventing an entity from being deleted that has an ID in a join table),
>>> or
>>> am I totally missing something here.
>>>
>>> Thanks!
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>> --
>> Simone Gianni CEO Semeru s.r.l. Apache Committer
>> MALE human being programming a computer http://www.simonegianni.it/
>>
>>
>>
>>
>
>
--
Simone Gianni CEO Semeru s.r.l. Apache Committer
MALE human being programming a computer http://www.simonegianni.it/