This is probably just my noob-nees to JPA/Hibernate, but when I remove an Entity that is at the
Non-Owning end of a many-to-many relationship the associated rows from the Join table are not removed, and consequently produces a constraint exception in the DB. Whereas when I remove an entity from the
Owning end - it does remove the row(s) from the Join table and all is well with the world.
I'm sure it's something simple...
I'm usinng Hibernate 3.2 CR4, Hibernate Annotations 3.2 CR2 and Hibernate Entity Manager 3.2 CR2. DB is Oracle 10g.
Here's the table definitions:
Code:
CREATE TABLE TableOne (
TableOneId NUMBER (20) NOT NULL,
data VARCHAR (255) NOT NULL,
CONSTRAINT PK_TableOne PRIMARY KEY (TableOneId)
);
CREATE TABLE TableTwo (
TableTwoId NUMBER (20) NOT NULL,
data VARCHAR (255) NOT NULL,
CONSTRAINT PK_TableTwo PRIMARY KEY (TableTwoId)
);
CREATE TABLE TableOne_TableTwo (
TableOneId NUMBER (20) NOT NULL,
TableTwoId NUMBER (20) NOT NULL,
CONSTRAINT FK_TableOne_TableTwo PRIMARY KEY (TableOneId, TableTwoId)
);
ALTER TABLE TableOne_TableTwo ADD (CONSTRAINT FK_TableOne_TableTwo_1 FOREIGN KEY (TableOneId) REFERENCES TableOne (TableOneId));
ALTER TABLE TableOne_TableTwo ADD (CONSTRAINT FK_TableOne_TableTwo_2 FOREIGN KEY (TableTwoId) REFERENCES TableTwo (TableTwoId));
Here are the JPA Annoted classes:
Code:
package entity;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@Entity()
@Table(name = "TABLEONE", schema = "GREG")
public class TableOne implements Serializable {
@Id()
@Column(name = "TABLEONEID", unique = true, nullable = false, precision = 20)
private Long tableOneId;
@Basic()
@Column(name = "DATA", nullable = false, length = 255)
private String data;
@ManyToMany()
@JoinTable(name = "TABLEONE_TABLETWO", joinColumns = { @JoinColumn(name = "TABLEONEID", referencedColumnName = "TABLEONEID", nullable = false) }, inverseJoinColumns = { @JoinColumn(name = "TABLETWOID", referencedColumnName = "TABLETWOID", nullable = false) })
private Set<TableTwo> TableTwos;
public TableOne() {
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Long getTableOneId() {
return tableOneId;
}
public void setTableOneId(Long tableOneId) {
this.tableOneId = tableOneId;
}
public Set<TableTwo> getTableTwos() {
return TableTwos;
}
public void setTableTwos(Set<TableTwo> tableTwos) {
TableTwos = tableTwos;
}
}
Code:
package entity;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@SuppressWarnings("serial")
@Entity
@Table(name = "TABLETWO")
public class TableTwo implements Serializable {
@Id
@Column(name="TABLETWOID", unique=true, nullable=false , precision=20)
private Long tableTwoId;
@Basic
@Column(name="DATA", nullable=false, length=255)
private String data;
@ManyToMany(mappedBy="TableTwos")
private Set<TableOne> TableOnes;
public TableTwo() {
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Set<TableOne> getTableOnes() {
return TableOnes;
}
public void setTableOnes(Set<TableOne> tableOnes) {
TableOnes = tableOnes;
}
public Long getTableTwoId() {
return tableTwoId;
}
public void setTableTwoId(Long tableTwoId) {
this.tableTwoId = tableTwoId;
}
}
And here is a JUnit test (the second test fails). Note that the DB is populated with matching rows in each of TableOne and TableTwo:
Code:
package test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import junit.framework.TestCase;
import entity.TableOne;
import entity.TableTwo;
public class TableTest extends TestCase {
EntityManagerFactory emf;
EntityManager em;
@Override
protected void setUp() throws Exception {
emf = Persistence.createEntityManagerFactory("Test");
em = emf.createEntityManager();
}
public void testRemoveTableOne() {
EntityTransaction tx = em.getTransaction();
tx.begin();
TableOne tableOne = em.find(TableOne.class, 1L);
em.remove(tableOne);
tx.commit();
}
public void testRemoveTableTwo() {
EntityTransaction tx = em.getTransaction();
tx.begin();
TableTwo tableTwo = em.find(TableTwo.class, 2L);
em.remove(tableTwo);
tx.commit();
}
}
And finally the persistence.xml:
Code:
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="Test" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>entity.TableOne</class>
<class>entity.TableTwo</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" />
<property name="hibernate.connection.driver_class"
value="oracle.jdbc.driver.OracleDriver" />
<property name="hibernate.connection.username" value="user" />
<property name="hibernate.connection.password" value="password" />
<property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521:orcl" />
<property name="hibernate.cache.provider_class"
value="org.hibernate.cache.HashtableCacheProvider" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.use_sql_comments" value="true" />
</properties>
</persistence-unit>
</persistence>
Any help much appreciated.