Can I use only Entity's id instead of fetching an entity from DB when there is a relationship in an Entity?

Viewed 712

I am using Spring Data JPA with Hibernate.

Lets say I have the following entity defined:

@Entity
@Table(name = "foods")
public class Food {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "food_id")
    private Long foodId;


    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "food_type_id")
    @NotNull
    private FoodType foodType;
    
    ...
}


@Entity
@Table(name = "food_types")
public class FoodType {
    
    public static final Integer PERISHABLE;
    public static final Integer NON_PERISHABLE;

    @Id
    @Column(name = "food_type")
    private Integer foodTypeId;

    private String name;
    
    ...
}

Every time when I want to create a Food entity and save it to the database, currently code looks like this:

Food food = new Food();
FoodType foodType = foodTypeRepository.findById(FoodType.PERISHABLE); // Call to DB to get Entity
food.setFoodType(foodType);
....

foodRepository.save(food);

If we consider FoodType to be constant in the DB. Can I use it like this:

Food food = new Food();
FoodType foodType = new FoodType();
foodType.setFoodTypeId(FoodType.PERISHABLE); // No Call to DB
food.setFoodType(foodType);
....

foodRepository.save(food);

I have tested it and yes I can use it that way, hibernate will save the Food entity, but are there any downsides, pitfalls, etc... I am not seeing.

PS. This is just a simple example illustrating the idea, it is part of old legacy project which I cannot modify to remove constant from DB, and use an enum instead.

2 Answers

To avoid extra call to DB you should use:

FoodType foodType = foodTypeRepository.getOne(FoodType.PERISHABLE); 

under the hood it calls EntityManager.getReference that obtain a reference to an entity without having to load its data as opposed to the foodTypeRepository.findById that lead to call EntityManager.find that obtain an entity along with its data.

See also this section of the hibernate documentation.

P.S. You can not use:

Food food = new Food();
FoodType foodType = new FoodType();
foodType.setFoodTypeId(FoodType.PERISHABLE);

as in this case hibernate consider foodType as a transient entity (not associated with a persistence context) and will try to save it as a new record if you have a proper cascading on your @ManyToOne association.

P.S.S. As it's mentioned in the documentation the method JpaRepository#getOne(ID) is deprecated and you should use JpaRepository#getById(ID) instead.

You do not need to fetch the entity associated with FoodType.PERISHABLE in order to set the relation on a Food entity to it and I'm not aware of any side effects or pitfalls of using FoodType.PERISHABLE directly as long it is a valid FoodType id.

As others mentioned, you could also use JpaRepository#getById(ID id) and that's probably the more canonical way of addressing this problem:

T getById(ID id) Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is implemented this is very likely to always return an instance and throw an EntityNotFoundException on first access. Some of them will reject invalid identifiers immediately.

Related