好吧,这可能是一个微不足道的问题,但我很难想象和理解它们的区别,以及何时使用它们。我还不太清楚像单向映射和双向映射这样的概念如何影响一对多/多对多关系。我现在使用的是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
}

在这种情况下,会有什么样的映射呢?希望您能回答这个特定的示例,但我还想了解一下什么时候使用一对多和多对多,什么时候使用连接表和连接列,什么时候使用单向和双向。


看一下这篇文章:映射对象关系

映射时需要考虑两类对象关系。第一类是基于多样性的,包括三种类型:

*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. 

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



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,但它也可能没有…


一对多:一个人有很多技能,一个技能在人之间不会重复使用。

单向:一个人可以直接通过他的技能集引用技能 双向:每个“子”技能都有一个指向 Person(在代码中没有显示)

多对多:一个人拥有多个技能,一个技能在多个人之间重复使用。

单向:一个人可以直接通过他的技能集引用技能 双向:一项技能有一组与之相关的人。

在一对多关系中,一个对象是“父对象”,一个是“子对象”。父元素控制子元素的存在。在多对多中,任何一种类型的存在都依赖于它们之外的东西(在更大的应用程序上下文中)。

您的主题(领域)应该规定关系是一对多还是多对多——然而,我发现使关系单向还是双向是一种工程决策,需要权衡内存、处理、性能等。

令人困惑的是,多对多双向关系不需要是对称的!也就是说,一群人可以指向一项技能,但该技能不需要只与这些人相关。通常情况下是这样的,但这种对称性并不是必需的。以爱为例——它是双向的(“我爱”,“爱我”),但通常是不对称的(“我爱她,但她不爱我”)!

所有这些都得到Hibernate和JPA的良好支持。请记住,Hibernate或任何其他ORM在管理双向多对多关系时并不关心保持对称性……这完全取决于应用程序。


首先,阅读所有的小字。注意,NHibernate(因此,我认为Hibernate也是如此)关系映射与DB和对象图映射有一种有趣的对应关系。例如,一对一关系通常被实现为多对一关系。

其次,在我们告诉你应该如何写你的O/R图之前,我们也必须看到你的DB。特别是,一项技能可以被多人拥有吗?如果是这样,你有一个多对多关系;否则就是多对一。

第三,我不喜欢直接实现多对多关系,而是在您的域模型中对“连接表”建模。,将其视为一个实体,如下所示:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

那你知道你有什么了吗?您有两个一对多的关系。(在本例中,Person可能有一个PersonSkills集合,但不会有一个Skills集合。)然而,有些人更喜欢使用多对多关系(人与技能之间);这是有争议的。

第四,如果你确实有双向关系(例如,不仅Person有一个技能集合,而且Skill也有一个Person集合),NHibernate不会在你的BL中强制执行双向关系;它只理解关系的双向性以实现持久性。

第五,在NHibernate(我假设是Hibernate)中,多对一比一对多(集合映射)更容易正确使用。

好运!


1)圆圈代表实体/ pojo / bean

2) deg是degree在图中的缩写(边数)

PK=主键,FK=外键

注意度和边的名称之间的矛盾。Many对应degree=1, One对应degree >1。


看起来每个人都在回答一对多和多对多的问题:

一对多、多对一和多对多的区别是:

一对多vs多对一是个角度的问题。单向和双向不会影响映射,但会在访问数据的方式上有所不同。

在多对一中,多方将保持对一方的参考。一个很好的例子是“一个州有城市”。在这种情况下,国家是一方,城市是多方。在表cities中会有一个列state_id。

在单向中,Person类将拥有List<Skill>技能但是 技能不会有人缘的人。在双向方面,两者都有 属性被添加,它允许您访问给定的Person 技能(即技能。人)。

在一对多中,一方将是我们的参照点。例如,“A User has Addresses”。在这种情况下,我们可能有三个列address_1_id, address_2_id和address_3_id,或者有一个多列唯一约束user_id的查找表 address_id。

在单向中,用户将有地址地址。双向 将在Address类中有一个额外的List<User>用户。

在多对多中,每一方的成员都可以引用另一方的任意数量的成员。为了实现这一点,使用了一个查找表。医生和病人之间的关系就是一个例子。一个医生可以有很多病人,反之亦然。


我会这样解释:

有个关系

@OneToOne
Person person;

@OneToOne
Nose nose;

一对多-多对一的关系

@OneToMany
Shepherd shepherd;

@ManyToOne
List<Sheep> sheeps;

多对多-多对多关系

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;

一对多

一对多表关系如下所示:

在关系数据库系统中,一对多表关系基于子表中引用父表中一条记录的主键的外键列将两个表关联起来。

在上面的图表中,post_comment表中的post_id列与post表id主键列有外键关系:

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

@ManyToOne注释

在JPA中,映射一对多表关系的最佳方法是使用@ManyToOne注释。

在我们的例子中,PostComment子实体使用@ManyToOne注释映射post_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集合的问题是,我们只能在它们的元素计数相当低时使用它们。

映射一个@ManyToOne关联的最好方法是依靠@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实体具有两个实用程序方法(例如addComment和removeComment),用于同步双向关联的双方。

当您使用双向关联时,您应该提供这些方法,否则,您将面临非常微妙的状态传播问题。

要避免使用单向的@ManyToOne关联,因为它比使用@ManyToOne或双向的@OneToMany关联效率低。

一对一的

一对一的表关系如下所示:

在关系数据库系统中,一对一的表关系基于子表中的Primary Key列链接两个表,Primary Key也是引用父表行的Primary Key的外键。

因此,我们可以说子表与父表共享主键。

在上面的图表中,post_details表中的id列也与post表的id主键列有外键关系:

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

使用JPA @OneToOne和@MapsId注释

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

映射是这样的:

@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_tag表中的post_id列也与post表id主键列有外键关系:

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

并且,post_tag表中的tag_id列与标签表id主键列有外键关系:

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

使用JPA @ManyToMany映射

这是如何映射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);
        }
    }

The tags association in the Post entity only defines the PERSIST and MERGE cascade types. The REMOVE entity state transition doesn't make any sense for a @ManyToMany JPA association since it could trigger a chain deletion that would ultimately wipe both sides of the association. The add/remove utility methods are mandatory if you use bidirectional associations so that you can make sure that both sides of the association are in sync. The Post entity uses the entity identifier for equality since it lacks any unique business key. You can use the entity identifier for equality as long as you make sure that it stays consistent across all entity state transitions. The Tag entity has a unique business key which is marked with the Hibernate-specific @NaturalId annotation. When that's the case, the unique business key is the best candidate for equality checks. The mappedBy attribute of the posts association in the Tag entity marks that, in this bidirectional relationship, the Post entity owns the association. This is needed since only one side can own a relationship, and changes are only propagated to the database from this particular side. The Set is to be preferred, as using a List with @ManyToMany is less efficient.