Well I am plugging away at scenario(2) ... as I figured, not trivial.
I looked at some of the examples of the <spring:bind> tag. It seems to be
the way to go. In my jsp I have the following:
<c:forEach var="ec" items="${establishment.establishmentContainers}"
varStatus="loopStatus">
<tr>
<td>
<spring:bind
path="establishment.establishmentContainers[${loopStatus.index}].establishment.id">
<input type="text" name="${status.expression}"
value="${status.value}">
</spring:bind>
</td>
<td>
<spring:bind
path="establishment.establishmentContainers[${loopStatus.index}].container.id">
<input type="text" name="${status.expression}"
value="${status.value}">
</spring:bind>
</td>
<td>
<spring:bind
path="establishment.establishmentContainers[${loopStatus.index}].quantity">
<input type="text" name="${status.expression}"
value="${status.value}">
</spring:bind>
</td>
</tr>
</c:forEach>
This creates a table full of indexed <input type="text"> elements in html:
establishmentContainers[0].establishment.id
establishmentContainers[0].container.id
establishmentContainers[0].quantity
establishmentContainers[1].establishment.id
establishmentContainers[1].container.id
establishmentContainers[1].quantity
... just as it should. And I can change the container and the quantity (the
establishment id doesn't change ...) But when I add a new row,
fuggedaboudit:
org.springframework.web.util.NestedServletException: Request processing
failed; nested exception is
org.springframework.beans.InvalidPropertyException: Invalid property
'establishmentContainers[2]' of bean class
[com.myco.myapp.model.Establishment]: Index of out of bounds in property
path 'establishmentContainers[2]'; nested exception is
java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
Not good. I created an EstablishmentContainerPropertyEditor and was
debugging setAsText(), but it never calls that method. It should because in
my EstablishmentFormController I am binding to it.
At any rate, I am not sure what I am doing wrong, if anyone has done this
before I'd appreciate the help. I can see that managing the deletion of
these objects is going to be a pain as well. I hope to be able to just wipe
the table for the current idEstablishment and just do inserts. Otherwise
it's going to be a nightmare.
Thanks in advance!
Bob
syg6 wrote:
>
> Hello again.
>
> Well I guess I'll deal with scenario (1) for now. The deal is that when
> you create a new Application you at least have to assign streetSection1.
> You can also assign streetSection2 and place but they are not obligatory.
> (You never create NEW StreetSections or Places ...) This is what I have.:
>
> @Entity
> public class Application
> ...
> private StreetSection streetSection1;
> private StreetSection streetSection2;
> private Place place;
> ...
> public Application()
> {
> this.streetSection1 = new StreetSection();
> this.streetSection2 = new StreetSection();
> this.place = new Place();
> }
> ...
> @ManyToOne
> @JoinColumn(name="idStreetSection1", nullable=false)
> public StreetSection getStreetSection1() ...
>
> @ManyToOne
> @JoinColumn(name="idStreetSection2", nullable=false)
> public StreetSection getStreetSection2() ...
>
> @ManyToOne
> @JoinColumn(name="idPLace", nullable=false)
> public Place getPlace() ...
>
> In the jsp I have the following:
>
> <form:hidden path="streetSection1" id="streetSection1" />
> <form:hidden path="streetSection2" id="streetSection2" />
> <form:hidden path="place" id="place" />
>
> I used to have it as 'streetSection1.id', 'streetSection2.id', etc. and
> had to put an <c:if> to check if streetSecion1.id was null or not. but now
> I have implemented getAsText() in the StreetSectionPropertyEditor and
> PlacePropertyEditor (I already had setAsText()):
>
> public void setAsText(String id)
> {
> StreetSection streetSection = streetSectionManager.get(new Long(id));
> setValue(streetSection);
> }
>
> public String getAsText()
> {
> StreetSection streetSection = (StreetSection) getValue();
> if (streetSection == null) return "";
> else return (streetSection.getId() == null) ? "" :
> streetSection.getId().toString();
> }
>
> PlacePropertyEditor is exactly the same. getAsText() returns the id of the
> existing StreetSection or Place, when loading an existing Application. If
> it's a new Application (and thus a new StreetSection and Place) it returns
> an empty string.
>
> And this is my ApplicationFormController initBinder():
>
> protected void initBinder()
> {
> StreetSectionPropertyEditor sspe = new StreetSectionPropertyEditor();
> sspe.setStreetSectionManager(streetSectionManager);
> binder.registerCustomEditor(StreetSection.class, "streetSection1",
> sspe);
> binder.registerCustomEditor(StreetSection.class, "streetSection2",
> sspe);
>
> PlacePropertyEditor ppe = new PLacePropertyEditor();
> ppe.setPlaceManager(placeManager);
> binder.registerCustomEditor(Place.class, "place", ppe);
> }
>
> This works for existing Applications. And it also works when creating a
> new Application if I fill in all three fields - streetSection1,
> streetSection2 and place. But when I create a new Application, and only
> assign streetSection1 or streetSection1 and streetSection2 I get this
> error:
>
> object references an unsaved transient instance - save the transient
> instance before flushing: com.myco.myapp.model.Place; nested exception is
> org.hibernate.TransientObjectException: object references an unsaved
> transient instance - save the transient instance before flushing:
> com.myco.myapp.model.Place
>
> Whew! And that's just scenario (1). I think I must be overlooking
> something simple, something that's implemented for StreetSection that is
> not implemented for Place. Since it only craps out when I send a null
> Place, it must be that it's not handling 'null' well. But in the jsp when
> I don't assign a Place I make sure the 'place' hidden is disabled (null)
> before submitting. Interestingly, setTextAs() is NOT called when a null
> value is sent. I guess this is correct. But it must mean that the
> PropertyEditor is not the problem because it's not even making it that far
> ...
>
> As far as scenario (2) goes I am looking into it ... unfortunately it
> doesn't seem a trivial thing to do. I thought that the <spring:bind> tag
> had been surpassed by the <form> tags for binding lists. In fact for
> simpler objects I am using <form:select>. But for the
> EstablishmentContainer objects I am going to have to add a 'row' to an
> html table with some <hidden> and text tags for each
> EstablishmentContainer added, and somehow get a PropertyEditor to assign
> them correctly. I will look into using the <bind> tag and a
> PropertyEditor. It's not going to be easy I think, but preferable to
> picking out all of the values from the request, manually instantiating and
> populating every EstablishmentContainer object and adding it to the List.
>
> I'll keep you posted!
>
> Bob
>
>
>
>
>
> or objects
>
> Mike Horwitz wrote:
>>
>> On 10/2/07, syg6 <[EMAIL PROTECTED]> wrote:
>>
>>>
>>> Thanks for your response Mike. I think you must have copy-pasted from
>>> another
>>> post or something, I am not working with a Country object. But I think
>>> your
>>> idea applies to both of my scenarios.
>>
>>
>> Correct. It is a copy and paste from the relevant post on the thread.
>>
>>
>>
>>> In scenario (1) I already have a PropertyEditor defined for
>>> StreetSection.
>>> It looks like this:
>>>
>>> public class StreetSectionPropertyEditor extends PropertyEditorSupport
>>> {
>>> public void setAsText(String id) {
>>> StreetSection streetSection = streetSectionManager.get(new
>>> Long(id));
>>> setValue(streetSection);
>>> }
>>
>>
>> Please be careful about handling null values - you may/may not need this
>> depending on the details of your implementation. You do need a getAsText
>> method to ensure the field gets properly populated with the id for an
>> existing establishment.
>>
>>
>>
>>> And it seems to work fine. I don't have a getAsText() defined and it has
>>> worked until now. But I am unsure of what I should use in the
>>> establishmentform.jsp. Should I use:
>>>
>>> <form:hidden path="streetSection" id="streetSection" /> or
>>
>>
>> This is the one you should use as it will use your property editor to
>> correctly bind your object to the existing instance in your database.
>>
>>
>>
>>> <form:hidden path="streetSection.id" id="streetSection.id" /> ?
>>
>>
>> This will create a new StreetSection and attempt to save it. Presumably
>> this
>> is not what you want?
>>
>>
>>
>>> It was working ok using 'streetSection' instead of 'streetSection.id'
>>> but
>>> I
>>> had problems when saving a NEW establishment (it worked fine when saving
>>> an
>>> existing Establishment curiously enough ...), 'can't save a transient
>>> object' error. Could this be because I don't have getAsText() defined?
>>
>>
>> Probably due to a cascade being set on the establishment object. Try
>> without
>> cascading save on the relationship. Judging by your jsp code this should
>> always be null for a new establishment object?
>>
>> Perhaps you could post the details of the error and we can try and help?
>>
>>
>>
>>> In scenario (2), how would I go about creating an
>>> EstablishmentContainerPropertyEditor? What I would need to do is tell
>>> Spring, 'Hey! Create an EstablishmentContainer object with '1' for
>>> idEstablishment, '2' for idContainer and '50' for quantity, and add it
>>> to
>>> the list of EstablishmentContainers, using values from the .jsp, either
>>> a
>>> multi-select or hiddens. But the setAsText() method takes only one
>>> string,
>>> usually an id. Do I need to override some other method in
>>> PropertyEditorSupport?
>>
>>
>> Property editors only do one thing: Text -> Object and Object -> Text. So
>> they are useful if you want to build up one or more relationships between
>> the object you are editing in the form and existing objects in the
>> database.
>> If what you are talking about is creating a new set of objects, then your
>> form needs to address that -> your form would create new
>> EstablishmentContainer objects and use property editors to build the
>> relationship between EstablishmentContainer -> Establishment and
>> EstablishmentContainer -> Container. Exactly how to go about this largely
>> depends on how you want to manage the whole lot on the jsp page. If you
>> have
>> something that works, then that is the best way!
>>
>> Spring does have the ability to bind to elements in an ordered list by
>> using
>> [index] type syntax, e.g. establishmentContainer[1]. Doing a Google
>> search
>> on 'Spring MVC bind list' will bring up a whole list of sites that
>> discuss
>> this in more detail.
>>
>> Mike.
>>
>>
>>> I think this is the way to go though, just not sure how to do it.
>>>
>>> Thanks!
>>> Bob
>>>
>>>
>>>
>>>
>>> Mike Horwitz wrote:
>>> >
>>> > I believe you could resolve both cases by registering custom editors
>>> in
>>> > your
>>> > controllers - this would allow Spring MVC to automatically convert
>>> between
>>> > the object and its string representation. See the thread on this
>>> > discussion
>>> > for more details http://tinyurl.com/3yghfd:
>>> >
>>> > You are going to need to provide *Spring* *MVC* with a way to get from
>>> an
>>> > id
>>> > to a country and vice-versa. As you correctly point out this is done
>>> by
>>> > registering a custom property editor in your controller. To do this
>>> you
>>> > need
>>> > to:
>>> >
>>> > 1) Extend the class *PropertyEditorSupport* to create the property
>>> editor:
>>> >
>>> http://java.sun.com/j2se/1.5.0/docs/api/java/beans/*PropertyEditorSupport*.html
>>> > <
>>> http://java.sun.com/j2se/1.5.0/docs/api/java/beans/PropertyEditorSupport.html
>>> >
>>> >
>>> > You need to override the getAsText() and setAsText() methods. The
>>> first
>>> > should call getValue() and return the id as text, and the second
>>> should
>>> > accept a text id and call setValue() on the editor with the
>>> corresponding
>>> > country object.
>>> >
>>> > 2) You then register this custom editor using one of the appropriate
>>> > methods
>>> > on your controller: initBinder() see api on http://tinyurl.com/2sutrm
>>> >
>>> > And it should all work like magic....
>>> >
>>> > Mike.
>>> >
>>> >
>>> > On 10/2/07, syg6 <[EMAIL PROTECTED]> wrote:
>>> >>
>>> >>
>>> >> I have a couple scenarios, one typical the other not so much, that I
>>> am
>>> >> trying to implement as quickly and easily as possible.
>>> >>
>>> >> The first is pretty straight-forward:
>>> >>
>>> >> @Entity
>>> >> public class Application
>>> >> ...
>>> >> private StreetSection streetSecion;
>>> >> ...
>>> >> @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
>>> >> @JoinColumn(name="streetSection", nullable=false)
>>> >> public StreetSection getStreetSection()
>>> >> {
>>> >> return streetSection;
>>> >> }
>>> >>
>>> >> In my form, which is the correct way to do the input?
>>> >>
>>> >> <form:hidden path="streetSection" id="streetSection" />
>>> >>
>>> >> or
>>> >>
>>> >> <form:hidden path="streetSection.id" id="streetSection.id" />
>>> >>
>>> >> In my testing I have found that they both work but each one has its
>>> own
>>> >> little quirks. For example, if I use 'streetSection.id', when it's a
>>> new
>>> >> Application the page crashes because streetSection.id is null. So I
>>> have
>>> >> to
>>> >> put a <c:if> in there and if it's null just use a normal <input
>>> >> type='hidden'> tag.
>>> >>
>>> >> If I use 'streetSection' I believe (I've done so many tests I forget
>>> ...)
>>> >> that Hibernate is giving me 'can't save transient object' errors.
>>> Anyway,
>>> >> just curious what the best practice is.
>>> >>
>>> >> The other scenario is more complicated. I have an object,
>>> Establishment,
>>> >> and
>>> >> another, Container, and they both have a list of
>>> EstablishmentContainer
>>> >> objects, which saved in a join table that has an attribute, quantity.
>>> So
>>> >> each POJO, Establishment and Container, have a OneToMany relationship
>>> >> with
>>> >> EstablishmentContainer, and EstablishmentContainer has a ManyToOne
>>> >> relationship with both Establishment and Container. This is
>>> apparently
>>> >> the
>>> >> best way to map join tables with attributes.
>>> >>
>>> >> @Entity
>>> >> public class Establishment
>>> >> ...
>>> >> private List<EstablishmentContainers> establishmentContainers;
>>> >> ...
>>> >> @OneToMany(mappedBy="establishment", fetch = FetchType.EAGER, cascade
>>> =
>>> {
>>> >> CascadeType.PERSIST, CascadeType.MERGE })
>>> >> public List<EstablishmentContainer> getEstablishmentContainers()
>>> >> {
>>> >> return establishmentContainer;
>>> >> }
>>> >>
>>> >> @Entity
>>> >> public class Container
>>> >> ...
>>> >> @OneToMany(mappedBy="container")
>>> >> public List<EstablishmentContainer> getEstablishmentContainers ()
>>> >> {
>>> >> return establishmentContainers;
>>> >> }
>>> >>
>>> >> @Entity
>>> >> public class EstablishmentContainer extends
>>> >> {
>>> >> private Long id;
>>> >> private Establishment establishment;
>>> >> private Container container;
>>> >>
>>> >> private int quantity;
>>> >> ...
>>> >> @ManyToOne
>>> >> @JoinColumn(name="idEstablishment")
>>> >> public Establishment getEstablishment()
>>> >> {
>>> >> return establishment;
>>> >> }
>>> >>
>>> >> @ManyToOne
>>> >> @JoinColumn(name="idContainer")
>>> >> public Cubo getContainer()
>>> >> {
>>> >> return container;
>>> >> }
>>> >>
>>> >> @Column(name="quantity")
>>> >> public int getQuantity()
>>> >> {
>>> >> return quantity;
>>> >> }
>>> >>
>>> >> In my EstablishmentFormController do I have to do the following for
>>> each
>>> >> EstablishmentContainer created in the page:
>>> >>
>>> >> 1. instantiate an EstablishmentContainer object
>>> >> 2. fill it it request parameters by hand
>>> >> 3. add it to a List
>>> >> 4. assign that list to my Establishment object
>>> >>
>>> >> ... for the EstablishmentContainers list to be properly saved when I
>>> do
>>> >> an
>>> >> establishmentManager.save()?
>>> >> This is what I am doing currently and it works but it seems to me
>>> that
>>> if
>>> >> I
>>> >> put the following in my jsp:
>>> >>
>>> >> input type="text" id="establishmentContainers.container.id"
>>> >> input type="text" id="establishmentContainers.establihsment.id"
>>> >> input type="text" id="establishmentContainers.quantity"
>>> >>
>>> >> Spring should be able to populate the List of EstablishmentObjects
>>> >> automatically, just as in the first example it populates my
>>> >> Application.StreetSection object when I put a
>>> >>
>>> >> input type="text" id="application.streetSection"
>>> >>
>>> >> element in the page. If it's not possible I can keep doing it the way
>>> I
>>> >> currently do, I just don't want to reinvent the wheel.
>>> >>
>>> >> Sorry for the long post. I look forward to any and all responses.
>>> >> Bob
>>> >>
>>> >> --
>>> >> View this message in context:
>>> >>
>>> http://www.nabble.com/Best-way-to-save-ManyToOne-relationships-with-Hibernate-and-Spring-MVC-tf4553758s2369.html#a12995187
>>> >> 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/Best-way-to-save-ManyToOne-relationships-with-Hibernate-and-Spring-MVC-tf4553758s2369.html#a12996194
>>> 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/Best-way-to-save-ManyToOne-relationships-with-Hibernate-and-Spring-MVC-tf4553758s2369.html#a13001634
Sent from the AppFuse - User mailing list archive at Nabble.com.
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]