Hibernate的ManyToOne与OneToOne区别

55

我看不出在“多对一”关系和“一对一”关系的方案中有任何区别:

@Entity
public class Order {

    @ManyToOne
    @JoinColumn(nullable = false)
    private Address address;

对比

@Entity
public class Order {

    @OneToOne
    @JoinColumn(nullable = false)
    private Address address;

有什么不同吗?

4个回答

61

虽然在模式上它们看起来完全相同,但在Hibernate层面有区别。

如果你尝试这样做:

Address address = new Address();
Order order1 = new Order();
order1.setAddress(address);
Order order2 = new Order();
order2.setAddress(address);
save();

一切都会没问题的。但是,如果你尝试获取订单,请在保存后执行:

@OneToOne case:
org.hibernate.HibernateException: More than one row with the given identifier was found: 1

@ManyToOne case:
SUCCESS

当然,在这两种情况下,你的 Address 类应该看起来不同。


所以我可以放心地在两种映射之间切换吗? - DD.
1
“在两个映射之间切换”是什么意思?如果您已经有了数据库模式,并且决定在应用程序中更改映射注释,则可以在不更改模式的情况下执行此操作(从@OneToOne到@ManyToOne)。另一种方式也可以实现,但是可能会出现数据问题。 - paulek
9
对于未来阅读paulek帖子的任何人:为了避免该错误,您的实体必须恰到好处。如果您只有单向关系,那么不会出现该错误。如果您有双向关系,只有在使用连接获取订单的地址选择时才会生成该错误。 - Martin Försterling
1
为什么在OneToOne情况下的save()方法中实际上没有生成异常? - sql_dummy
我尝试使用 session.save(order1) 保存订单1,然后使用 session.save(order2) 保存订单2,但是出现了异常 org.hibernate.PropertyValueException: not-null property references a null or transient value - sql_dummy
这里也可以告诉我哪个对象调用了save();方法吗? - sql_dummy

8

在OneToOne关联的情况下,通常应该对address_id连接列设置唯一约束来确保只有一个订单可以有一个特定的地址。


1
这似乎不是事实。 - DD.
至少在MySQL中,它会创建一个“唯一”类型的索引作为外键。 - borjab

5
TL;DR: 在第一眼看起来,模式可能看起来相同,但是 OneToOne 可能具有额外的 UNIQUE INDEX 约束。这保证了 OneToOne 关联。
这在 Doctrine ORM documentation of Association Mapping 中有很好的说明(我认为它不特定于 Hibernate)。
例如:
ManyToOne:
考虑表 User 和 Address,而列 User.address_id 具有 ManyToOne 关联到列 Address.id。这将是 SQL:
CREATE TABLE User (
    id INT AUTO_INCREMENT NOT NULL,
    address_id INT DEFAULT NULL,
    PRIMARY KEY(id)
) ENGINE = InnoDB;

CREATE TABLE Address (
    id INT AUTO_INCREMENT NOT NULL,
    PRIMARY KEY(id)
) ENGINE = InnoDB;

ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id); 

一对一关系:

现在,考虑表ProductShipment,其中列Product.shipment_idShipment.id列具有一对一(单向)关联。这是SQL语句:

CREATE TABLE Product (
    id INT AUTO_INCREMENT NOT NULL,
    shipment_id INT DEFAULT NULL,
    UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipment_id),
    PRIMARY KEY(id)
) ENGINE = InnoDB;
CREATE TABLE Shipment (
    id INT AUTO_INCREMENT NOT NULL,
    PRIMARY KEY(id)
) ENGINE = InnoDB;
ALTER TABLE Product ADD FOREIGN KEY (shipment_id) REFERENCES Shipment(id);

唯一的区别是 UNIQUE INDEX 指令,它规定在 Product 表中不得出现两次 shipment.id,从而保证了 OneToOne 关联。

0

这表示通过具有相同主键,双方的关系都能得到最好的服务。因为在 @OneToOne 关系的情况下,双方都只与一个订单相关联,所以这样做更合理


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接