Hi, I'm following the example in the the
Java Persistence With Hibernatebook on p. 307 "Mapping the join table to a collection of components. Below, I have a QPolicy class which can have a number of QRules assigned to it. There is an intermediate association class between a QPolicy and a QRule called QPolicyRule that also contains a severity. I've annotated it precisely how the book lists, which is supposed to manage cascades for me automatically. However, if I write the following code:
Code:
EntityTransaction tx = em.getTransaction();
QPolicy policy = new QPolicy(...);
policy.addRule(new QRule(...));
tx.begin();
em.persist(policy);
tx.commit();
What I'm expecting is that hibernate will create a new row in QPolicy table, a new row in QPolicyRule, and a new row in QRule table. What actually happens is that hibernate creates a new row in QPolicy, QPolicyRule, and then throws an exception complaining that QRule is transient and that I must persist that object separately first.
But this is precisely the whole reason for embedding the collection of components, as the book clearly states:
Quote:
The advantage of a collection of components is clearly the implicit lifecycle of the link objects. To create an association between a Category (QPolicy) and an Item (QRule), ad a new CategorizedItem (QPolicyRule) instance to the collection. To break the link, remove the element from the collection. No extra cascading settings are required, and the Java code is simplified.
What am I missing here?
Hibernate version: hibernate-3.2.4.ga
hibernate-commons-annotations-3.0.0.ga
hibernate-entitymanager-3.3.1.ga
hibernate-annotations-3.3.0.ga
Mapping documents:Code:
@Entity
@Table(name="QPOLICY")
public final class QPolicy {
/** The set of associations with Rules for this policy. */
@org.hibernate.annotations.CollectionOfElements
@JoinTable(name = "QPOLICY_QRULE", joinColumns = @JoinColumn(name = "QPOLICY_ID"))
private Set<QPolicyRule> policyRules = new HashSet<QPolicyRule>();
}
Code:
@Embeddable
public final class QPolicyRule {
/** The QPolicy that is part of this association. */
@org.hibernate.annotations.Parent
private QPolicy QPolicy;
/** The rule that is part of this association. */
@ManyToOne
@JoinColumn(name = "QRULE_ID", nullable = false, updatable = false)
private final QRule rule;
/** The severity of this rule in the context of this QPolicy. */
@Enumerated(EnumType.STRING)
@Column(name = "SEVERITY", nullable = false, updatable = false)
private final Severity severity;
}
The class QRule has no interesting mappings that are relevant, as it has no direct associations to QPolicy or QPolicyRule
Full stack trace of any exception that occurs:Quote:
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.qcommon.repository.QRule
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397)
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:87)
at org.hibernate.type.ComponentType.nullSafeSet(ComponentType.java:307)
Name and version of the database you are using: hsqldb-1.8.0.7
The generated SQL (show_sql=true):Code:
Hibernate:
insert
into
QPOLICY
(AUTO_VERSION, DESCRIPTION, NAME, role, VERSION, QPOLICY_ID)
values
(?, ?, ?, ?, ?, ?)
Code:
Hibernate:
insert
into
QPOLICY_QRULE
(QPOLICY_ID, QRULE_ID, SEVERITY)
values
(?, ?, ?)
Note that it does
not do the insert into the QRULE table, the next thing I get in the log debug is the exception complaining that the QRule object is transient. I know the object is transient, but I thought what the book describes would manage it in a cascading style.