-
1. Re: turning seam generated home object to stateful home
nickarls Mar 4, 2008 8:23 AM (in response to seammm)Show some code. Do you have a @Name annotation? Do you get any exceptions on deploy?
-
2. Re: turning seam generated home object to stateful home
seammm Mar 4, 2008 11:55 AM (in response to seammm)Nicklas Hi - I have the @Name annotation but I was missing the @Local annotation and bunch of other declarations in the interface and overrides in the bean. At 3am, one needs a fresh pair of eyeballs I guess.
public boolean isManaged(); public Organization getInstance(); public String remove();
That took care of the
xxx not bound
error. But I have a different error now, which isEntityManager is closed
.There are organizationList.xhtml and organization.xhtml as generated by Seam and hand modified back bean OrganizationHome and OrganizationHomeLocal.
I browse organizations using organizationList.xhtml, edit an organization using organization.xhtml, and get out of edit mode using the untouched Done button.
Upon second time I try to enter edit mode by clicking on any of the organizations results in
EntityManager is closed
.Actual code is below..
Second thing that HALF works is that, if you look at the organization.xhtml, I am trying to implement a ui pattern to manage many-to-many relations with the extra complexity of categorization.
So the user edits an Organization AND the Products it is allowed to see. Since the Products can be a lot, I categorize the products. User sees the Organization fields AND list of categories parent of which is hardcoded if not specified in the request parameter, AND the Products the category of which is the current category. User selects/unselects Product using the checkboxes and backing productSelection map.
The problem is, Product selection hashmap is not passed the correct values from the checkboxes in the ui, if user ventured into sub categories. However it works if the user don't go into sub categories but select/unselect whatever products are under default category. I don't know how clear that was but it is in the code.
I think once it works, it will be a nice ui pattern for managing large many-to-many relations.
And yes, I tried RF ListShuttle but I couldn't add categorization to its left side so I gave up.
Thanks.
@Stateful @Scope(ScopeType.SESSION) @Name("organizationHome") public class OrganizationHome extends EntityHome<Organization> implements OrganizationHomeLocal { static final Long DEFAULT_CATEGORY_ID = new Long(1); @RequestParameter Long organizationId; @RequestParameter Long categoryId; private Map<Product, Boolean> productSelection = new HashMap<Product, Boolean>(); @Logger Log log; @Override public Object getId() { if (organizationId==null) { return super.getId(); } else { return organizationId; } } @Override @Begin public void create() { super.create(); productSelection = new HashMap<Product, Boolean>(); } private void persistProducts() { getInstance().getProductList().clear(); Set<Product> keys = productSelection.keySet(); for (Product p: keys) { log.info("key {0} value {1}", p.getName(), productSelection.get(p)); if (productSelection.get(p)) { getInstance().getProductList().add(p); log.info("selected {0}", p.getName()); } } getEntityManager().merge(getInstance()); } @Override public String persist() { attachInstanceToEntityManager(); String s = super.persist(); persistProducts(); return s; } @Override public Organization getInstance() { return super.getInstance(); } @Override @Transactional public String update() { attachInstanceToEntityManager(); persistProducts(); return super.update(); } @Factory(value="productList", scope=ScopeType.EVENT) public List<Product> findProducts() { if (categoryId == null) { categoryId = DEFAULT_CATEGORY_ID; } return getEntityManager().createQuery("select pc.product from ProductCategory pc where pc.category.id = :categoryId order by pc.product.name") .setParameter("categoryId", categoryId) .getResultList(); } // @Factory(value="categoryList", scope=ScopeType.EVENT) // public List<Category> findCategories() // { // if (categoryId == null) // { // categoryId = DEFAULT_CATEGORY_ID; // } // // return getEntityManager().createQuery("from Category where parent.id = :parentId order by name") // .setParameter("parentId", categoryId) // .getResultList(); // } @Remove @Destroy public void destroy() {} public Map<Product, Boolean> getProductSelection() { return productSelection; } public void setProductSelection(Map<Product, Boolean> productSelection) { this.productSelection = productSelection; } @Override @Transactional public boolean isManaged() { attachInstanceToEntityManager(); return super.isManaged(); } @Override protected Organization loadInstance() { Organization o = super.loadInstance(); for (Product p: o.getProductList()) { productSelection.put(p, true); } return o; } @Override @Transactional public String remove() { attachInstanceToEntityManager(); return super.remove(); } /** * if super.isManaged() returns false, but getInstance() has an Id, then * instance needs to be re-attached to the entityManager in order to * prevent "detached entity passed to persist" exceptions. */ private void attachInstanceToEntityManager() { log.info("** attach called"); if (!super.isManaged() && getInstance().getId() != null) { log.info("** attach " + getInstance()); setInstance(getEntityManager().merge(getInstance())); } } /** * Need to null out entityManagers prior to passivation so that they'll get * reinitialized upon subsequent reqests. Failing to do this in a clustered * environment will result in "EntityManager is closed" error messages. */ @PrePassivate public void prePassivate() { setEntityManager(null); } }
@Name("categoryList") public class CategoryList extends EntityQuery { static final Long DEFAULT_CATEGORY_ID = new Long(1); @RequestParameter Long categoryId; @Logger Log log; @Override public String getEjbql() { if (categoryId == null) { categoryId = DEFAULT_CATEGORY_ID; } log.info("** from Category where parent.id = " + categoryId + " order by name"); return ("from Category where parent.id = " + categoryId + " order by name"); } }
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.org/rich" template="layout/template.xhtml"> <ui:define name="body"> <h:messages globalOnly="true" styleClass="message"/> <h:form id="organizationForm"> <rich:panel> <f:facet name="header">organization</f:facet> <s:decorate id="nameDecoration" template="layout/edit.xhtml"> <ui:define name="label">Name</ui:define> <h:inputText id="name" required="true" value="#{organizationHome.instance.name}"/> </s:decorate> <div style="clear:both"/> </rich:panel> <rich:panel> <f:facet name="header">productList</f:facet> <div class="results"> <h:dataTable id="categoryList" var="category" value="#{categoryList.resultList}" rendered="#{not empty categoryList.resultList}"> <h:column> <s:link id="category" value="#{category.name}" view="/organization.xhtml"> <f:param name="categoryId" value="#{category.id}"/> </s:link> </h:column> </h:dataTable> <h:outputText value="No product exists" rendered="#{empty productList}"/> <h:dataTable id="productList" var="product" value="#{productList}" rendered="#{not empty productList}"> <h:column> <h:selectBooleanCheckbox value="#{organizationHome.productSelection[product]}"/> </h:column> <h:column> <f:facet name="header">Name</f:facet> <s:link id="product" value="#{product.name}" view="/product.xhtml"> <f:param name="productId" value="#{product.id}"/> </s:link> </h:column> </h:dataTable> </div> </rich:panel> <div class="actionButtons"> <h:commandButton id="save" value="Save" action="#{organizationHome.persist}" rendered="#{!organizationHome.managed}"/> <h:commandButton id="update" value="Save" action="#{organizationHome.update}" rendered="#{organizationHome.managed}"/> <h:commandButton id="delete" value="Delete" action="#{organizationHome.remove}" rendered="#{organizationHome.managed}"/> <s:button propagation="end" id="done" value="Done" view="/organizationList.xhtml"/> </div> </h:form> </ui:define> </ui:composition>
@Entity public class Category implements Serializable { private Long id; private String name; private Category parent; private List<Category> children; ...
@Entity public class Organization implements Serializable { private Long id; private String name; private List<Product> productList; ...
@Entity public class Product implements Serializable { private Long id; private String name; ...
@Entity public class OrganizationProduct implements Serializable { private Long id; private Organization organization; private Product product; ...
@Entity public class ProductCategory implements Serializable { private Long id; private Product product; private Category category; ...