如何在Hibernate中使用Delta管理多对多关系的版本历史记录?

如何在Hibernate中使用Delta管理多对多关系的版本历史记录?,hibernate,database-design,version,hibernate-mapping,redundancy,Hibernate,Database Design,Version,Hibernate Mapping,Redundancy,我们正在开发一个系统,一个人可以向另一个人发送文档,文档可以有多个附件,如下所示 Document { Set<Attachment> attachments; } 但这种结构的问题是,如果X向Y发送一个带有100个附件的文档,而Y做了一些小的修改,那么我必须创建一个新版本,同时复制新版本的所有附件,其中大部分附件与以前版本中的附件相同,由于我们将拥有数百万个文档,并且每个文档将通过n个用户移动,因此该模型将产生一个包含大量冗余数据的巨大附件表 因此,我们想到了一种连

我们正在开发一个系统,一个人可以向另一个人发送文档,文档可以有多个附件,如下所示

Document {
       Set<Attachment> attachments;
}
但这种结构的问题是,如果X向Y发送一个带有100个附件的文档,而Y做了一些小的修改,那么我必须创建一个新版本,同时复制新版本的所有附件,其中大部分附件与以前版本中的附件相同,由于我们将拥有数百万个文档,并且每个文档将通过n个用户移动,因此该模型将产生一个包含大量冗余数据的巨大附件表

因此,我们想到了一种连接的替代结构,如下所示

Document - id primary key, ver_id primary key
Attachment - id, doc_id, attached_ver_id, detached_version_id
但我无法为具有此结构的文档创建hibernate实体,因此我的问题是,是否有其他表结构能够更好地解决此问题,并且冗余更少,并且可以为上述表结构创建hibernate映射。

Background 当需要保存数据历史记录时,通常有两种可能的方法:

方法1-“克隆”:创建新条目时,其详细信息将从最新的现有条目复制

……或者

方法2-“增量”:存储第一个条目的详细信息。每个后续条目都存储以前版本的更改

优点/缺点:方法1通常更简单、更快,因为可以直接查找任何记录的详细信息,而无需构建它们。但是方法2使用更少的存储空间。(可能值得注意的是,根据我的经验,方法1一直是首选方法,因为检索的简单性和速度通常比存储更重要)

问题是什么?我的理解是,您从方法1开始,但现在更喜欢方法2

答复 但我无法使用此文件为文档创建hibernate实体 结构,所以我的问题是,是否还有其他表结构 能够以较少的冗余更好地解决此问题,并且 可以为上表创建hibernate映射 结构

应该完全可以为这个数据库结构创建实体——每个实体当然只是数据库表的Hibernate类表示。建议在文档版本和附件之间添加映射表:

Document - id primary key, ver_id primary key, ...
Attachment - id primary key, ...
DocumentAttachmentDelta - doc_id, doc_ver_id, attachment_id, added_or_removed_flag
这里的
DocumentAttachmentDelta
是一个明确定义的映射表,其中@manytone关系链接到标识文档版本和附件的主键。它还有一个附加的布尔标志,用于指定是否删除或添加此版本文档的附件。因此,从上面可以看出,对于文档的第一个版本,将添加其所有初始附件,但对于后续版本,仅存储增量,可以是添加或删除

实体详细信息(根据评论中的请求)

@实体
类文档{
/*……其他领域*/
@OneToMany(cascade=CascadeType.ALL,mappedBy=“document”,orphan=true)
列出文档附件三角洲;
}
@实体
班级附件{
/*……其他领域*/
@OneToMany(cascade=CascadeType.ALL,mappedBy=“附件”,orphan=true)
列出文档附件三角洲;
}
@实体
类文档AttachmentDelta{
/*……其他领域*/
@许多酮
文件;
@许多酮
附件;
}

或者您将文档和附件之间的关系定义为@ManyToMany,并通过Java端的回调确保所有附件的文档都具有相同的id,并且只有版本id不同。

如果我真的想使用Delta,我将使用以下模型

@Entity
public class Document {

    @Id
    private String id;

    @Lob
    private byte [] firstVersion; 

    @OneToMany(mappedBy = "document")
    private final Set<Attachment> attachments = Sets.newHashSet();
}
这样,您就拥有了用户创建的文档的原始版本。然后,每个附件都引用文档的早期版本,反映了增量字段中的更改。当用户向用户发送文档的一个版本(实际上只是一个附件)时,您可以在个人/用户实体和附件之间添加多对多。通过这种方式,可以重建发送的文档版本及其所有前辈

因为文档只能有一个初始版本,所以我会考虑一个局部唯一约束(例如PASGRESE中的部分唯一索引)的可能性,强制每个文档中只有一个附件没有delta和父类。但是,这不能在Hibernate中建模

check约束强制没有父级的版本也没有增量,因为它正是文档实体中包含的文档的第一个版本。在这个模型中,“version”字段并不是严格必需的,但是当您想要为文档的特定版本命名并强制每个文档都是唯一的(请参见我的注释中的唯一约束)时,它可能非常有用

但是,我可能会按如下方式解决此问题:

@Entity
public class Document {

    @Id
    private String id;

    @OneToMany(mappedBy = "document")
    private final Set<Attachment> attachments = Sets.newHashSet();

}

@Entity
@Table(uniqueConstraints = {
        @UniqueConstraint(columnNames = { "document_id", "version" })
})
public class Attachment {

    @Id
    private Long id;

    @Column(nullable = false, name = "version")
    private Long version;

    @Lob
    @Column(name = "content")
    private byte [] content;

    @JoinColumn(name = "document_id")
    @ManyToOne(optional = false)
    private Document document;

    @JoinColumn(name = "previous_version_id")
    @ManyToOne(optional = true)
    private Attachment previousVersion;

}
@实体
公共类文档{
@身份证
私有字符串id;
@OneToMany(mappedBy=“document”)
私有最终集附件=Sets.newHashSet();
}
@实体
@表(唯一约束={
@UniqueConstraint(columnNames={“文档id”,“版本”})
})
公共类附件{
@身份证
私人长id;
@列(nullable=false,name=“version”)
私人长版;
@高球
@列(name=“content”)
私有字节[]内容;
@JoinColumn(name=“document\u id”)
@多通(可选=假)
私人文件;
@JoinColumn(name=“以前的版本号”)
@多通(可选=真)
私人附件先前版本;
}
在这里,我仍然需要文档的部分唯一索引_
@Entity
public class Document {

    @Id
    private String id;

    @Lob
    private byte [] firstVersion; 

    @OneToMany(mappedBy = "document")
    private final Set<Attachment> attachments = Sets.newHashSet();
}
@Entity
@Table(uniqueConstraints = {
        @UniqueConstraint(columnNames = { "document_id", "version" })
})
@Check(constraints = "(delta is null and previous_version_id is null) or (delta is not null and previous_version_id is not null)")
public class Attachment {

    @Id
    private Long id;

    @Column(nullable = false, name = "version")
    private Long version;

    @Lob
    @Column(name = "delta")
    private byte [] delta;

    @JoinColumn(name = "document_id")
    @ManyToOne(optional = false)
    private Document document;

    @JoinColumn(name = "previous_version_id")
    @ManyToOne(optional = true)
    private Attachment previousVersion;

}
@Entity
public class Document {

    @Id
    private String id;

    @OneToMany(mappedBy = "document")
    private final Set<Attachment> attachments = Sets.newHashSet();

}

@Entity
@Table(uniqueConstraints = {
        @UniqueConstraint(columnNames = { "document_id", "version" })
})
public class Attachment {

    @Id
    private Long id;

    @Column(nullable = false, name = "version")
    private Long version;

    @Lob
    @Column(name = "content")
    private byte [] content;

    @JoinColumn(name = "document_id")
    @ManyToOne(optional = false)
    private Document document;

    @JoinColumn(name = "previous_version_id")
    @ManyToOne(optional = true)
    private Attachment previousVersion;

}