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]