hibernate 一对多、多对一和多对多的区别?

c86crjj0  于 2023-10-23  发布在  其他
关注(0)|答案(8)|浏览(103)

好吧,这可能是一个微不足道的问题,但我很难想象和理解这些差异以及何时使用它们。我还不太清楚单向和双向Map等概念如何影响一对多/多对多关系。我现在正在使用Hibernate,所以任何与ORM相关的解释都会有所帮助。
举个例子,假设我有以下设置:

public class Person {
    private Long personId;
    private Set<Skill> skills;
    //Getters and setters
}

public class Skill {
    private Long skillId;
    private String skillName;
    //Getters and setters
}

那么在这种情况下我会有什么样的Map呢?对于这个具体例子的答案,我非常感激,但我也很想概述一下什么时候使用一对多和多对多,什么时候使用连接表和连接列,什么时候使用单向和双向。

46qrfjad

46qrfjad1#

看起来每个人都在回答One-to-many与。Many-to-many
One-to-manyMany-to-oneMany-to-Many的区别是:

One-to-many vs Many-to-one是一个Angular 问题UnidirectionalBidirectional不会影响Map,但会影响您如何访问数据。

  • Many-to-one中,many侧将保留one侧的引用。一个很好的例子是“一个国家有城市”。在这种情况下,State是一侧,City是多侧。表cities中将有一列state_id

在 * 单向 * 中,Person类将有List<Skill> skills,但Skill将没有Person person。在 bidirectional 中,这两个属性都被添加了,它允许你在给定技能的情况下访问Person(即skill.person)。

  • One-to-Many中,一侧将是我们的参考点。例如,“一个用户有一个用户”。在这种情况下,我们可能有三个列address_1_idaddress_2_idaddress_3_idlook up table,其中multi column unique constraintuser_idaddress_id上。

在 * 单向 * 中,User将具有Address addressBidirectional 将在Address类中增加List<User> users

  • Many-to-Many中,每一方的成员可以引用另一方的任意数量的成员。为了实现这一点,使用了look up table。这方面的例子是医生和病人之间的关系。一个医生可以有很多病人,反之亦然。
ukdjmx9f

ukdjmx9f2#

一对多:一个人有很多技能,技能不能在人与人之间重复使用
*单向:一个人可以直接引用技能通过其集
*双向:每个“子”技能都有一个指向Person的指针(在代码中没有显示)
多对多:一个人有很多技能,技能在人与人之间重复使用
*单向:一个人可以直接引用技能通过其集
*双向:一个技能有一组与之相关的人。

在一对多关系中,一个对象是“父对象”,另一个对象是“子对象”。父母控制孩子的存在。在多对多中,任何一种类型的存在都依赖于它们之外的东西(在更大的应用程序上下文中)。
你的主题(领域)应该决定关系是一对多还是多对多--然而,我发现使关系单向或双向是一个权衡内存、处理、性能等的工程决策。
令人困惑的是,多对多双向关系不需要是对称的!也就是说,一群人可以指向一个技能,但这个技能不需要仅仅与这些人相关。通常是这样的,但这种对称性不是必要条件。以爱为例,它是双向的(“我爱”,“爱我”),但往往是不对称的(“我爱她,但她不爱我”)!
所有这些都得到了Hibernate和JPA的支持。请记住,Hibernate或任何其他ORM都不会在管理双向多对多关系时给予关于保持对称性的提示.

gorkyyrv

gorkyyrv3#

1)圆圈是实体/POJO/Bean
2)deg是度的缩写,如图中的度(边数)
PK=主键,FK=外键
请注意度数和边的名称之间的矛盾。许多对应于度=1,而一个对应于度>1。

6ie5vjzr

6ie5vjzr4#

一对多

一对多表关系如下所示:

在关系数据库系统中,一对多表关系基于子表中的Foreign Key列引用父表中一条记录的Primary Key来关联两个表。
在上面的表格关系图中,post_comment表格中的post_id列与post表格id Primary Key列具有Foreign Key关系:

ALTER TABLE
        post_comment
    ADD CONSTRAINT
        fk_post_comment_post_id
    FOREIGN KEY (post_id) REFERENCES post

@ManyToOne annotation

在JPA中,Map一对多表关系的最佳方式是使用@ManyToOne注解。
在我们的例子中,PostComment子实体使用@ManyToOne注解Mappost_id外键列:

@Entity(name = "PostComment")
    @Table(name = "post_comment")
    public class PostComment {
    
        @Id
        @GeneratedValue
        private Long id;
    
        private String review;
    
        @ManyToOne(fetch = FetchType.LAZY)
        private Post post;
        
    }

使用JPA @OneToMany注解

仅仅因为您可以选择使用@OneToMany注解,并不意味着它应该是所有 * 一对多 * 数据库关系的默认选项。
JPA集合的问题是,我们只能在它们的元素数量相当少的时候使用它们。
Map@OneToMany关联的最佳方式是依赖@ManyToOne端来传播所有实体状态更改:

@Entity(name = "Post")
    @Table(name = "post")
    public class Post {
    
        @Id
        @GeneratedValue
        private Long id;
    
        private String title;
    
        @OneToMany(
            mappedBy = "post", 
            cascade = CascadeType.ALL, 
            orphanRemoval = true
        )
        private List<PostComment> comments = new ArrayList<>();
    
        //Constructors, getters and setters removed for brevity
    
        public void addComment(PostComment comment) {
            comments.add(comment);
            comment.setPost(this);
        }
    
        public void removeComment(PostComment comment) {
            comments.remove(comment);
            comment.setPost(null);
        }
    }

Post实体具有两个实用程序方法(例如,addCommentremoveComment),其用于同步双向关联的两侧。
无论何时使用双向关联,您都应该提供这些方法,否则您将面临very subtle state propagation issues的风险。
应该避免单向@OneToMany关联,因为它的效率低于使用@ManyToOne或双向@OneToMany关联。

一对一

一对一表关系如下所示:

在关系数据库系统中,一对一表关系基于子表中的Primary Key列链接两个表,该列也是引用父表行的Primary KeyForeign Key
因此,我们可以说子表与父表共享Primary Key
在上面的表格关系图中,post_details表格中的id列也与post表格中的idPrimary Key列具有Foreign Key关系:

ALTER TABLE
        post_details
    ADD CONSTRAINT
        fk_post_details_id
    FOREIGN KEY (id) REFERENCES post

使用带@MapsId注解的JPA @OneToOne

Map@OneToOne关系的最佳方法是使用@MapsId。这样,您甚至不需要双向关联,因为您总是可以通过使用Post实体标识符来获取PostDetails实体。
Map如下所示:

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {

    @Id
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name = "id")
    private Post post;

    public PostDetails() {}

    public PostDetails(String createdBy) {
        createdOn = new Date();
        this.createdBy = createdBy;
    }

    //Getters and setters omitted for brevity
}

这样,id属性既充当主键又充当外键。您会注意到,@Id列不再使用@GeneratedValue注解,因为标识符填充了post关联的标识符。

多对多

多对多表关系如下所示:

在关系数据库系统中,多对多表关系通过子表链接两个父表,子表包含引用两个父表的Primary Key列的两个Foreign Key列。
在上面的表格关系图中,post_tag表中的post_id列也与post表id Primary Key列具有Foreign Key关系:

ALTER TABLE
        post_tag
    ADD CONSTRAINT
        fk_post_tag_post_id
    FOREIGN KEY (post_id) REFERENCES post

而且,post_tag表中的tag_id列与tag表id Primary Key列具有Foreign Key关系:

ALTER TABLE
        post_tag
    ADD CONSTRAINT
        fk_post_tag_tag_id
    FOREIGN KEY (tag_id) REFERENCES tag

使用JPA @ManyToManyMap

下面是如何将many-to-many表关系Map到JPA和Hibernate:

@Entity(name = "Post")
    @Table(name = "post")
    public class Post {

        @Id
        @GeneratedValue
        private Long id;

        private String title;

        @ManyToMany(cascade = { 
            CascadeType.PERSIST, 
            CascadeType.MERGE
        })
        @JoinTable(name = "post_tag",
            joinColumns = @JoinColumn(name = "post_id"),
            inverseJoinColumns = @JoinColumn(name = "tag_id")
        )
        private Set<Tag> tags = new HashSet<>();

        //Getters and setters ommitted for brevity

        public void addTag(Tag tag) {
            tags.add(tag);
            tag.getPosts().add(this);
        }

        public void removeTag(Tag tag) {
            tags.remove(tag);
            tag.getPosts().remove(this);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Post)) return false;
            return id != null && id.equals(((Post) o).getId());
        }

        @Override
        public int hashCode() {
            return getClass().hashCode();
        }
    }

    @Entity(name = "Tag")
    @Table(name = "tag")
    public class Tag {

        @Id
        @GeneratedValue
        private Long id;

        @NaturalId
        private String name;

        @ManyToMany(mappedBy = "tags")
        private Set<Post> posts = new HashSet<>();

        //Getters and setters ommitted for brevity

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Tag tag = (Tag) o;
            return Objects.equals(name, tag.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name);
        }
    }
  1. Post实体中的tags关联仅定义PERSISTMERGE级联类型。REMOVE实体状态转换对于@ManyToMany JPA关联没有任何意义,因为它可能触发链删除,最终删除关联的两端。
    1.如果您使用双向关联,则添加/删除实用程序方法是必需的,以便您可以确保关联的两端同步。
  2. Post实体使用实体标识符进行相等,因为它缺少任何唯一的业务键。只要确保实体标识符在所有实体状态转换中保持一致,就可以使用实体标识符来实现相等性。
  3. Tag实体有一个唯一的业务密钥,该密钥标记有特定于Hibernate的@NaturalId注解。在这种情况下,唯一业务密钥是the best candidate for equality checks
  4. Tag实体中的posts关联的mappedBy属性标记,在此双向关系中,Post实体拥有该关联。这是必要的,因为只有一方可以拥有关系,并且更改只能从这一特定方传播到数据库。
  5. Set是首选,因为使用List@ManyToMany效率较低。
v6ylcynt

v6ylcynt5#

我会这样解释:

一对一-一对一(一个人有一个鼻子-一个鼻子有一个比索)

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany - ManyToOne(一个牧羊人有许多牧羊人-一只羊有一个牧羊人)

@OneToMany
Shepherd shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany - ManyToMany(许多旅行者有许多目的地-

许多目的地有许多旅行者)

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;
brjng4g3

brjng4g36#

看看这篇文章:Map对象关系
Map时需要关注两类对象关系。第一类基于多样性,包括三种类型:

*One-to-one relationships.  This is a relationship where the maximums of each of its multiplicities is one, an example of which is holds relationship between Employee and Position in Figure 11.  An employee holds one and only one position and a position may be held by one employee (some positions go unfilled).
*One-to-many relationships. Also known as a many-to-one relationship, this occurs when the maximum of one multiplicity is one and the other is greater than one.  An example is the works in relationship between Employee and Division.  An employee works in one division and any given division has one or more employees working in it.
*Many-to-many relationships. This is a relationship where the maximum of both multiplicities is greater than one, an example of which is the assigned relationship between Employee and Task.  An employee is assigned one or more tasks and each task is assigned to zero or more employees.

第二类是基于方向性的,它包括两种类型,单向关系和双向关系。

*Uni-directional relationships.  A uni-directional relationship when an object knows about the object(s) it is related to but the other object(s) do not know of the original object.  An example of which is the holds relationship between Employee and Position in Figure 11, indicated by the line with an open arrowhead on it.  Employee objects know about the position that they hold, but Position objects do not know which employee holds it (there was no requirement to do so).  As you will soon see, uni-directional relationships are easier to implement than bi-directional relationships.
*Bi-directional relationships.  A bi-directional relationship exists when the objects on both end of the relationship know of each other, an example of which is the works in relationship between Employee and Division.  Employee objects know what division they work in and Division objects know what employees work in them.
cidc1ykv

cidc1ykv7#

这可能需要如下的多对多关系

public class Person{

    private Long personId;
    @manytomany

    private Set skills;
    //Getters and setters
}

public class Skill{
    private Long skillId;
    private String skillName;
    @manyToMany(MappedBy="skills,targetClass="Person")
    private Set persons; // (people would not be a good convenion)
    //Getters and setters
}

你可能需要定义一个joinTable + JoinColumn,但它也可以工作,没有.

xtupzzrd

xtupzzrd8#

首先,阅读所有的小字。请注意,NHibernate(因此,我假设Hibernate也是如此)关系Map与DB和对象图Map有一个有趣的对应关系。例如,一对一关系通常被实现为多对一关系。
第二,在我们告诉你应该如何编写O/RMap之前,我们还必须看看你的数据库。尤其是一个技能,能被多个人拥有吗?如果是这样的话,你有一个多对多的关系;否则就是多对一
第三,我不喜欢直接实现多对多关系,而是在域模型中对“连接表”进行建模--即,将其视为一个实体,如下所示:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

那你看看你有什么您有两个一对多关系。(在这种情况下,Person可能有一个PersonSkills的集合,但不会有Skills的集合。)然而,有些人更喜欢使用多对多关系(Person和Skill之间);这是有争议的。
第四,如果你有双向关系(例如,不仅Person有Skills的集合,Skill也有Person的集合),NHibernate不会在BL中为你强制双向性;它仅理解用于持久性目的的关系的双向性。
第五,在NHibernate(我假设是Hibernate)中,多对一比一对多(集合Map)更容易正确使用。
祝你好运!

相关问题