7 Replies Latest reply on Aug 3, 2007 12:23 PM by pmuir

    seam-ui example: s:selectItems example is broken

    stephen.friedrich

      On a fresh Seam from CVS and pristine JBossAS 4.2.1:
      - Deploy the seam-ui example
      - Select the s:selectItems topic
      - Select the "Edit the continent/country relationship"
      - Add some countries to Antarctica
      - Click on Apply
      - Select the s:fragment topic
      - Select the s:selectItems topic again (you'll get a new conversation)
      => Bug: Your previous edits are lost

      Gosh - this cost me the best part of the night.
      I copied the "h:selectManyListbox" part from continents.xhtml but my changes to the DB were always lost.
      I did not notice that the changes weren't persisted in the example either, because I always stayed in the same conversation (and of course I expected the example to be correct).

      What is the best way to solve this, specifically:
      How to update country.continent when the selectManyListbox is used?

      Here's what worked for me in my own code, but maybe there's a better way?

       public void setDepartments(List<Department> departments) {
       for (Department department : departments) {
       DepartmentGroup oldGroup = department.getGroup();
       if (oldGroup != null)
       oldGroup.getDepartments().remove(department);
       department.setGroup(this);
       }
       this.departments = departments;
       }
      


        • 1. Re: seam-ui example: s:selectItems example is broken
          pmuir

          Oops - that example was never going to work, a country can't be in *more* than one continent. Thanks for reporting it. I've altered the example in CVS to be what it should be but I suspect it doesn't help you as now it doesn't use a selectManyXXX. You're problem is most likely related to your hibernate mappings.

          • 2. Re: seam-ui example: s:selectItems example is broken
            stephen.friedrich

            Hm, that really doesn't help much.
            How about a simple example that uses Seam's s:selectItems bound to a @OneToMany association in a selectMany-Component? :-)

            I did a grep through all the examples' code to find this one usage in the ui example. All the other usages of s:selectItems were in selectOne-Components which is much simpler anyway.

            I can't see how hibernate mappings can help here. If I understood correctly there's no such beast as an EJB2 entity bean with container-managed bidirectional relationships.
            If the UI manipulates only the list no possible mapping can update the single entities, right?

            My solution that updates each child entity when the list setter on the parent entity is called seems to work fine, but it feels realy crude.
            Also I'd like to avoid loading the "oldGroup" (see code below) if it isn't yet loaded anyway. It's pointless and causes unnecessary DB access because the change in the parent entity does not affect the DB at all. Still I'd like to update already loaded "oldGroup"s so that their state is still valid in case they are processed further in the same conversation.

            Any idea?

            • 3. Re: seam-ui example: s:selectItems example is broken
              pmuir

              No, but there is more complicated ManyToMany mapping between colour and person. Show your current mappings and views, and walk through the steps of what you want to happen, I'm not quite understanding what you are wanting. (and yes, you shouldn't need to use the piece of code above)

              • 4. Re: seam-ui example: s:selectItems example is broken
                stephen.friedrich

                Thanks a lot for looking into this, I really, really do appreciate this.

                I prepared an example to clarify what I want to achieve.
                There are departments which have employees.
                In the edit page for a department I'd like to have a multi-select for the employees that work in the department.
                (A selectManyShuffle form trinidad ultimately, for now I am testing with a standard selectManyListbox.)

                I started with the seampay example and made the following additions.
                Then directly opened http://127.0.0.1:8080/seam-pay/departments.seam

                Currently edits never seem to make it to the DB.
                My "hack" that manually sets the parent's id at the child entities when the list setter is called is no good either, because it does not remove deselected entities from the selection.
                I played with different cascade options, but that also didn't make any difference.
                Any idea what I need to change to make this work?

                Added to import.sql

                insert into Department(id, name) values(1, 'Accounting');
                insert into Department(id, name) values(2, 'Marketing');
                insert into Department(id, name) values(3, 'R&D');
                
                insert into Employee(id, department_id, name) values(1, 1, 'Bean Counter');
                insert into Employee(id, department_id, name) values(2, 1, 'Mister Money');
                insert into Employee(id, department_id, name) values(3, 1, 'Calc U. Lator');
                
                insert into Employee(id, department_id, name) values(4, 2, 'Power Pointy');
                
                insert into Employee(id, department_id, name) values(5, 3, 'Heinz Hacker');
                insert into Employee(id, department_id, name) values(6, 3, 'Sam Seam');
                


                Added to components.xml
                <framework:entity-query name="departments"
                 ejbql="from Department"
                 order="name"
                 max-results="20"
                 entity-manager="#{entityManager}" />
                
                 <framework:entity-query name="employees"
                 ejbql="from Employee"
                 order="name"
                 max-results="20"
                 entity-manager="#{entityManager}" />
                
                 <framework:entity-home name="departmentHome"
                 entity-class="org.jboss.seam.example.seampay.Department"
                 entity-manager="#{entityManager}" />
                
                 <factory name="selectedDepartment" value="#{departmentHome.instance}"/>
                


                New: departments.xhtml
                <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                <html xmlns="http://www.w3.org/1999/xhtml"
                 xmlns:ui="http://java.sun.com/jsf/facelets"
                 xmlns:h="http://java.sun.com/jsf/html"
                 xmlns:f="http://java.sun.com/jsf/core"
                 xmlns:s="http://jboss.com/products/seam/taglib"
                 template="template.xhtml">
                <head>
                 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
                 <title>Seam Pay</title>
                 <link href="screen.css" rel="stylesheet" type="text/css" />
                 <link href="date.css" rel="stylesheet" type="text/css" />
                </head>
                <body>
                
                 <div class="body">
                 <h1>Departments</h1>
                
                 <h:messages styleClass="message"/>
                
                
                 <!-- search results -->
                 <table class="results">
                 <tr>
                 <th>Id</th>
                 <th>Name</th>
                 </tr>
                 <ui:repeat value="#{departments.resultList}" var="department">
                 <tr>
                 <td>
                 <s:link view="/departments.xhtml" value="#{department.id}">
                 <f:param name="departmentId" value="#{department.id}"/>
                 </s:link>
                 </td>
                 <td>#{department.name}</td>
                 </tr>
                 </ui:repeat>
                 </table>
                 <h:outputText value="No departments found" rendered="#{empty departments.resultList}" styleClass="message"/>
                
                
                 <f:subview id="departmentDetails" rendered="#{departmentHome.idDefined}">
                 <h2>Department Details</h2>
                
                 <h:form>
                 <s:validateAll>
                 <table>
                 <tr>
                 <td>Name:</td>
                 <td>
                 <h:inputText id="name" value="#{selectedDepartment.name}" required="true"/>
                 </td>
                 </tr>
                 </table>
                
                 <h:selectManyListbox value="#{selectedDepartment.employees}" size="8">
                 <s:selectItems value="#{employees.resultList}" var="employee" label="#{employee.name}" />
                 <s:convertEntity />
                 </h:selectManyListbox>
                 </s:validateAll>
                
                 <h:commandButton value="Update" action="#{departmentHome.update}" />
                 </h:form>
                 </f:subview>
                
                 </div>
                
                </body>
                </html>
                


                New: Department.java
                package org.jboss.seam.example.seampay;
                
                import javax.persistence.*;
                import java.io.Serializable;
                import java.util.List;
                
                @Entity
                public class Department implements Serializable {
                 @Id @GeneratedValue
                 private Long id;
                
                 String name;
                
                 @OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
                 private List<Employee> employees;
                
                 public Long getId() {
                 return id;
                 }
                 public void setId(Long id) {
                 this.id = id;
                 }
                
                 public String getName() {
                 return name;
                 }
                
                 public void setName(String name) {
                 this.name = name;
                 }
                
                 public List<Employee> getEmployees() {
                 return employees;
                 }
                
                 public void setEmployees(List<Employee> employees) {
                 this.employees = employees;
                 }
                }


                New: Employee.java
                package org.jboss.seam.example.seampay;
                
                import javax.persistence.Entity;
                import javax.persistence.GeneratedValue;
                import javax.persistence.Id;
                import javax.persistence.ManyToOne;
                import java.io.Serializable;
                
                @Entity
                public class Employee implements Serializable {
                 @Id @GeneratedValue
                 private Long id;
                
                 String name;
                
                 @ManyToOne
                 private Department department;
                
                 public Long getId() {
                 return id;
                 }
                 public void setId(Long id) {
                 this.id = id;
                 }
                
                 public String getName() {
                 return name;
                 }
                
                 public void setName(String name) {
                 this.name = name;
                 }
                
                 public Department getDepartment() {
                 return department;
                 }
                
                 public void setDepartment(Department department) {
                 this.department = department;
                 }
                }


                • 5. Re: seam-ui example: s:selectItems example is broken
                  pmuir

                  Read http://www.hibernate.org/hib_docs/annotations/reference/en/html/entity.html - section 2.2.5.3.2.1 - you need the OneToMany side to be the owner, not the otherway round I think. I think if you debug, you should find that the java objects have all the correct information, its just not being updated in the db.

                  • 6. Re: seam-ui example: s:selectItems example is broken
                    stephen.friedrich

                    So what's the gist of this? The UI I have in mind (or more precisely that is required by our client) simply cannot be implemented with all this sophisticated infrastructure (JPA, JSF, Seam) ?
                    SelectMany... can't be used based on a OneToMany association?

                    When I debug I find that when JSF/Seam calls setEmployees(employees) then each employee's department field is not updated.
                    When I do that manually with some ugly code (updating both removed and added employees) my example works fine.
                    Sigh - so I guess that's my only choice.

                    • 7. Re: seam-ui example: s:selectItems example is broken
                      pmuir

                      You need to call refresh on the entity after persisting it I think.