java – Hibernate OneToOne BiDirectional可选关系:在没有可选对象的情况下插入时工作,使用新的可选对象更新时中断

我在两个对象ChecklistItem和ButtonAction之间进行了以下OneToOne关系设置(如下面的代码片段所示).我想这是一种独特的设置.它是双向的,但在ChecklistItem方面是可选的,而ButtonAction是所有者,将ChecklistItem的外键存储为其主键.

ChecklistItem类:

@Entity
@Table(name = "CHECKLIST_ITEM", schema = "CHKL_APP")
public class ChecklistItem implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CHECKLIST_ITEM_ID_SEQ")
    @SequenceGenerator(name = "CHECKLIST_ITEM_ID_SEQ", sequenceName = "CHKL_APP.CHECKLIST_ITEM_ID_SEQ")
    private Long id;

    @OneToOne(optional = true, mappedBy = "checklistItem", cascade = CascadeType.ALL)
    private ButtonAction button;

    //...
}

ButtonAction类:

@Entity
@Table(name = "ACTION_BUTTON", schema = "CHKL_APP")
public class ButtonAction implements Serializable {
    @Id
    @Column(name = "checklist_item_id", unique = true, nullable = false, insertable = true, updatable = false)
    @GenericGenerator(name = "generate-from-checklist-item", strategy = "foreign", parameters = @Parameter(name = "property", value = "checklistItem"))
    @GeneratedValue(generator = "generate-from-checklist-item")
    private Long checklistItemId;

    @OneToOne
    @PrimaryKeyJoinColumn
    @JsonIgnore
    private ChecklistItem checklistItem;

    //...
}

我正在使用SpringBoot,所以我只有一个ChecklistItemRepository接口,它扩展了SpringBoot的CrudRepository:

public interface ChecklistItemRepository extends CrudRepository<ChecklistItem, Long> {}

在我的ChecklistItem服务中,我已经将save方法配置为这样工作:

@Service
@Transactional
public class ChecklistItemServiceImpl implements ChecklistItemService {

    @Override
    public ChecklistItem saveChecklistItem(ChecklistItem checklistItem) {
        processButtonAction(checklistItem);
        return checklistItemRepository.save(checklistItem);
    }

    private void processButtonAction(ChecklistItem checklistItem,String username) {
        ButtonAction button = checklistItem.getButton();

        if(button != null) {
            button.setChecklistItem(checklistItem);

            if(checklistItem.getId() != null){
                button.setChecklistItemId(checklistItem.getId());
            }
        }
    }

    //...
}

因此,无论何时保存ChecklistItem(通过POST或PUT),它都会在调用保存之前更新ButtonAction(当用户选择包含一个时),并引用ChecklistItem及其ID(如果不为null).

这是我的问题……当用户使用新的ButtonAction(用户最初在没有ButtonAction的情况下发布了ChecklistItem)的情况下输入ChecklistItem时,我收到以下错误:

org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.me.chklapp.checklistitem.action.ButtonAction.checklistItem];
nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.me.chklapp.checklistitem.action.ButtonAction.checklistItem]

我在网上找到的每一个“答案”都说需要设置这种关系,但我已经在我的服务中这样做了.我已经通过调试和检查每个对象对另一个对象的非空引用来验证它是这样做的.此外,我找不到其他人遇到同样的问题,在某些情况下,它会保存,而在其他情况下,它会中断;在这些情况下,它是全有或全无.

当我打开更详细的hibernate日志记录时,我能够看到的唯一可疑的事情就是在抛出错误之前,它在cheklistitem id匹配的buttonaction表上执行选择.我猜Hibernate这样做是为了确定它是否需要在buttonaction表上进行插入或更新.但也许它会使用那个空行而不是我的ChecklistItem上的ButtonAction对象?

谢谢你的帮助!

最佳答案 说实话,我仍然不确定为什么我的原始问题出现或为什么这个解决方案有效,所以如果有人可以对这些事情有所了解,请评论;我希望更好地理解.

感谢@CarlitosWay和@Matthew与我一起努力寻找解决方案.当CarlitosWay评论说不需要GeneratedValue和GenericGenerator时,CarlitosWay正在做些什么.我不能删除它们;我需要一些方法来告诉Hibernate从哪里获取ButtonAction的ID,但它让我开始寻找替代配置.我发现了一个看起来很有希望的地方:MapsId.我查看了一些例子,然后四处讨论,看看会有什么用.如果不出意外,这似乎证实我有一些独特的设置,因为我的解决方案与MapsId的常用示例不同.

我在下面发布我的结果代码,但是,我仍然不完全确定Hibernate如何处理所有这些,所以我可能在这里有一些多余的注释.如果可能的话,请告诉我如何清理它.

ButtonAction类:

@Entity
@Table(name = "ACTION_BUTTON", schema = "CHKL_APP")
public class ButtonAction implements Serializable {
    @Id
    @Column(name = "checklist_item_id", unique = true, nullable = false, insertable = true, updatable = false)
    private Long checklistItemId;

    @OneToOne
    @MapsId
    @PrimaryKeyJoinColumn
    @JsonIgnore
    private ChecklistItem checklistItem;

    //...
}

基本上,我在checklistItem上的MapsId注释的checklistItemId上交换了GenericGenerator和GeneratedValue注释.在我看到的大多数其他示例中,MapsId注释位于另一个类(在本例中为ChecklistItem),但我在想,因为ButtonAction是关联的所有者以及ID来自何处,它需要在本机对象上. ChecklistItem,ChecklistItemRepository和ChecklistItemServiceImpl在我的问题中与我原来的代码都没有变化.

从理论上讲,我的原始代码是Hibernate做这个JPA等效的方法.但由于他们的行为不同,我必须误解一些事情,所以如果你知道原因,请回复!

点赞