Hibernate 中的双向一对一关系
Bidirectional one-to-one relationships in Hibernate
有两个table:飞机和引擎
引擎 table 的组成如下 [Engine_ID, Engine_Name,Airplane_Owner_ID]
。
飞机 table 组成如下 [Airplane_ID, Left_Engine, Right_Engine]
Left_Engine和Right_Engine是引擎table的外键,此外Airplane_Owner_ID是飞机table的外键。因此在飞机和引擎之间定义了三个一对一的关系 tables.
我知道如何指定两个 table 之间的单个一对一关系,但如何指定两个 table 之间的多个关系?是同一个流程吗?
如何在 Hibernate 中指定这些关系?
"Left_Engine and Right_Engine are foreign keys from the Engine table,
moreover Airplane_Owner_ID is the foreign key from the Airplane table."
您的问题是 Airplane
被 Engine
引用,而 Engine
被 Airplane
引用。在您的数据模型中,每个 table 都是另一个的 child。循环依赖在数据库中和在堆栈的其他部分一样糟糕。
最好的解决办法是修复数据模型。
- 从
Airplane
中删除 Left_Engine
和 Right_Engine
- 将
Engine_Position
添加到 Engine
- 在
Engine (Airplane_Owner_ID, Engine_Position)
上添加唯一约束
- 还在
Engine_Position
上为 LEFT、RIGHT 添加检查约束,或在参考数据上使用外键 table
这个模型有两个优点:
- 清晰的所有权 - 飞机拥有引擎,引擎不拥有飞机。
- 轻松支持不同引擎配置的飞机(一、三、四……)
正如@APC 所说,引擎和飞机之间的循环依赖是一个糟糕的设计。但是使用下面描述的解决方案,Engine.Airplane_Owner_ID 只能作为逻辑后备 link 实现,而数据库 table 中不存在。
Airplane.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping">
<hibernate-mapping>
<class name="Airplane" table="AIRPLANE">
<id name="id" type="int" column="AIRPLANE_ID">
<generator class="native"/>
</id>
<property name="name" column="AIRPLANE_NAME" type="string" length="250"/>
<many-to-one name="rightEngine" class="Engine" cascade="save-update" unique="true"/>
<many-to-one name="leftEngine" class="Engine" cascade="save-update" unique="true"/>
</class>
</hibernate-mapping>
Engine.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping">
<hibernate-mapping>
<class name="Engine" table="ENGINE">
<id name="id" type="int" column="ENGINE_ID">
<generator class="native"/>
</id>
<property name="name" column="ENGINE_NAME" type="string" length="250"/>
<property name="position" column="ENGINE_POSITION" type="java.lang.Byte" />
<one-to-one name="ownerAirplane" property-ref="rightEngine" />
</class>
</hibernate-mapping>
Airplane.java
public class Airplane {
private int id;
private String name;
private Engine rightEngine;
private Engine leftEngine;
public Airplane(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Engine getRightEngine() {
return rightEngine;
}
public void setRightEngine(Engine rightEngine) {
this.rightEngine = rightEngine;
}
public Engine getLeftEngine() {
return leftEngine;
}
public void setLeftEngine(Engine leftEngine) {
this.leftEngine = leftEngine;
}
@Override
public String toString() {
return "Airplane{" +
"id=" + id +
", name='" + name + '\'' +
", rightEngline=" + (rightEngine == null ? null : rightEngine.getName()) +
", leftEngine=" + (leftEngine == null ? null : leftEngine.getName()) +
'}';
}
}
Engine.java
public class Engine {
private int id;
private String name;
private byte position;//0=left, 1=right
private Airplane ownerAirplane;
/**
* @param name
* @param position 0=left, 1=right
*/
public Engine(String name, byte position) {
this.name = name;
this.position = position;
}
public Airplane getOwnerAirplane() {
return ownerAirplane;
}
public void setOwnerAirplane(Airplane ownerAirplane) {
this.ownerAirplane = ownerAirplane;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* @return 0=left, 1=right
*/
public byte getPosition() {
return position;
}
/**
* @param position 0=left, 1=right
*/
public void setPosition(byte position) {
this.position = position;
}
@Override
public String toString() {
return "Engine{" +
"id=" + id +
", name='" + name + '\'' +
", position=" + position +
", ownerAirplane=" + (ownerAirplane == null ? null : ownerAirplane.getName()) +
'}';
}
}
Main.java
public static void main(final String[] args) throws Exception {
Session session = ourSessionFactory.openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
Engine engineRight1 = new Engine("engineRight1", (byte) 1);
Engine engineLeft1 = new Engine("engineLeft1", (byte) 0);
Airplane airplane1 = new Airplane("Airplane1");
Engine engineRight2 = new Engine("engineRight2", (byte) 1);
Engine engineLeft2 = new Engine("engineLeft2", (byte) 0);
Airplane airplane2 = new Airplane("Airplane2");
Engine engineRight3 = new Engine("engineRight3", (byte) 1);
Engine engineLeft3 = new Engine("engineLeft3", (byte) 0);
Airplane airplane3 = new Airplane("Airplane3");
engineLeft1.setOwnerAirplane(airplane1);
engineRight1.setOwnerAirplane(airplane1);
airplane1.setLeftEngine(engineLeft1);
airplane1.setRightEngine(engineRight1);
engineRight2.setOwnerAirplane(airplane2);
airplane2.setRightEngine(engineRight2);
// airplane2.setLeftEngine(engineLeft1);
engineRight3.setOwnerAirplane(airplane3);
airplane3.setLeftEngine(engineLeft3);
session.save(airplane1);
session.save(airplane2);
session.save(airplane3);
session.save(engineLeft1);
session.save(engineLeft2);
session.save(engineLeft3);
session.save(engineRight1);
session.save(engineRight2);
session.save(engineRight3);
transaction.commit();
} catch (HibernateException e) {
transaction.rollback();
e.printStackTrace();
}
有两个table:飞机和引擎
引擎 table 的组成如下 [Engine_ID, Engine_Name,Airplane_Owner_ID]
。
飞机 table 组成如下 [Airplane_ID, Left_Engine, Right_Engine]
Left_Engine和Right_Engine是引擎table的外键,此外Airplane_Owner_ID是飞机table的外键。因此在飞机和引擎之间定义了三个一对一的关系 tables.
我知道如何指定两个 table 之间的单个一对一关系,但如何指定两个 table 之间的多个关系?是同一个流程吗?
如何在 Hibernate 中指定这些关系?
"Left_Engine and Right_Engine are foreign keys from the Engine table,
moreover Airplane_Owner_ID is the foreign key from the Airplane table."
您的问题是 Airplane
被 Engine
引用,而 Engine
被 Airplane
引用。在您的数据模型中,每个 table 都是另一个的 child。循环依赖在数据库中和在堆栈的其他部分一样糟糕。
最好的解决办法是修复数据模型。
- 从
Airplane
中删除 - 将
Engine_Position
添加到Engine
- 在
Engine (Airplane_Owner_ID, Engine_Position)
上添加唯一约束
- 还在
Engine_Position
上为 LEFT、RIGHT 添加检查约束,或在参考数据上使用外键 table
Left_Engine
和 Right_Engine
这个模型有两个优点:
- 清晰的所有权 - 飞机拥有引擎,引擎不拥有飞机。
- 轻松支持不同引擎配置的飞机(一、三、四……)
正如@APC 所说,引擎和飞机之间的循环依赖是一个糟糕的设计。但是使用下面描述的解决方案,Engine.Airplane_Owner_ID 只能作为逻辑后备 link 实现,而数据库 table 中不存在。
Airplane.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping">
<hibernate-mapping>
<class name="Airplane" table="AIRPLANE">
<id name="id" type="int" column="AIRPLANE_ID">
<generator class="native"/>
</id>
<property name="name" column="AIRPLANE_NAME" type="string" length="250"/>
<many-to-one name="rightEngine" class="Engine" cascade="save-update" unique="true"/>
<many-to-one name="leftEngine" class="Engine" cascade="save-update" unique="true"/>
</class>
</hibernate-mapping>
Engine.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping">
<hibernate-mapping>
<class name="Engine" table="ENGINE">
<id name="id" type="int" column="ENGINE_ID">
<generator class="native"/>
</id>
<property name="name" column="ENGINE_NAME" type="string" length="250"/>
<property name="position" column="ENGINE_POSITION" type="java.lang.Byte" />
<one-to-one name="ownerAirplane" property-ref="rightEngine" />
</class>
</hibernate-mapping>
Airplane.java
public class Airplane {
private int id;
private String name;
private Engine rightEngine;
private Engine leftEngine;
public Airplane(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Engine getRightEngine() {
return rightEngine;
}
public void setRightEngine(Engine rightEngine) {
this.rightEngine = rightEngine;
}
public Engine getLeftEngine() {
return leftEngine;
}
public void setLeftEngine(Engine leftEngine) {
this.leftEngine = leftEngine;
}
@Override
public String toString() {
return "Airplane{" +
"id=" + id +
", name='" + name + '\'' +
", rightEngline=" + (rightEngine == null ? null : rightEngine.getName()) +
", leftEngine=" + (leftEngine == null ? null : leftEngine.getName()) +
'}';
}
}
Engine.java
public class Engine {
private int id;
private String name;
private byte position;//0=left, 1=right
private Airplane ownerAirplane;
/**
* @param name
* @param position 0=left, 1=right
*/
public Engine(String name, byte position) {
this.name = name;
this.position = position;
}
public Airplane getOwnerAirplane() {
return ownerAirplane;
}
public void setOwnerAirplane(Airplane ownerAirplane) {
this.ownerAirplane = ownerAirplane;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* @return 0=left, 1=right
*/
public byte getPosition() {
return position;
}
/**
* @param position 0=left, 1=right
*/
public void setPosition(byte position) {
this.position = position;
}
@Override
public String toString() {
return "Engine{" +
"id=" + id +
", name='" + name + '\'' +
", position=" + position +
", ownerAirplane=" + (ownerAirplane == null ? null : ownerAirplane.getName()) +
'}';
}
}
Main.java
public static void main(final String[] args) throws Exception {
Session session = ourSessionFactory.openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
Engine engineRight1 = new Engine("engineRight1", (byte) 1);
Engine engineLeft1 = new Engine("engineLeft1", (byte) 0);
Airplane airplane1 = new Airplane("Airplane1");
Engine engineRight2 = new Engine("engineRight2", (byte) 1);
Engine engineLeft2 = new Engine("engineLeft2", (byte) 0);
Airplane airplane2 = new Airplane("Airplane2");
Engine engineRight3 = new Engine("engineRight3", (byte) 1);
Engine engineLeft3 = new Engine("engineLeft3", (byte) 0);
Airplane airplane3 = new Airplane("Airplane3");
engineLeft1.setOwnerAirplane(airplane1);
engineRight1.setOwnerAirplane(airplane1);
airplane1.setLeftEngine(engineLeft1);
airplane1.setRightEngine(engineRight1);
engineRight2.setOwnerAirplane(airplane2);
airplane2.setRightEngine(engineRight2);
// airplane2.setLeftEngine(engineLeft1);
engineRight3.setOwnerAirplane(airplane3);
airplane3.setLeftEngine(engineLeft3);
session.save(airplane1);
session.save(airplane2);
session.save(airplane3);
session.save(engineLeft1);
session.save(engineLeft2);
session.save(engineLeft3);
session.save(engineRight1);
session.save(engineRight2);
session.save(engineRight3);
transaction.commit();
} catch (HibernateException e) {
transaction.rollback();
e.printStackTrace();
}