首页 > 解决方案 > 在同一事务中查找实体时,多对一关系始终为空

问题描述

我有一个与 ModeratorEntity 具有多对一关系的 ConversationEntity

@Entity
@Table(name="CONVERSATIONS")
public class ConversationEntity {

    @Id
    private Integer id;
    
    private Integer moderatorId;

    @ManyToOne
    @JoinColumn(name="moderatorId", insertable=false, updatable=false)
    private ModeratorEntity moderator;
}

@Entity
@Table(name="MODERATORS")
public class ModeratorEntity {

    @Id
    private Integer id;
    
    private Integer name;
}

以及一个具有事务方法的服务类,它首先保存 ModeratorEntity,然后保存之前创建的具有 moderatorId 的 ConversationEntity

    @Transactional
    public void doStuff(Moderator moderator, Integer conversationId) {
        Integer moderatorId = moderatorService.save(moderator);
        Integer conversationId = conversationService.save(conversationId, moderatorId);

        //do other stuff

        Conversation conversation = conversationService.findById(conversationId);
    }

当我试图在同一事务中通过 id 查找 ConversationEntity 时,下面几行,我得到了设置了字段 moderatorId 但 ModeratorEntity 对象 = null 的 ConversationEntity。

如果我在事务之外执行此操作,我将正确设置 ModeratorEntity 对象。

我尝试在 ModeratorRepository 和 ConversationRepository 中使用 saveAndFlush 并在 ManyToOne 关系中设置 FetchType.EAGER 但它们都不起作用

标签: javahibernatejpaspring-data-jpatransactions

解决方案


有很多关于类似问题的问题。例如,与 Spring Data 双向关系的 @Transactional 返回 nullHibernate:比较当前和上一个记录

只要您在单个事务中,您将始终获得相同的实例,并且实际上不会从数据库中加载任何数据。而且因为(至少看起来)您从未将引用设置为ModeratorEntityit stays null

一旦你在一个新事务中,数据库就会被访问,JPA 会填充一个新实例,现在包括一个ModeratorEntity引用。

因此,可能的修复是:

  • 代替 id 让我们moderatorService.save返回一个实体并将其设置在Conversation. 你还不如放弃moderatorId. 这是使用 JPA 做事的惯用方式。

  • 在单独的事务中执行查询。弹簧TransactionTemplate可能会派上用场。虽然这确实有效,但它会导致 JPA 内部渗透到您的应用程序中,我建议避免这种情况。


推荐阅读