如何在Hibernate会话中管理未保存实体的映射?

如何在Hibernate会话中管理未保存实体的映射?,hibernate,map,revert,hibernate-session,Hibernate,Map,Revert,Hibernate Session,在我的实体(myGroup:Group)上,我有一个集合属性(members:HashMap),我想允许用户向myGroup.members映射添加多个新的(未持久化的)GroupMembers 我使用集合属性的hashmap的原因是为了确保集合的使用是有效的(即,在执行业务逻辑期间,必须能够通过其ID快速多次获取GroupMember) 我不想马上坚持它们的原因是为了支持添加的恢复。用户必须能够操作myGroup.membersmap,添加和删除成员直到满意为止,然后才能持久化(在Hiber

在我的实体(
myGroup:Group
)上,我有一个集合属性(
members:HashMap
),我想允许用户向
myGroup.members
映射添加多个新的(未持久化的)
GroupMember
s

  • 我使用集合属性的hashmap的原因是为了确保集合的使用是有效的(即,在执行业务逻辑期间,必须能够通过其ID快速多次获取
    GroupMember

  • 我不想马上坚持它们的原因是为了支持添加的恢复。用户必须能够操作
    myGroup.members
    map,添加和删除成员直到满意为止,然后才能持久化(在Hibernate会话和刷新中保存实体)

问题在于,必须为新的
GroupMember
s分配一个零ID,以便Hibernate检测它们是否未保存。(*)因为成员位于映射中,由ID键控,这意味着在保存或还原之前只能添加一个新成员。添加第二个ID为零的新成员只会覆盖(引用)第一个成员

我的问题是如何最好地调整代码解决方案,以支持向集合(
myGroup.members
)添加多个未保存的实体,同时保持使用hashmap或其他映射/字典的性能优势

注意:我已经尝试过一种破解方法,即从
GroupMember
实例中唯一的数据组合派生出一个ID,然后,在保存之前,该ID被设置为零,以便Hibernate检测到它需要被持久化,但这是不雅的,我还没有找到一种方法让它可靠地工作

*我不完全理解Hibernate在这方面的行为

以下
实体的缩写代码:

@Entity
@Table (name="GROUP")
public class Group
{
    private Map<Long,GroupMember> members = new HashMap<Long,GroupMember>();

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "ownerGroup", cascade = { CascadeType.ALL })
    @MapKey(name="id")
    public Map<Long,GroupMember> getMembers() { return this.members; }

    public void setMembers(Map<Long,GroupMember> members) { this.members = members; }

    // ...other properties and methods...
}
@实体
@表(name=“GROUP”)
公共课组
{
私有映射成员=新HashMap();
@OneToMany(fetch=FetchType.LAZY,mappedBy=“ownerGroup”,cascade={CascadeType.ALL})
@映射键(name=“id”)
公共映射getMembers(){返回this.members;}
public void setMembers(映射成员){this.members=members;}
//…其他属性和方法。。。
}

我对您将要实现的目标有点困惑。未持久化的成员没有任何ID。使用ID作为密钥时,这些成员的“查找”行为是什么?对我来说,你可以通过ID查找新成员似乎不合理,因为他们还没有任何身份

如果不需要查找新成员,我建议您:

  • 存储“新成员”的单独属性
  • 在实体中具有方法commit()和revert()
  • 更新完成后,用户将调用commit(),新实体将被移动到实际的“映射”映射/列表中,以实现实际的持久性
  • 如果需要查找新成员,并且从您的文章中看,您似乎可以自己生成一个经过验证的唯一密钥,您会考虑不使用休眠来生成ID吗?您可以在Hibernate中选择是否由Hibernate生成ID(作为代理密钥)并由您提供ID(自然密钥)。我认为自备ID更适合您的情况,因为您似乎没有将该ID视为代理密钥


    编辑:关于OP关于无法单独存储的原因的评论,可以通过这种方法轻松解决(我认为这也更有意义):

    (普塞多代码)

    类组{
    私有列表成员;//在hibernate中映射
    私有列表未保存成员;//未映射
    公共地图getProposedMemberMap(){
    //返回组合的“视图”成员和未保存的成员
    }
    @预更新/ /您可以考虑运行它它也提前更新
    公共无效提交(){
    成员。添加所有(未添加成员);
    未保存的成员。清除();
    }
    公屋{
    未保存的成员。清除();
    }
    }
    

    通过这样做,您可以查看“建议的成员”以进行迭代,同时分别存储实际成员和未保存的成员。

    首先,在生成密钥方面,我受到我必须使用的现有模式的限制,这意味着只有数据库必须为我生成密钥。因此,虽然我同意你的观点,但在这种情况下是不行的。其次,将未保存的实体添加到同一集合属性(而不是单独的集合)的整个要点是,我可以将GUI数据迭代组件绑定到单个集合(从而显示所有成员,包括未保存的成员)。不幸的是,您的建议无法解决问题。为了将未保存的实体分开保存,我认为将“合并成员”视图作为另一个属性公开更为合理,同时将成员和未保存的成员分开保存。请在回答中查看我的最新情况是的,这正是我在今天下午的一次会议上仔细考虑的。这听起来很合理。明天将测试这个想法。@Lisa使用map并不少见,但不常见的是使用ID作为键。很高兴你找到了一个更好的方法来处理这个问题,因为我相信最初的方法有点危险,因为你有一个“中间”ID,而不是真正的ID。
    class Group {
      private List<Member> members;  // mapped in hibernate
      private List<Member> unsavedMembers;  // unmapped
    
      public Map<Long, Member> getProposedMemberMap() {
        // returned the combined "view" members and unsavedMembers
      }
    
      @PreUpdate  // you may consider running it it pre-update too
      public void commit() {
        members.addAll(unsavedMembers);
        unsavedMembers.clear();
      }
    
      public void revert() {
        unsavedMembers.clear();
      }
    }