public class Customer {
// This field doesn't exist in the database
// It is simulated with a SQL query
// "OO speak": Customer owns the orders
private List<Order> orders;
}
public class Order {
// This field actually exists in the DB
// In a purely OO model, we could omit it
// "DB speak": Order contains a foreign key to customer
private Customer customer;
}
反向侧是对象的面向对象“所有者”,在这种情况下是客户。客户在表中没有列来存储订单,因此您必须告诉它在订单表中可以保存这些数据的位置(通过mappedBy
实现)。
另一个常见的例子是具有既可以是父节点也可以是子节点的节点的树。在这种情况下,两个字段在一个类中使用:
public class Node {
// Again, this is managed by Hibernate.
// There is no matching column in the database.
@OneToMany(cascade = CascadeType.ALL) // mappedBy is only necessary when there are two fields with the type "Node"
private List<Node> children;
// This field exists in the database.
// For the OO model, it's not really necessary and in fact
// some XML implementations omit it to save memory.
// Of course, that limits your options to navigate the tree.
@ManyToOne
private Node parent;
}
@Parent
或@Child
注解来表示连接的含义(而不是如何实现),而不是使用“XtoY”。 - Aaron Digulla令人难以置信的是,在3年内,没有人用映射关系的两种方式举例回答您的问题。
正如其他人所提到的,“所有者”方在数据库中包含指针(外键)。您可以将任何一方指定为所有者,但是,如果您将One side指定为所有者,则关系将不是双向的(反向也就是“many” side将不知道它的“owner”)。这对封装/松耦合可能是有利的:
// "One" Customer owns the associated orders by storing them in a customer_orders join table
public class Customer {
@OneToMany(cascade = CascadeType.ALL)
private List<Order> orders;
}
// if the Customer owns the orders using the customer_orders table,
// Order has no knowledge of its Customer
public class Order {
// @ManyToOne annotation has no "mappedBy" attribute to link bidirectionally
}
唯一的双向映射解决方案是让“多”方拥有指向“一”方的指针,并使用@OneToMany“mappedBy”属性。如果没有“mappedBy”属性,Hibernate将期望进行双重映射(数据库将具有连接列和连接表,这是冗余的(通常是不希望的))。
// "One" Customer as the inverse side of the relationship
public class Customer {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "customer")
private List<Order> orders;
}
// "many" orders each own their pointer to a Customer
public class Order {
@ManyToOne
private Customer customer;
}
在数据库中,拥有带有外键表的实体被称为拥有实体,而被指向的其他表则被称为反向实体。
双向关系的简单规则:
1.对于多对一的双向关系,拥有方总是关系的拥有方。例如:一个房间有很多人(一个人只属于一个房间)-> 拥有方是人。
2.对于一对一的双向关系,拥有方对应包含相应外键的一侧。
3.对于多对多的双向关系,任何一侧都可以是拥有方。
希望能够帮到您。
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "BoyFriend21")
public class BoyFriend21 {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Boy_ID")
@SequenceGenerator(name = "Boy_ID", sequenceName = "Boy_ID_SEQUENCER", initialValue = 10,allocationSize = 1)
private Integer id;
@Column(name = "BOY_NAME")
private String name;
@OneToOne(cascade = { CascadeType.ALL })
private GirlFriend21 girlFriend;
public BoyFriend21(String name) {
this.name = name;
}
public BoyFriend21() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BoyFriend21(String name, GirlFriend21 girlFriend) {
this.name = name;
this.girlFriend = girlFriend;
}
public GirlFriend21 getGirlFriend() {
return girlFriend;
}
public void setGirlFriend(GirlFriend21 girlFriend) {
this.girlFriend = girlFriend;
}
}
import org.hibernate.annotations.*;
import javax.persistence.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "GirlFriend21")
public class GirlFriend21 {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Girl_ID")
@SequenceGenerator(name = "Girl_ID", sequenceName = "Girl_ID_SEQUENCER", initialValue = 10,allocationSize = 1)
private Integer id;
@Column(name = "GIRL_NAME")
private String name;
@OneToOne(cascade = {CascadeType.ALL},mappedBy = "girlFriend")
private BoyFriend21 boyFriends = new BoyFriend21();
public GirlFriend21() {
}
public GirlFriend21(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GirlFriend21(String name, BoyFriend21 boyFriends) {
this.name = name;
this.boyFriends = boyFriends;
}
public BoyFriend21 getBoyFriends() {
return boyFriends;
}
public void setBoyFriends(BoyFriend21 boyFriends) {
this.boyFriends = boyFriends;
}
}
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import java.util.Arrays;
public class Main578_DS {
public static void main(String[] args) {
final Configuration configuration = new Configuration();
try {
configuration.configure("hibernate.cfg.xml");
} catch (HibernateException e) {
throw new RuntimeException(e);
}
final SessionFactory sessionFactory = configuration.buildSessionFactory();
final Session session = sessionFactory.openSession();
session.beginTransaction();
final BoyFriend21 clinton = new BoyFriend21("Bill Clinton");
final GirlFriend21 monica = new GirlFriend21("monica lewinsky");
clinton.setGirlFriend(monica);
session.save(clinton);
session.getTransaction().commit();
session.close();
}
}
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import java.util.List;
public class Main578_Modify {
public static void main(String[] args) {
final Configuration configuration = new Configuration();
try {
configuration.configure("hibernate.cfg.xml");
} catch (HibernateException e) {
throw new RuntimeException(e);
}
final SessionFactory sessionFactory = configuration.buildSessionFactory();
final Session session1 = sessionFactory.openSession();
session1.beginTransaction();
GirlFriend21 monica = (GirlFriend21)session1.load(GirlFriend21.class,10); // Monica lewinsky record has id 10.
BoyFriend21 boyfriend = monica.getBoyFriends();
System.out.println(boyfriend.getName()); // It will print Clinton Name
monica.setBoyFriends(null); // It will not impact relationship
session1.getTransaction().commit();
session1.close();
final Session session2 = sessionFactory.openSession();
session2.beginTransaction();
BoyFriend21 clinton = (BoyFriend21)session2.load(BoyFriend21.class,10); // Bill clinton record
GirlFriend21 girlfriend = clinton.getGirlFriend();
System.out.println(girlfriend.getName()); // It will print Monica name.
//But if Clinton[Who owns the relationship as per "mappedby" rule can break this]
clinton.setGirlFriend(null);
// Now if Monica tries to check BoyFriend Details, she will find Clinton is no more her boyFriend
session2.getTransaction().commit();
session2.close();
final Session session3 = sessionFactory.openSession();
session1.beginTransaction();
monica = (GirlFriend21)session3.load(GirlFriend21.class,10); // Monica lewinsky record has id 10.
boyfriend = monica.getBoyFriends();
System.out.println(boyfriend.getName()); // Does not print Clinton Name
session3.getTransaction().commit();
session3.close();
}
}
在关系型数据库系统中,只有三种类型的表关系:
因此,一对多
表关系如下所示:
请注意,该关系基于子表中的外键列(例如,post_id
)。
因此,在管理 一对多
表关系时,存在单一的真相来源。
现在,如果您采用映射到我们之前看到的 一对多
表关系的双向实体关系:
如果您查看上面的图表,您会发现有两种管理这种关系的方法。
在 Post
实体中,您有 comments
集合:
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
在PostComment
中,post
关联被映射如下:
@ManyToOne(
fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;
所以,您有两种方法可以更改实体关联:
comments
子集合中添加一个条目,应该通过其post_id
列将新的post_comment
行与父post
实体关联起来。PostComment
实体的post
属性,post_id
列也应该被更新。因为有两种表示外键列的方式,所以必须定义哪个是真相来源,当涉及到将关联状态更改转换为等效的外键列值修改时。
mappedBy
属性告诉我们@ManyToOne
方面负责管理外键列,并且集合仅用于获取子实体和级联父实体状态更改到子级(例如删除父级也应该删除子实体)。
它被称为反向侧,因为它引用了管理此表关系的子实体属性。
现在,即使您定义了mappedBy
属性并且子侧@ManyToOne
关联管理外键列,您仍然需要同步双向关联的两侧。
最好的方法是添加这两个实用程序方法:
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
addComment
和 removeComment
方法确保双方同步。因此,如果我们添加一个子实体,则该子实体需要指向父实体,而父实体应该在子集合中包含该子实体。