JSF2和JPA双向OneToMany

时间:2014-06-09 02:16:54

标签: java java-ee jpa jsf-2

我是JSF2的新手。使用JPA,我在菜肴和配料之间有双向的OneToMany关系。

该应用程序有一个视图,允许添加至少一种成分的菜肴,用户可以通过Javascript添加其他成分,这基本上只是复制代表成分的HTML元素。

截图让我更清楚我的意思 - 最初只有一种成分,但用户可以通过点击相应的按钮一次添加/删除一种成分:

enter image description here

我刚刚在辅助bean中的@PostConstruct中创建的初始成分:

@Named
@Produces
private Dish addDish;

@PostConstruct
public void init() {
    this.addDish = new Dish();
    final Ingredient ingredient = new Ingredient();
    ingredient.setDish(this.addDish);
    this.addDish.getIngredients().add(ingredient);
}

public String add() {
    databaseService.persist(addDish);
    return OUTCOME;
}

提交含有初始成分的菜肴工作正常,它会被正确地保存到数据库中。

但现在我想知道我怎么知道用户提交的表格中实际有多少成分,然后我会在哪里创建适当数量的Ingredient实例并将它们添加到Dish实例?

2 个答案:

答案 0 :(得分:1)

为了提供您想要的快速示例,我将为您提供一些基本代码。假设您想要创建菜肴本身和所有相关成分,您应该在JPA中声明一对多关系并将级联模式设置为all。这样,当您保存菜肴时,您将能够保存每种相关成分。

话虽如此,让我们使用JSF相关代码,它允许您添加 - 删除一个成分并保存菜:

<强> newDish.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core">
<h:head />
<h:body>
    <h:messages />
    <h:form id="form" prependId="true">
        <h:panelGrid columns="1">
            Preparation:
            <h:inputTextarea value="#{newDishBean.newDish.preparation}"
                required="true" />
        </h:panelGrid>
        <h:dataTable value="#{newDishBean.newDish.ingredients}"
            var="ingredient" id="ingredients">
            <h:column>
                <h:inputText value="#{ingredient.amount}" required="true" />
            </h:column>
            <h:column>
                <h:inputText value="#{ingredient.unit}" required="true" />
            </h:column>
            <h:column>
                <h:inputText value="#{ingredient.name}" required="true" />
            </h:column>
        </h:dataTable>
        <h:commandButton value="Add Ingredient">
            <!-- Sends the whole form (execute="@form") into the POST request in order to keep the model updated -->
            <f:ajax listener="#{newDishBean.addIngredient}" render="ingredients"
                execute="@form" />
        </h:commandButton>
        <h:commandButton value="Remove Ingredient">
            <!-- Here we don't need to send extra info. Just the button component (@this) is executed -->
            <f:ajax listener="#{newDishBean.removeIngredient}"
                render="ingredients" />
        </h:commandButton>
        <!-- Non-ajax request. The whole form is sent to the server -->
        <h:commandButton value="Create dish"
            action="#{newDishBean.createDish}" />
    </h:form>
</h:body>
</html>

<强> NewDishBean.java:

@ManagedBean
@ViewScoped
public class NewDishBean {

    public class Dish {

        private String preparation;

        private List<Ingredient> ingredients = new ArrayList<Ingredient>();

        public List<Ingredient> getIngredients() {
            return ingredients;
        }

        public String getPreparation() {
            return preparation;
        }

        public void setIngredients(List<Ingredient> ingredients) {
            this.ingredients = ingredients;
        }

        public void setPreparation(String preparation) {
            this.preparation = preparation;
        }

        @Override
        public String toString() {
            return "Dish [preparation=" + preparation + ", ingredients="
                    + ingredients + "]";
        }

    }

    public class Ingredient {

        private Dish dish;

        private int amount;

        private String unit;

        private String name;

        public int getAmount() {
            return amount;
        }

        public Dish getDish() {
            return dish;
        }

        public String getName() {
            return name;
        }

        public String getUnit() {
            return unit;
        }

        public void setAmount(int amount) {
            this.amount = amount;
        }

        public void setDish(Dish dish) {
            this.dish = dish;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void setUnit(String unit) {
            this.unit = unit;
        }

        @Override
        public String toString() {
            return "Ingredient [amount=" + amount + ", unit=" + unit
                    + ", name=" + name + "]";
        }
    }

    private Dish newDish = new Dish();

    public void addIngredient() {
        Ingredient i = new Ingredient();
        i.setDish(newDish);
        newDish.ingredients.add(i);
    }

    public void createDish() {
        // Do your JPA stuff
        System.out.println(newDish + " created!");
    }

    public Dish getNewDish() {
        return newDish;
    }

    public void removeIngredient() {
        if (!newDish.ingredients.isEmpty()) {
            newDish.ingredients.remove(newDish.ingredients.size() - 1);
        }
    }

}

基本上你所做的就是在创建bean时创建一个新菜。每次点击“添加成分”时,都会在新菜中添加一种成分并显示在表中。

这是实现您想要的JSF标准方法。作为一个有状态框架,JSF倾向于为模型中的每个更改更新它的状态(可以保留在服务器端或客户端)。当然,你可以使用Javascript为表添加一行新成分,但这需要自己更新这个状态。

另见:

答案 1 :(得分:0)

我最终在我的问题中抛弃了这个方法,然后针对类似的问题找到了accepted answer建议的解决方案。

更新JSF生成的标记感觉有点蠢,使用AJAX的解决方案非常简单,似乎更像JSF方式。

唯一遗憾的是,对服务器的请求是针对可以完全在客户端完成的事情。

注意:如果您喜欢我,不希望AJAX呈现整个表单,只需要<div>,即动态添加元素,将该部分包装在<h:panelGroup id="render-by-ajax" layout="block">中。