M-Razavi
M-Razavi

Reputation: 3467

Bidirectional one-to-one relationships in Hibernate

There are two tables: Airplane and Engine

The Engine table is made up as follows [Engine_ID, Engine_Name,Airplane_Owner_ID].
The Airplane table is made up as follows [Airplane_ID, Left_Engine, Right_Engine]

Left_Engine and Right_Engine are foreign keys from the Engine table, moreover Airplane_Owner_ID is the foreign key from the Airplane table. Therefore there are three one-to-one relationships defined between the Airplane and Engine tables.

I know how to specify single one-to-one relationships between two tables but how can I specify multiple relationships between two tables? Is it the same process?

How can these relationships be specified in Hibernate?

Upvotes: 0

Views: 504

Answers (2)

M-Razavi
M-Razavi

Reputation: 3467

As @APC said, Cyclic dependencies between Engine and Airplane is a bad design. But with the solution described below, Engine.Airplane_Owner_ID could be implemented only as logical back-link which not exist in Database 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();
        }

Upvotes: 0

APC
APC

Reputation: 146349

"Left_Engine and Right_Engine are foreign keys from the Engine table,
moreover Airplane_Owner_ID is the foreign key from the Airplane table."

Your problem is Airplane is referenced by Engine and Engine is referenced by Airplane. In your data model each table is the child of the other. Cyclic dependencies are just as bad in the database as they are in other parts of the stack.

The best solution is to fix the data model.

  • Drop Left_Engine and Right_Engine from Airplane
  • add Engine_Position to Engine
  • add a unique constraint on Engine (Airplane_Owner_ID, Engine_Position)
  • also add a check constraint on Engine_Position for LEFT, RIGHT, or use a foreign key on a reference data table

This model has two virtues:

  1. Clear ownership - planes own engines, engines don't own planes.
  2. It easily supports planes with different configurations of engines (one, three, four...)

Upvotes: 2

Related Questions