Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate Spring - @OneToMany - Foreign key is not getting stored in database

Requirement - For Recipe application, user can input Recipe Name and Ingredients. User can input more than one ingredient of a recipe on the JSP page.

Database Model: Created 2 tables. (Recipe can have more than one Ingredient)

Recipe - 
   recipe_id (primary key)
   name

Ingredient
  ingredient_id (primary key)
  recipe_id (foreign key to recipe(recipe_id))
  quantity
  ingredient

Issue - For this case, @OneToMany (as shown below) is used for Hibernate mapping but when user save the form, foreign key (i.e. recipe_id) is not getting stored in the Ingredient table. Say user inputs following:

Recipe -  Sandwich
Ingredient - 250g(as quantity) Salt (as ingredient)

After saving following is getting saved (i.e. No value in the recipe table)

**Recipe**
recipe_id - 1
name - Sandwich

**Ingredient**
ingredient_id - 50
recipe_id - NULL
quantity - 250g
ingredient - Salt

There is no error while saving form.

Following is saved in log by Hibernate when user save the data:

Hibernate: select seq_recipe.nextval from dual
Hibernate: select seq_ingredient.nextval from dual
Hibernate: insert into RECIPE (NAME, RECIPE_ID) values (?, ?)
Hibernate: insert into ingredient (COMMENTS, INGREDIENT, RECIPE_ID, INGREDIENT_ID) values (?, ?, ?, ?)

Oracle table

create table ingredient(ingredient_id number(4) primary key,
                    quantity varchar2(20),
                    ingredient varchar2(40),
                    recipe_id number(4),
                    constraint recipe_fk foreign key (recipe_id)
                    references recipe(recipe_id));

create table recipe(id number primary key,
                    name varchar2(25) unique,
                    ingredient_id number(4),
                    constraint ingredient_fk foreign key(ingredient_id)
                    references ingredient(ingredient_id));

Ingredient and Recipe POJO

@Entity
@Table(name = "ingredient", uniqueConstraints={
        @UniqueConstraint(columnNames="INGREDIENT_ID")
})
public class Ingredient implements Serializable {
    private static final long serialVersionUID = -2489479702169761646L;

    @Id
    @Column(name = "INGREDIENT_ID", unique=true, nullable=false)
    @SequenceGenerator(name="seq_ingredient", sequenceName="seq_ingredient")
    @GeneratedValue(strategy=GenerationType.AUTO, generator="seq_ingredient")
    private Integer ingredientId;

    @Column(name = "QUANTITY")
    private String quantity;

    @Column(name = "INGREDIENT")
    private String ingredient;

    @ManyToOne(fetch=FetchType.EAGER)
    @JoinColumn(name="RECIPE_ID", nullable=false)
    private Recipe recipe;

    //getter and setters



@Entity
@Table(name = "recipe")
public class Recipe implements Serializable {
    private static final long serialVersionUID = 4398676897855994030L;

    @Id
    @Column(name = "id")
    private Integer id;

    @Column(name = "name")
    private String name;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="recipe")
    private List<Ingredient> ingredients;

//getters and setter
}

MVC that will map the /add url and also handle POST method, when user save the data

@RequestMapping("/add")
public String newRecipe(Model model) {
    Recipe recipe = new Recipe();
    recipe.setIngredients(new AutoPopulatingList<Ingredient>(Ingredient.class));

    model.addAttribute("recipe", recipe);
    return "recipes/new";
}

@RequestMapping(value = "add", method = RequestMethod.POST)
public String addRecipe(@Valid Recipe recipe, BindingResult result,
        Model model) {
    if (result.hasErrors()) {
        return "recipes/new";
    }
    recipeServices.addRecipe(recipe);
    return "redirect:/recipe/" + recipe.getId();
}

**DAO to save Recipe objec **

public class RecipeDaoImpl implements RecipeDao {
    public void addRecipe(Recipe recipe) {
        getSession().save(recipe);
    }
}

JSP code

<html>
<script type="text/javascript">
<!--
    function addRow(tableID) {

        <!-- Code delete to improve readability. Dynamically add row -->
    }
-->
</script>
</head>
<body>
    <s:url value="/recipe/add" var="recipe_new" />

    <sf:form modelAttribute="recipe" action="${recipe_new}" method="POST">
        <table>
            <tr>
                <th>Recipe Name:</th>
                <td><sf:input path="name" id="recipeName" /> <br />
                        <sf:errors path="name" cssClass="error" /></td>
            </tr>
            <tr>
                <td>
                    <h3>Ingredients</h3> <INPUT type="button" value="+"
                    onclick="addRow('dataTable')" /> <INPUT type="button" value="-"
                    onclick="deleteRow('dataTable')" />
                    <table id="dataTable" width="350px" border="1">
                        <tr>
                            <th></th>
                            <th>Quantity</th>
                            <th>Ingredient</th>
                        </tr>
                        <tr>
                            <td><input type="checkbox" name="chk" /></td>
                            <td><sf:input type="text" path="ingredients[0].quantity"
                                    title="Quantity" placeholder="Quantity" /></td>
                            <td><sf:input type="text" path="ingredients[0].ingredient"
                                    title="Ingredient" placeholder="Ingredient" /></td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </sf:form>
</body>
</html>

Humble request to help me as I have been trying to fix this issue from past 2 days and not able to understand why foreign key not getting stored in database.

like image 731
user92161 Avatar asked Sep 06 '25 13:09

user92161


1 Answers

mappedBy is generally for bidirection relationship, to save extra queries for updation/insertion.

Basically for OneToMany on Parent side and ManyToOne on Child Side.

In your case, mappedBy means that Parent does not hold responsibility to save references to Child.

So try using @JoinColumn in your case.

like image 72
Ankur Singhal Avatar answered Sep 09 '25 05:09

Ankur Singhal