【问题标题】:Can I use only Entity's id instead of fetching an entity from DB when there is a relationship in an Entity?当实体中存在关系时,我可以只使用实体的 id 而不是从数据库中获取实体吗?
【发布时间】:2021-07-14 08:35:42
【问题描述】:

我在 Hibernate 中使用 Spring Data JPA。

假设我定义了以下实体:

@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;
    
    ...
}

每当我想创建一个Food 实体并将其保存到数据库时,当前代码如下所示:

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

foodRepository.save(food);

如果我们认为 FoodType 在数据库中是常量。我可以这样使用吗:

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

foodRepository.save(food);

我已经测试过了,是的,我可以这样使用它,hibernate 会保存Food 实体,但是有什么缺点、陷阱等...我没有看到。

PS。这只是一个说明这个想法的简单示例,它是旧遗留项目的一部分,我无法修改它以从 DB 中删除常量,而是使用枚举。

【问题讨论】:

  • „getById(..)“如果persistanceprovider支持的话,应该只返回一个没有数据库调用的代理。在休眠状态下,它被称为“getReference(..)”。
  • 这个foodType.setFoodTypeId() 不应该作为公共方法公开,也不应该被调用。您可以缓存食物类型,以免每次都加载它们。

标签: java spring hibernate spring-data-jpa spring-data


【解决方案1】:

为避免额外调用 DB,您应该使用:

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

在后台调用EntityManager.getReference 获得对实体的引用而无需加载其数据,而不是导致调用EntityManager.findfoodTypeRepository.findById 获得一个实体及其数据

另请参阅休眠文档的this section

附:你不能使用:

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

在这种情况下,hibernate 将 foodType 视为 transient 实体(不与 persistence context 关联),如果您有正确的 cascading,将尝试将其保存为新记录在您的 @ManyToOne 关联中。

附言正如documentation 中提到的,JpaRepository#getOne(ID) 方法已被弃用,您应该改用JpaRepository#getById(ID)

【讨论】:

  • 感谢您的回答,只是一个简单的问题。你是什​​么意思级联将尝试保存它。如果它与数据库中已有的 id 具有相同的 id,什么级联类型会导致保存它?
  • Hibernate 会尝试将 foodType 保存为一个新的,它会导致 ERROR: duplicate key value violates unique constraint 因为您已经有了具有此 ID 的食物类型。
【解决方案2】:

您无需获取与FoodType.PERISHABLE 关联的实体即可将Food 实体上的关系设置为它,我不知道直接使用FoodType.PERISHABLE 的任何副作用或陷阱这是一个有效的FoodType id。

正如其他人提到的,您也可以使用JpaRepository#getById(ID id),这可能是解决此问题的更规范的方式:

T getById(ID id) 返回给定实体的引用 标识符。取决于 JPA 持久性提供程序的方式 实现这很可能总是返回一个实例并抛出 首次访问时出现 EntityNotFoundException。他们中的一些人会拒绝 立即无效标识符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-12
    • 1970-01-01
    • 2014-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 2015-02-14
    相关资源
    最近更新 更多