0

My question is about the code below:

Ingredient.java

@Entity
@Table(name = "recipes_ingredients")
public class Ingredient implements Serializable {
 
    @Id
    @Column(name = "ingredient_id")
    private Long id;
 
    @NotEmpty
    private String name;
 
    private Integer quantity;
 
    // Getters and Setters
}

Recipe.java

@Entity
@Table(name = "recipes")
public class Recipe implements Serializable {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "recipe_id")
    private Long id;
 
    @NotEmpty
    private String name;
 
    @ManyToOne(fetch = FetchType.LAZY)
    private Client client;
 
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "fk_recipe_id")
    private List<Ingredient> ingredients;
 
    // Getters and Setters
}

What I want to do is that the PK (recipes_ingredients table) is composed of: ingredient_id and recipe_id in some way (using the FK that I already have and making it also PK or just taking that ID in some other way and making it PK with ingredient_id).

The ingredient_id will be numbers that will start at 1 onwards for each recipe, in this way if I have a recipe with recipe_id = 1 and 3 ingredients, my idea is that in the ingredients table it has something like this:

ingredient_id(PK)    name      quantity   recipe_id(PK)
1                    Oil       150        1
2                    Salt      5          1
3                    Flour     500        1

Any idea how I can do this?

Despotars
  • 541
  • 1
  • 8
  • 23
  • Why? It is an auto-generated id, so there's no need for a composite primary key? – RobOhRob Mar 26 '21 at 14:48
  • I have removed that option from the ingredients entity (id), because I don't want it to be a auto generated number, I want to assign it myself and always start at 1 (to number the ingredients), for that reason, I need to crete a composite PK – Despotars Mar 26 '21 at 14:53

1 Answers1

0
    public class IngredientId {

        private Recipe recipe;

        private Long id;

        public IngredientId(Recipe recipe, Long id) {
            this.recipe = recipe;
            this.id = id;
        }

        public IngredientId() {
        }

        //Getters and setters are omitted for brevity

        public Recipe getRecipe() {
            return recipe;
        }

        public void setRecipe(Recipe recipe) {
            this.recipe = recipe;
        }

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        @Override
        public boolean equals(Object o) {
            if ( this == o ) {
                return true;
            }
            if ( o == null || getClass() != o.getClass() ) {
                return false;
            }
            IngredientId ingredientId = (IngredientId) o;
            return Objects.equals( recipe, ingredientId.recipe ) && Objects.equals( id, ingredientId.id );
        }

        @Override
        public int hashCode() {
            return Objects.hash( recipe, id );
        }
    }

    @Entity
    @IdClass( IngredientId.class )
    @Table(name = "recipes_ingredients")
    public class Ingredient {

        @Id
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "recipe_id")
        private Recipe recipe;

        @Id
        @GeneratedValue
        @Column(name = "ingredient_id")
        private Long id;

        private String name;

        private Integer quantity;

        public IngredientId getId() {
            return new IngredientId( recipe, id );
        }

        public void setId(IngredientId id) {
            this.recipe = id.getRecipe();
            this.id = id.getId();
        }
    }

    @Entity
    @Table(name = "recipes")
    public class Recipe {

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "recipe_id")
        private Long id;

        private String name;

        @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
        @JoinColumn(name = "recipe_id")
        private List<Ingredient> ingredients = new ArrayList<>();

    }

It's important to notice that a composite key cannot have null values. So this makes sense only if you know that each ingredient is bound to a recipe. See this answer on StackOverflow and this Hibernate Jira

You can have a look at the Hibernate ORM documentation for more details.

Davide D'Alto
  • 7,421
  • 2
  • 16
  • 30
  • In the class IngredientId, need to be Static? because I have an error and if I delete the Static the error disappear – Despotars Mar 26 '21 at 21:55
  • When I try to save a recipe, I have this error: java.sql.SQLIntegrityConstraintViolationException: Column 'recipe_id' cannot be null – Despotars Mar 26 '21 at 22:00
  • No, they don't need to be static. When I tested it I've created them in the same class. I will edit the code. – Davide D'Alto Mar 27 '21 at 09:44
  • A composite key cannot have null values in hibernate ORM. See https://stackoverflow.com/questions/70909/hibernate-mapping-a-composite-key-with-null-values and https://hibernate.atlassian.net/browse/HHH-1109 – Davide D'Alto Mar 27 '21 at 09:55
  • If you can have ingredients not associated to a recipe, it's better if you don't use them in the composite key. Note that if you want the association to be an ordered list, you can use `@OrderColumn` – Davide D'Alto Mar 27 '21 at 09:59
  • I have the same problem you say, it cannot be null. What do you recommend me? The ingredients is just an example, I also have a variable that is "Steps", and I have the same problem, I want to assign an ID manually and that the PK will be the step_id (or Ingredient_id) and the recipe_id – Despotars Mar 28 '21 at 02:07
  • The mapping you want to do is not possible, unless you know that all the values are not null. The problem is not that the id is assigned manually, the problem is that there must be an id if you want to use a composite key. That said, why do you want to assign it manually? Do you want to keep the ingredients in a certain order? – Davide D'Alto Mar 28 '21 at 09:29