Administrator Administrator [
http://community.jboss.org/people/admin] modified the
document:
"EJB3 relationships"
To view the document, visit:
http://community.jboss.org/docs/DOC-13222
--------------------------------------------------------------
This page gives some HowTos about handling EJB3 relationships, and it points to some
common problems and error messages.
It is written by a JBoss user, not a guru ;-).
For questions and comments mail me at: wolfgang DOT knauf AT gmx DOT de
h1. Declaration of relationships
h2. Datatype
It sounds easy to use lists (implementations of java.util.List) for the "to
many" part of the relationship. This is normally no problem, but in combination with
FetchType.EAGER it might result in deployment errors (see below).
Those problems do not occur, if a java.util.Set is used. In most use cases, this is the
better approach, because in a "to many" relation, there will be probably no
duplicates. Only if duplicates are allowed, a java.util.List should be used.
h2. "mappedBy"
To create a bidirectional relationship, you have to connect the two sides with a
"mappedBy" attribute on one of the two sides of the relation. The
"mappedBy" specifies the property name of the other side. The property without
"mappedBy" is the *defining side* of the relation.
Example:
@Entity()
public class ParentBean implements Serializable
{
private Set<ChildBean> listChilds = new HashSet<ChildBean>();
@OneToMany(*mappedBy="parent"*)
public Set<ChildBean> getChilds()
{
return this.listChilds;
}
}
@Entity()
public class ChildBean implements Serializable
{
@ManyToOne ()
public ParentBean get*Parent*()
{
return this.parent;
}
}
If you want to specify e.g. a @JoinTable, you must do this on the defining side of the
relation. On the "mappedBy" side, this annotation will have no effect (not even
an error message).
h2. FetchType
There are two fetch types available:
* FetchType.EAGER: the relationship fields should be fetched immediately when loading the
parent field.
* FetchType.LAZY: the relationship fields should be fetched when necessary (e.g. when they
are accessed).
Both sides have drawbacks, so there is no easy decision for either of them.
*FetchType.EAGER:*
* Benefit: easier to code, because the field is always populated by the server.
* Drawback: performance: if the parent relationship is loaded, all its relationship fields
are populated, too.
* Drawback: If an entity has more than one relationship field, this exception may occur on
publish:
javax.persistence.PersistenceException: [PersistenceUnit: ...] Unable to build
EntityManagerFactory
at
org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:677)
at
org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:132)
at
org.jboss.jpa.deployment.PersistenceUnitDeployment.start(PersistenceUnitDeployment.java:311)
...
Caused by: *org.hibernate.HibernateException: cannot simultaneously fetch multiple bags*
at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:89)
at org.hibernate.loader.entity.EntityLoader.(EntityLoader.java:98)
...
For an explanation and workarounds see here:
http://jroller.com/eyallupu/entry/hibernate_exception_simultaneously_fetc...
http://jroller.com/eyallupu/entry/hibernate_exception_simultaneously_fetc....
In short: try to use LAZY where possible, or replace java.util.List collections by
java.util.Set.
*FetchType.LAZY:*
* Benefit: performance: on loading the entity, only the data of the entity itself is
fetched. No relationship fields are populated.
* Drawback: the relationship fields can only be lazily loaded when the entity bean is
under entity manager control ("attached"). As soon as it becomes
"detached", accessing an unloaded relationship field will result in this
exception:org.hibernate.LazyInitializationException: failed to lazily initialize a
collection of role: my.package.ParentBean.childs, no session or session was closed
org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
...
The workaround for this is to force loading of the relationship fields while it is still
attached. This must happen in the session bean where it is loaded. So, if you have a
method "findById" in your session bean, add the bold line: public ParentBean
findById (Integer int_ParentId)
{
ParentBean parentBean = this.entityManager.find(ParentBean.class, int_ParentId);
*if (parentBean != null)
{
parentBean.getChilds().size();
}*
return parentBean;
}
I included a NULL check, because "find" returns NULL if the ID is invalid.
Important: a call to parentBean.getChilds() does not fetch the childs. You have to access
the content of the collection to fetch them. I did this by calling "size()".
This prefetching has to be handled with care: check your use cases and verify that
preloading makes sense. Otherwise, you might load unnecessary data, and this slows your
app down.
*Default Values:*
The defaults for the "fetch" attribute differ beetween the different
relationship types.
* @OneToMany: LAZY
* @ManyToOne: EAGER
* @OneToOne: EAGER
* @ManyToMany: LAZY
*EntityManager.find versus getReference*
"find" returns NULL if the entity is not found. "getReference" throws
a javax.persistence.EntityNotFoundException, if nothing is found.
There is another major difference beetween the two: "getReference" does *not*
load relationship fields, even if they are set to "EAGER". This results in the
org.hibernate.LazyInitializationException if the entity gets detached and the relationship
is accessed. On the other hand, it might improve performance if you have a bit of session
bean code, where you don't need the relationship fields.
h2. CascadeType
*CascadeType.REMOVE*
This is the one which requires most thinking about. It depends on your use case, whether
it makes sense to delete the childs, when the parent is deleted. For
@ManyToMany-Relationships, a CascadeType.REMOVE is always wrong.
A CascadeType.REMOVE does *not* mean "remove connection beetween child and parent,
if parent is deleted", it means "delete child".
Assume you have a one-to-many relation beetween parent and child without
CascadeType.REMOVE, and you want to delete a parent with childs, you will see this (quite
meaningless) exception in the server console:
13:50:10,687 WARN [JDBCExceptionReporter] SQL Error: 0, SQLState: null
13:50:10,687 ERROR [JDBCExceptionReporter] failed batch
13:50:10,687 ERROR [AbstractFlushingEventListener] Could not synchronize database state
with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at
org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:126)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:114)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
...
Caused by: java.sql.BatchUpdateException: failed batch
at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source)
at
org.jboss.resource.adapter.jdbc.CachedPreparedStatement.executeBatch(CachedPreparedStatement.java:474)
...
13:50:10,703 WARN [arjLoggerI18N]
[com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator_2]
TwoPhaseCoordinator.beforeCompletion - failed for
com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple@1fb6021
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException:
Could not execute JDBC batch update
at
org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
at
org.hibernate.ejb.AbstractEntityManagerImpl$1.beforeCompletion(AbstractEntityManagerImpl.java:513)
at
com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:101)
at
com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:263)
at
com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:86)
at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:177)
...
"server.log" gave me a bit more of information:
2008-12-28 13:50:10,687 DEBUG [org.hibernate.util.JDBCExceptionReporter]
(http-127.0.0.1-8080-1) Could not execute JDBC batch update [delete from ParentBean where
id=?]
...(same stacktrace as above)
If it is intended to keep the child (no cascading of the deletion), then you first have to
remove the child from the relationship (see chapter "Managing the (bidirectional)
relationship - Deleting an item" below).
*CascadeType.PERSIST and CascadeType.MERGE*
There is not much to say about them. I think you can set those flags whereever you want
without drawbacks.
Leaving those flags might be a problem, if your use cases allows modification of parent
and childs in one step (so that saving the parent should also save the childs). If you
dont' activate this flag, then only the parent is saved, but not the childs.
*CascadeType.REFRESH*
Same as above: I think you can set those flags whereever you want without drawbacks.
*CascadeType.ALL*
Combination of all the above, so set it only, if you are sure about CascadeType.REMOVE.
h1. Managing the (bidirectional) relationship
This chapters describes how to handle bidirectional relationships in code.
The major clue is: always perform actions on both sides of a relation!
My general advice is to place manipulation of relationships in the session bean layer. If
you build methods like "addChildToParent(ParentBean parent, ChildBean child)" or
"removeChildToParentMapping (ParentBean parent, ChildBean child)", then you have
encapsulated the relationship handling in one place. And more important: if you load the
entitities from database in the beginning of this method, you will not have problems with
detached entitities.
*Adding an item*
Here is a sample for a @OneToMany relationship (a parent has many childs): Two childs
shall be added to the parent.
ParentBean parent = ...;
ChildBean child1 = ...;
parent.getChilds().add(child1);
child1.setParent(parent);
ChildBean child2 = ...;
parent.getChilds().add(child2);
child2.setParent(parent);
this.entityManager.merge(parent);
Important: add the new child to the child list of the parent, *and* set the parent in the
child.
If the line child1.setParent(parent); was left, the childs database column
"parentid" would be NULL, thus the child would not be connected to the parent.
The same approach is used for @ManyToMany relationships:
LeftBean left = ...;
RightBean right = ...;
left.getRights().add(right);
right.getLefts().add(left);
this.entityManager.merge(left);
*Removing an item*
If you want to remove an item from a relationship (without deleting either entity bean),
you have to perform this code:
ParentBean parent = ...;
ChildBean child = ...;
parent.getChilds().remove(child);
child.setParent(null);
this.entityManager.merge(parent);
This leaves the child in database with the "ParentID" column being NULL.
If you leave the line child.setParent(null);, you will be surprised to find out the child
and parent are still connected.
For a @ManyToMany relationship, it might look like this:
LeftBean leftBean = this.entityManager.find(LeftBean.class, int_LeftId);
RightBean rightBean = this.entityManager.find(RightBean.class, int_RightId);
leftBean.getRights().remove(rightBean);
rightBean.getLefts().remove(leftBean);
this.entityManager.persist(leftBean);
*Deleting an item*
If you actually want to delete an item, and you don't cascade removal, you may see two
kinds of exceptions, depending on the kind of relationship you use.
*1) Deleting the parent side*
If you delete a parent with childs, you will see the exceptions shown in the above chapter
"Declaration of relationships - CascadeType - CascadeType.REMOVE".
The solution is to first remove the relationship before deleting the object. Here is a
sample for a @ManyToMany relationship:
public void deleteLeft(Integer int_LeftId)
{
LeftBean leftBean = this.entityManager.find(LeftBean.class, int_LeftId);
for (RightBean rightBean : leftBean.getRights())
{
rightBean.getLefts().remove(leftBean);
}
this.entityManager.remove(leftBean);
}
After fetching the LeftBean, I loop over the relation to the RightBean. For each
relationship element RightBean, I remove the LeftBean from the collection. Afterwards, I
can persist it.
*2) Deleting the child side*
This exception seems to happen only on the "ToOne" side of a
"@ManyToOne" relationship.
javax.ejb.EJBTransactionRolledbackException: Transaction rolled back
org.jboss.ejb3.tx.Ejb3TxPolicy.handleEndTransactionException(Ejb3TxPolicy.java:54)
org.jboss.aspects.tx.TxPolicy.endTransaction(TxPolicy.java:175)
org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:87)
org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:190)
org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
...
code to your session bean
...
Caused by: javax.transaction.RollbackException:
[com.arjuna.ats.internal.jta.transaction.arjunacore.commitwhenaborted]
[com.arjuna.ats.internal.jta.transaction.arjunacore.commitwhenaborted] Could not commit
transaction.
com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1426)
com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:135)
com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
org.jboss.aspects.tx.TxPolicy.endTransaction(TxPolicy.java:170)
org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:87)
org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:190)
org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
...
Caused by: javax.persistence.EntityNotFoundException: deleted entity passed to persist:
[org.jboss.wiki.relationships.eager.ChildEagerBean#]
org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:598)
org.hibernate.ejb.AbstractEntityManagerImpl$1.beforeCompletion(AbstractEntityManagerImpl.java:513)
com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:101)
com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:263)
com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:86)
com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:177)
com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1414)
com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:135)
com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
org.jboss.aspects.tx.TxPolicy.endTransaction(TxPolicy.java:170)
The solution is the same as above: remove the child from the parents collection first:
public void deleteChild (Integer int_ChildId)
{
ChildBean child = this.entityManager.getReference(ChildBean.class, int_ChildId);
child.getParent().getChilds().remove(child);
this.entityManager.remove(child);
}
h1. Entity Bean as JoinTable
This chapter describes how to create a Entity Bean "ParentToChildBean" for a
ManyToMany-mapping beetween two other entities named "ParentBean" and
"ChildBean" (so that both of those just form an OneToMany to the mapping entity.
If the JPA specification allowed us to declare IDs on relation fields, this would be easy,
but unfortunately something like this declaration in "ParentToChildBean" is
illegal:
@ManyToOne
@Id
public ChildBean getChild()
{
..
}
*The fields of the Entity:*
So, we have to do use a trick: we declare the relation, and we duplicate the primary key
fields of the "ParentBean" and "ChildBean" entities. This sample
assumes that both have an Integer Id.
private Integer iParentId;
private Integer iChildId;
private Parent parent;
private Child child;
@Id
public Integer getParentId()
{
...
}
@Id
public Integer getChildId()
{
...
}
@ManyToOne
public ParentBean getParent()
{
...
}
@ManyToOne
public ChildBean getChild()
{
...
}
//TODO: setters
*Cleaning up the database structure:*
This results in one problem: the Id fields result in database columns, and the
relationship fields create foreign key columns, too. So, we have to tell the server
1) to use the same column names for Id fields and relationships and
2) not to add the relationship fields to insert/update statements.
This is a sample for the "parentId", same applies to the "childId".
@Id
@Column(name="PARENTID")
public Integer getParentId()
{
}
@ManyToOne
@JoinColumn(name="PARENTID", insertable=false, updatable=false)
public ParentBean getParent()
{
...
}
The annotation "@JoinColumn" declares that the relationship columns should map
to a column named "PARENTID", which is also the name of the "parentId"
field. The attributes "insertable" and "updateable" must be set to
"false" so that JBoss does not add them twice to insert/updates. If they were
not set, this would result in this error:
java.sql.SQLException: Column count does not match in statement [insert into
ParentToChildBean (PARENTID, CHILDID, ..., other fields, ..., PARENTID, CHILDID) values
(?, ?, ..., ?, ?)]
at org.hsqldb.jdbc.Util.throwError(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.(Unknown Source)
at org.hsqldb.jdbc.jdbcConnection.prepareStatement(Unknown Source)
...
*Cleaning up code:*
There is one more thing to handle: the id fields may not differ from the relationship
field values. So, I advice to
1) declare getters and setters of the id fields as "private". The relationship
shall only be accessed through the one-to-many properties. The private setters are
required anyway, because JBoss uses them to set the values on loading the entity. We might
have used field based property access to avoid them, but I prefer the property based field
access.
2) the setters of the one-to-many properties must update the id fields!
Here is the full code:
@Entity
@IdClass(value=ParentToChildPK.class)
public class ParentToChildEntity implements Serializable
{
private Integer iParentId;
private Integer iChildId;
private Parent parent;
private Child child;
@Id
@Column(name="PARENTID")
*@SuppressWarnings("unused")
private* Integer getParentId()
{
return this.iParentId;
}
*@SuppressWarnings("unused")
private* void setParentId(Integer iParentIdNew)
{
this.iParentId = iParentIdNew;
}
@Id
@Column(name="CHILDID")
*@SuppressWarnings("unused")
private* Integer getChildId()
{
return this.iChildId;
}
*@SuppressWarnings("unused")
** private* void setChildId(Integer iChildIdNew)
{
this.iChildId = iChildIdNew;
}
@ManyToOne
@JoinColumn(name="PARENTID", insertable=false, updatable=false)
public ParentBean getParent()
{
return this.parent;
}
public void setParent(ParentBean parentNew)
{
this.parent = parentNew;
*if (parentNew != null)
this.iParentId = parentNew.getId();
else
this.iParentId = null;*
}
@ManyToOne
@JoinColumn(name="CHILDID", insertable=false, updatable=false)
public ChildBean getChild()
{
return this.child;
}
public void setChild(ChildBean childNew)
{
this.child = childNew;
*if (childNew != null)
this.iChildId = childNew.getId();
else
this.iChildId = null;*
}
}
*ID Class:*
Now we are nearly done. The only thing left is an id class. This one contains an exact
copy of the id fields! The names *must* not differ!
public class ParentToChildPK implements Serializable
{
private Integer iParentId;
private Integer iChildId;
public Integer getParentId()
{
return this.iParentId;
}
public void setParentId(Integer iParentIdNew)
{
this.iParentId = iParentIdNew;
}
public Integer getChildId()
{
return this.iChildId;
}
public void setChildId(Integer iChildIdNew)
{
this.iChildId = iChildIdNew;
}
}
The Id class has to be declared on the entity:
@Entity()
*(a)IdClass(**ParentToChildPK.class)*
public class ParentToChildBean implements Serializable
{
...
We need the Id class because if it was missing,...
* ...saving a ParentToChildBean would result in this error:
14:32:44,281 WARN [JDBCExceptionReporter] SQL Error: 0, SQLState: null
14:32:44,281 ERROR [JDBCExceptionReporter] failed batch
14:32:44,281 ERROR [AbstractFlushingEventListener] Could not synchronize database state
with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at
org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:126)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:114)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167)
...
Caused by: java.sql.BatchUpdateException: failed batch
at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source)
at
org.jboss.resource.adapter.jdbc.CachedPreparedStatement.executeBatch(CachedPreparedStatement.java:476)
at
org.jboss.resource.adapter.jdbc.WrappedStatement.executeBatch(WrappedStatement.java:774)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 65 more
If sql logging is activated, the real reason is clear:
insert into ParentToChildBean (PARENTID, other fields) values (?, ...)
The field "CHILDID" is missing.
* Loading of a "ParentToChildBean" would not be possible, because
"EntityManager.getReference" needs a primary key class for concatenated primary
keys.
h2. Alternate way: EmbeddedId
The previous sample had one drawback: the id fields had to be duplicated to entity and
primary key class.
*Embedded ID class:*
To avoid this, we create an EmbeddedId class. This one is nearly the same as the primary
key class:
import javax.persistence.Embeddable;
*@Embeddable*
public class ParentToChildPK implements Serializable
{
private Integer iParentId;
private Integer iChildId;
***@Column(name="PARENTID")*
public Integer getParentId()
{
return this.iParentId;
}
public void setParentId(Integer iParentIdNew)
{
this.iParentId = iParentIdNew;
}
*@Column(name="CHILDID")*
public Integer getChildId()
{
return this.iChildId;
}
public void setChildId(Integer iChildIdNew)
{
this.iChildId = iChildIdNew;
}
}
There are two differences to the primary key class:
* It has the annotation "javax.persistence.Embeddable"
* The column names are defined on this class (but could be overridden if the embedded id
was used for different entities)
*Entity "ParentToChildBean":*
The entity code is a bit simpler now:
import javax.persistence.EmbeddedId;
...
@Entity
public class ParentToChildBean implements Serializable
{
*private* *ParentToChildPK pk;*
private Parent parent;
private Child child;
*@EmbeddedId*
@SuppressWarnings("unused")
private ParentToChildPK getPk()
{
return this.pk;
}
@SuppressWarnings("unused")
private void setPk(ParentToChildPK pk)
{
this.pk = pk;
}
@ManyToOne
@JoinColumn(name="PARENTID", insertable=false, updatable=false)
public ParentBean getParent()
{
return this.parent;
}
public void setParent(ParentBean parentNew)
{
this.parent = parentNew;
if (this.pk == null)
{
this.pk = new ParentToChildPK ();
}
if (parentNew != null)
{
this.pk.setParentId(parentNew.getId());
}
else
{
this.pk.setParentId(null);
}
}
@ManyToOne
@JoinColumn(name="CHILDID", insertable=false, updatable=false)
public ChildBean getChild()
{
return this.child;
}
public void setChild(ChildBean childNew)
{
this.child = childNew;
if (this.pk == null)
{
this.pk = new ParentToChildPK ();
}
if (childNew != null)
{
this.pk.setChildId(childNew.getId());
}
else
{
this.pk.setChildId(null);
}
}
}
The embedded id (with annotation "(a)javax.persistence.EmbeddedId") is a member
variable now and has getter and setter. Those are private for the same reasons as in the
above sample.
The relationship fields don't change. But the setter for "child" and
"parent" are more complex now, because they have to write the new ID to the
embedded id class, and this primary key class variable maybe has to be created first.
But anyway, less code has to be written, and there are no duplicate fields (which would be
fatal, if a field was renamed and only one variable was changed).
--------------------------------------------------------------
Comment by going to Community
[
http://community.jboss.org/docs/DOC-13222]
Create a new document in EJB3 at Community
[
http://community.jboss.org/choose-container!input.jspa?contentType=102&am...]