Giddyap, adding the MERGE CascadeType did it.

Note, in my previous post I left off the cascade and fetch attribs of the
OneToMany by accident. This is what it should have looked like:


@OneToMany(cascade = { CascadeType.ALL },fetch = FetchType.EAGER)
@JoinColumn(name="objectA")
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.DELETE_ORPHAN,
org.hibernate.annotations.CascadeType.SAVE_UPDATE})
public Set<AAndBJoin> getAAndBJoins ()


Note that in the OneToMany I already had cascade = { CascadeType.ALL },
which in theory should include the MERGE CascadeType. And this seemed to be
working when updating an existing ObjectA with elements in the AAndBJoin
Collection.

But when creating a new ObjectA with elements in the AAndBJoin Collection it
did not work. So, as per your advice, I added the Hibernate MERGE
Annotation:


@OneToMany(cascade = { CascadeType.ALL },fetch = FetchType.EAGER)
@JoinColumn(name="objectA")
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.DELETE_ORPHAN,
org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.MERGE })
public Set<AAndBJoin> getAAndBJoins ()


And it works! 

As per your comment that for everything to work 100% 'AAndBJoin must own the
relationship from AAndBJoin to ObjectB', I guess I should use the changes I
made in ObjectA to make it the owner of the AAndBJoin relationship as an
example.

In order to make ObjectA the owner of the AAndBJoin relationship I did the
following:

1. in ObjectA get rid of the 'mappedBy=objectA' attribute of the @OneToMany
Annotation
2. in ObjectA add the @JoinColumn(name='idObjectA'). 
3. in AAndBJoin add 'insertable=false, updatable=false' to the
@JoinColumn(name='idObjectA') Annotation.

Well, AAndBJoin looks like this:

@ManyToOne
@JoinColumn(name="idObjectB")
public ObjectB getObjectB()

And ObjectB looks like this:

@OneToMany(mappedBy="objectB")
public Set<AAndBJoin> getAAndBJoins ()

So I'm not sure quite what to do. The class I want to be the owner
(AAndBJoin) doesn't have a 'mappedBy' attribute to get rid of because
'mappedBy' is only for OneToMany. And it already has a Join column, so I
don't have to add one. And in ObjectB, I don't have a Join column to add the
insertable and updateable attributes to ...

To tell you the truth I am hesitant (read: scared to death!) to change it
now that it works. But if you could give me a hint as to how to change these
mappings to make AAndBJoin the owner of the relationship with ObjectB,
perhaps I'll give it a try.

Jeez, what a long, strange trip it's been. I feel as though I owe something
to the AppFuse community for all the help I've received. Do you think it
would be worthwhile to do a tutorial for AppFuse 2, something along the
lines of the old Weblog tutorial, but using 'complex' objects like this one?
In other words, a 'simulated' ManyToMany using OneToMany and a Model Object
to represent the association? Something like:

Weblog -> OneToMany -> WeblogPost
Post -> OneToMany -> WeblogPost
WeblogPost -> ManyToOne -> Weblog
WeblogPost -> ManyToOne -> Post

I know I sure could have used a tutorial, and I don't think my situation is
anything out of the ordinary. Having attributes in a Join table must be
pretty common. In my project alone I have 6!!

If there is any interest I'll try to throw it together.

Thanks again listers, and specifically Mssr. Horowitz.

Bob

, with a ManyToMany


Mike Horwitz wrote:
> 
> On 10/31/07, syg6 <[EMAIL PROTECTED]> wrote:
>>
>>
>> Well, your advice sounds good but there's one thing I don't understand.
>> You
>> said that:
>>
>>
>> When you perform an operation on the entity that owns
>> the relationship the relationship will change. Perform an operation on
>> the
>> other side of the relationship and nothing will happen to the
>> relationship
>> itself
>>
>>
>> All changes made to the Collection of ObjectBs associated with ObjectA
>> are
>> done from the ObjectA side of the relation, in objectaform.jsp. When I
>> update an ObjectA, Hibernate is automatically updating changes in the
>> Collection of ObjectBs -- inserts, updates, deletes (except when I delete
>> ALL elements from the Collection, in which case I update the Collection
>> manually, as discussed in previous messages). And when I create a new
>> ObjectA, Hibernate is at least trying to save Collection elements
>> properly.
>> The only problem is that it can't properly save them because it doesn't
>> yet
>> have an idObjectA to correctly populate the idObjectA foreign key column
>> in
>> the aandbjoin table.
> 
> 
> As previously explained there are exceptions where it appears to work as
> you
> have observed above. However if you want it all to work, and work
> properly,
> then you MUST make ObjectA the owner of the relationship. One other word
> of
> caution in your case: If you want it all to flow neatly from ObjectA down
> to
> ObjectB through the realtionship on AAndBJoin, then ObjectA must own the
> relationship from ObjectA to AAndBJoin and AAndBJoin must own the
> relationship from AAndBJoin to ObjectB. Also note that saving a new
> ObjectB
> with a new set of associations will not work.
> 
> So it seems to me that Hibernate already at least thinks ObjectA is the
>> owner, otherwise it wouldn't be making inserts, updates and deletes in
>> the
>> aandbjoin table, no?
> 
> 
> No.
> 
> 
>> At any rate, I tried changing the mapping as section '2.2.5.3.2.1.
>> Bidirectional' recommends:
>>
>> ObjectA class:
>>
>> @OneToMany
>> @JoinColumn(name="idObjectA")
>> @org.hibernate.annotations.Cascade(
>> { org.hibernate.annotations.CascadeType.DELETE_ORPHAN,
>>    org.hibernate.annotations.CascadeType.SAVE_UPDATE
>> })
>> public Set<AAndBJoin> getAAndBJoins ()
>>
>> AAndBJoin class:
>>
>> @ManyToOne
>> @JoinColumn(name="idObjectA", insertable=false, updatable=false)
>> public ObjectA getObjectA()
>>
>> And now when I save a new ObjectA with associated ObjectBs I get a
>> 'object
>> references an unsaved transient instance - save the transient instance
>> before flushing' error.
>>
>> Anything else I need to do?
> 
> 
> Yes. The Hibernate operation called on a save in the AppFuse default DAO
> is
> merge(), so you need to make sure merge is cascaded.
> 
> Mike
> 
> Thanks!
>> Bob
>>
>>
>> Mike Horwitz wrote:
>> >
>> > On 10/31/07, syg6 <[EMAIL PROTECTED]> wrote:
>> >
>> >>
>> >> I used both Hibernate docs and Hibernate in Action to come up with
>> this
>> >> mapping, it seems to be the 'standard' way to doing this (mapping a
>> >> relation
>> >> with attributes, in my case, quantity). The example you cited is a
>> >> relation
>> >> without attributes, so I don't think it's suitable for me. From
>> Hibernate
>> >> in
>> >> Action:
>> >>
>> >> 'you should ask whether it might be better to map CategorizedItem
>> >> [AAndBJoin] as an entity class and use two one-to-many associations.'
>> >
>> >
>> > In full agreement! What you end up with is not a many-to-many
>> > relationship,
>> > but two one-to-many relationships with an additional entity (at least
>> in
>> > Hibernate land). The example relates to each of these one-to-many
>> > relationships.
>> >
>> >
>> >
>> >> At any rate, I posted just before seeing your message. The problem
>> with
>> >> deleting all elements in the Collection (which was 99% of my last
>> post!)
>> >> is
>> >> resolved.
>> >>
>> >> The only problem that remains is when I save a new ObjectA and at the
>> >> same
>> >> time associate ObjectBs, that I don't have an idObjectA to assign to
>> the
>> >> FK
>> >> column of the aandbjoins table, so the AAndBJoin Objects are
>> improperly
>> >> saved (idObjectA is null), and are not loaded when I load the ObjectA
>> >> (see
>> >> my last message). Your suggestion didn't have anything to do with this
>> >> problem did it?
>> >
>> >
>> > It did. In Hibernate one side of the relationship is designated as the
>> > owner
>> > of the relationship. When you perform an operation on the entity that
>> owns
>> > the relationship the relationship will change. Perform an operation on
>> the
>> > other side of the relationship and nothing will happen to the
>> relationship
>> > itself. There may be some exceptions to this rule, but I have found
>> that
>> > if
>> > I stick to it all works more or less as expected.
>> >
>> > In your terms it seems you want ObejctA to own the relationship (the
>> > OneToMany side). Your annotations need to change to reflect this.
>> >
>> >
>> >
>> >> I have tried making the two foreign key columns in AAndBJoin
>> >> nullable=false,
>> >> like this:
>> >
>> >
>> > This is not enough on its own to make the one to many the owner of the
>> > relationship:
>> >
>> > "To map a bidirectional one to many, with the one-to-many side as the
>> > owning
>> > side, you have to remove the mappedBy element and set the many to one
>> > @JoinColumn as insertable and updatable to false. This solution is
>> > obviously
>> > not optimized and will produce some additional UPDATE statements."
>> >
>> > Also note from the example that a JoinColumn annotation is required on
>> > both
>> > sides of the relationship.
>> >
>> > Mike.
>> >
>> >
>> >> public class AAndBJoin
>> >> {
>> >> private Long id;
>> >> private ObjectA objectA;
>> >> private ObjectB objectB;
>> >>
>> >> @Column(name="idAAndBJoin")
>> >> @Id @GeneratedValue(strategy = GenerationType.AUTO)
>> >> public Long getId()
>> >>
>> >> @ManyToOne
>> >> @JoinColumn(name="idObjectA",nullable=false)
>> >> public ObjectA getObjectA()
>> >>
>> >> @ManyToOne
>> >> @JoinColumn(name="idObjectB",nullable=false)
>> >> public ObjectB getObjectB()
>> >>
>> >> but when I save a new ObjectA I get an 'not-null property references a
>> >> null
>> >> or transient value' error.
>> >>
>> >> As you can see I am using an id as the Primary Key. If I were to use a
>> >> Composite Primary Key -- idObjectA and idObjectB -- would this 'force'
>> >> Hibernate to fill-in values for these ids, even when saving a new
>> >> ObjectA?
>> >>
>> >> Thanks!
>> >> Bob
>> >>
>> >>
>> >>
>> >> Mike Horwitz wrote:
>> >> >
>> >> > I think your issue is that in your case you want the one-to-many
>> side
>> >> of
>> >> > the
>> >> > relationship to own the relationship. In order to make this happen
>> you
>> >> are
>> >> > going to have to change your annotations. The following section of
>> the
>> >> > Hibernate manual should help: http://tinyurl.com/ypdln3 look for
>> >> section
>> >> > 2.2.5.3.2.1.
>> >> >
>> >> > Mike
>> >> >
>> >> > On 10/31/07, syg6 <[EMAIL PROTECTED]> wrote:
>> >> >>
>> >> >>
>> >> >> Well what's curious is that with my other 'normal' ManyToMany (see
>> my
>> >> >> previous post. ObjectA has a Collection of ObjectC) if from my
>> ObjectA
>> >> >> form
>> >> >> I delete all ObjectCs, the 'objectcs' Request parameter is 'null'
>> and
>> >> >> Hibernate correctly deletes all ObjectC objects.
>> >> >>
>> >> >> But with my ObjectB Collection, this does not work, I assume
>> because
>> I
>> >> am
>> >> >> doing a 'manual' ManyToMany, in other words ObjectA and ObjectB
>> have
>> a
>> >> >> OneToMany relation with AAndBJoin, and AAndBJoin has a ManyToOne to
>> >> both
>> >> >> ObjectA and ObjectB. The behavior should be the same, no? If I send
>> >> >> 'null',
>> >> >> it should delete all elements from the Collection, just as it does
>> >> with
>> >> >> my
>> >> >> ObjectC Collection. But it does not.
>> >> >>
>> >> >> At any rate, I've tried manually deleting all ObjectBs from the
>> >> >> Collection;
>> >> >> In my ObjectAFormController onSubmit() I tried the following (only
>> >> when
>> >> >> the
>> >> >> 'objectbs' Request parameter is null):
>> >> >>
>> >> >> 1. Load ObjectA from database (to get existing ObjectB Collection).
>> >> >> 2. Use ObjectBManager to get() each ObjectB
>> >> >> 3. Use ObjectBManager to remove() each ObjectB
>> >> >> 4. Call ObjectAManager.save()
>> >> >>
>> >> >> And I get a 'deleted object would be re-saved by cascade (remove
>> >> deleted
>> >> >> object from associations):' error, as I knew I would. And if I
>> instead
>> >> >> call
>> >> >> objectA.setObjectBs(null) I get a 'A collection with
>> >> >> cascade="all-delete-orphan" was no longer referenced by the owning
>> >> entity
>> >> >> instance' error.
>> >> >>
>> >> >> The only difference I can see between the Collection of ObjectBs
>> and
>> >> >> ObjectCs is that in my ObjectAFormController onSubmit(), after this
>> >> line:
>> >> >>
>> >> >> ObjectA objectA = (ObjectA) command;
>> >> >>
>> >> >> ... if in the objectaform.jsp I have deleted all of the ObjectC
>> >> elements
>> >> >> from the Collection, objectA.getObjectCs() is null. Correct! And
>> >> >> Hibernate
>> >> >> goes on to delete these ObjectBs from the database. But if I have
>> >> deleted
>> >> >> all of the ObjectB elements from the Collection,
>> objectA.getObjectBs
>> ()
>> >> is
>> >> >> NOT null; it has the 'old' data, the Collection as it existed
>> before
>> >> >> doing
>> >> >> the deletes. As such, Hibernate does not update the database. And I
>> >> >> assume
>> >> >> that's also why if I try to manually delete the elements, I am
>> getting
>> >> an
>> >> >> error.
>> >> >>
>> >> >> Both Collections have a Custom PropertyEditor. The difference is
>> that
>> >> >> ObjectC only implements setAsText():
>> >> >>
>> >> >> public void setAsText(String id)
>> >> >> {
>> >> >> ObjectC objectC = objectCManager.get(new Long(id));
>> >> >> setValue(objectC);
>> >> >> }
>> >> >>
>> >> >> whereas the Custom PropertyEditor for ObjectB, because I use my own
>> >> Model
>> >> >> Object (AAndBJoin) to represent the Join, and has to grab a bunch
>> of
>> >> >> values
>> >> >> from text and hidden fields in the form to create AAndBJoin
>> Objects,
>> >> >> implements setValue():
>> >> >>
>> >> >> public void setValue(Object object)
>> >> >> {
>> >> >> // array of values sent from text/hidden inputs in form
>> >> >> String[] strings = (String[])object;
>> >> >>
>> >> >> Set<AAndBJoin> aAndBJoins = new LinkedHashSet<AAndBJoin>();
>> >> >>
>> >> >> for (number of text/hidden inputs in the page)
>> >> >> {
>> >> >>    AAndBJoin aAndBJoin = new AAndBJoin();
>> >> >>    aAndBJoin.setId(new Long(strings[0]));
>> >> >>    aAndBJoin.setObjectA(objectAManager.get(new Long(strings[1])));
>> >> >>    aAndBJoin.setObjectB(objectBManager.get(new Long(strings[2])));
>> >> >>    aAndBJoin.setQuantity(new Integer(strings[3]));
>> >> >>    aAndBJoins.add(aAndBJoin);
>> >> >> }
>> >> >> super.setValue(aAndBJoins);
>> >> >> }
>> >> >>
>> >> >> Anyway, the point is if I delete all ObjectC elements, ObjectC's
>> >> Custom
>> >> >> PropertyEditor is not being called (because since it's null there's
>> no
>> >> >> reason to call it) and when I call objectA.getObjectCs() I get
>> null,
>> >> ok.
>> >> >> But
>> >> >> when I delete all ObjectB elements, even though its Custom
>> >> PropertyEditor
>> >> >> isn't called either, when I call objectA.getObjectBs() I get the
>> old
>> >> >> Collection info, before the delete. Which is why Hibernate doesn't
>> >> update
>> >> >> the database, and why I get errors if I try to delete them
>> manually.
>> >> >>
>> >> >> Odd, inconsistent behavior, no? It seems that since I am using my
>> own
>> >> >> Model
>> >> >> Object (AAndBJoin) to represent the relation, I need to do
>> something
>> >> else
>> >> >> to
>> >> >> make Hibernate aware that all elements have been deleted. But I
>> have
>> >> no
>> >> >> idea
>> >> >> what!
>> >> >>
>> >> >> Incidentally, any ideas about the other problem? Where when I
>> create
>> a
>> >> >> new
>> >> >> ObjectA and associate various ObjectB elements, those AAndBJoin
>> >> Objects
>> >> >> are
>> >> >> saved incorrectly, with idObjectA=null, because when I call
>> >> >> objectA.save()
>> >> >> I
>> >> >> still don't have an idObjectA?
>> >> >>
>> >> >> Thanks, sorry for the long post,
>> >> >> Bob
>> >> >>
>> >> >>
>> >> >> DNewfield wrote:
>> >> >> >
>> >> >> > syg6 wrote:
>> >> >> >> Something else I have noticed: deleting members of the AAndBJoin
>> >> >> >> Collection
>> >> >> >> seems to be working, more or less, now that I put the
>> DELETE_ORPHAN
>> >> >> >> Hibernate Annotation. But there is one case where it does NOT
>> work
>> >> --
>> >> >> >> when
>> >> >> >> you delete all of the elements. When you delete all of the
>> >> elements,
>> >> >> the
>> >> >> >> 'AAndBJoin' Request parameter is of course null. What Hibernate
>> >> should
>> >> >> do
>> >> >> >> in
>> >> >> >> this case is delete all elements. But what it in fact does, is
>> >> >> nothing.
>> >> >> >>
>> >> >> >> It seems that in order for Hibernate to properly delete elements
>> >> from
>> >> >> a
>> >> >> >> Collection, at least one element has to be sent in the Request.
>> >> Thus,
>> >> >> if
>> >> >> >> you
>> >> >> >> had 4 and deleted 3, 1 element is sent in the Request and
>> Hibernate
>> >> >> >> compares
>> >> >> >> the Collection before and after, and properly deletes 3. But if
>> you
>> >> >> >> delete
>> >> >> >> all 4, in the Request 'null' is received, Hibernate seems to do
>> no
>> >> >> >> comparison, and deletes nothing. Ugh.
>> >> >> >
>> >> >> > You need to detect this case, because assigning "null" to the the
>> >> >> > collection attribute is wrong--the appropriate answer is to
>> retrieve
>> >> >> the
>> >> >> > (hibernate managed) collection, and either remove each item from
>> it
>> >> or
>> >> >> > clear() the collection, then re-setting the (same) cleared
>> >> collection
>> >> >> to
>> >> >> > the attribute.
>> >> >> >
>> >> >> > -Dale
>> >> >> >
>> >> >> >
>> >> ---------------------------------------------------------------------
>> >> >> > To unsubscribe, e-mail: [EMAIL PROTECTED]
>> >> >> > For additional commands, e-mail: [EMAIL PROTECTED]
>> >> >> >
>> >> >> >
>> >> >> >
>> >> >>
>> >> >> --
>> >> >> View this message in context:
>> >> >>
>> >>
>> http://www.nabble.com/Many-to-many-collection-problem-tf4670322s2369.html#a13507645
>> >> >> Sent from the AppFuse - User mailing list archive at Nabble.com.
>> >> >>
>> >> >>
>> ---------------------------------------------------------------------
>> >> >> To unsubscribe, e-mail: [EMAIL PROTECTED]
>> >> >> For additional commands, e-mail: [EMAIL PROTECTED]
>> >> >>
>> >> >>
>> >> >
>> >> >
>> >>
>> >> --
>> >> View this message in context:
>> >>
>> http://www.nabble.com/Many-to-many-collection-problem-tf4670322s2369.html#a13508465
>> >> Sent from the AppFuse - User mailing list archive at Nabble.com.
>> >>
>> >> ---------------------------------------------------------------------
>> >> To unsubscribe, e-mail: [EMAIL PROTECTED]
>> >> For additional commands, e-mail: [EMAIL PROTECTED]
>> >>
>> >>
>> >
>> >
>>
>> --
>> View this message in context:
>> http://www.nabble.com/Many-to-many-collection-problem-tf4670322s2369.html#a13511405
>> Sent from the AppFuse - User mailing list archive at Nabble.com.
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: [EMAIL PROTECTED]
>> For additional commands, e-mail: [EMAIL PROTECTED]
>>
>>
> 
> 

-- 
View this message in context: 
http://www.nabble.com/Many-to-many-collection-problem-tf4670322s2369.html#a13513652
Sent from the AppFuse - User mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to