折腾了两天,终于在JBoss Seam中搞定了JSF Converter。
在这个程序中,产品(Product)和分类(Category)是多对多的关系,关系维护方为产品,在创建产品时,允许选择多个分类,因为Product.categories是一个List属性,同时产品选择<select />标签也是由从Action中查询出来的所有Category的List,所以在页面上,需要在页面渲染、用户提交后的category进行转换。
显示时,我们以Category.id为<option />的value值,而Category.title为label。
Entity如下(省略setter和getter):
Category.java
@Entity @Table(name = "category") public class Category extends BaseEntity { @Id @GeneratedValue @Column(name = "id", unique = true, nullable = false, length = 10) private Integer id; @Column(name = "title") @NotNull @Length(min = 4, max = 64, message = "{invalid.length}") private String title; @ManyToMany(mappedBy = "categories") private List<product> products = new ArrayList</product><product>(); } </product> |
Product.java
@Entity @Table(name = "product") public class Product extends BaseEntity { @Id @GeneratedValue @Column(name = "id", nullable = false, unique = true, length = 10) private Integer id; @Column(name = "title") @NotNull @Length(min = 4, max = 64) private String title; @Column(name = "price") @NotNull private Float price; @ManyToMany(cascade = { CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY) @JoinTable(name = "category_product", joinColumns = { @JoinColumn(name = "product_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "category_id", referencedColumnName = "id") }) private List<category> categories = new ArrayList</category><category>(); } </category> |
如果persistence.xml中,hibernate.hbm2ddl.auto的属性配置为create-drop,那么在JBoss启动时,这两个Entity会生成三张表,除了product和category外,还有一张关联表:category_product。
xhtml页面代码如下:
<ui :define name="label">Category</ui> <h :selectManyListbox id="categories" value="#{productEditAction.product.categories}" converter="categoryConverter" > <s :selectItems var="cat" value="#{productEditAction.categories}" label="#{cat.title}"></s> </h> |
其中,#{productEditAction.categories}表示系统中所有的分类,是一个List<category>列表。
JSF Converter代码:
import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.ConverterException; import net.ymeng.studyseam.entity.Category; import net.ymeng.studyseam.service.InventoryService; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.faces.Converter; /** * @author yangtze * */ @Name("categoryConverter") @Converter(id = "categoryConverter") public class CategoryConverter implements javax.faces.convert.Converter { @In(create = true, value = "inventoryServiceImpl") private InventoryService inventoryService; /* * (non-Javadoc) * * @see * javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext * , javax.faces.component.UIComponent, java.lang.String) */ public Object getAsObject(FacesContext context, UIComponent component, String value) { if (value != null && !"".equals(value)) { Integer catId = Integer.valueOf(value); Category cat = inventoryService.findCategoryById(catId); return cat; } return null; } /* * (non-Javadoc) * * @see * javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext * , javax.faces.component.UIComponent, java.lang.Object) */ public String getAsString(FacesContext context, UIComponent component, Object value) { if (value instanceof Category) { return String.valueOf(((Category) value).getId()); } else { throw new ConverterException("Unsupported valued! Value is: " + value + "\tType is: " + value.getClass()); } } } |
其中getAsString方法在页面显示时会自动调用,将传入的对象value转换为字符串输出,因为这里用作option值的是Category.id,所以我们在此方面中取出分类的id并返回。
而getAsObject方法是在用户提交时,将option的值转换为Integer,并从Service层中查找对应的Category实例、返回。
所以对于提交后的保存动作,我们就不需要再做任何处理,Product.categories中已经是转换好的List<Category>,直接persist/merge即可。
如果在转换类中使用@Converter注解,就不再需要在faces-config.xml中进行converter的配置了,Seam会自动帮你完成这个配置。
不错的示例,关于converter找了半小时的资料了,你这个例子最详细了
🙂
终于找到了,虽然知道hibernate中那种多对多的写法,在seam中想写却不知道怎么写,现在看见了,呵呵,谢谢了