From 14891191e18f7966f779e132c97f1fe7f0643d3f Mon Sep 17 00:00:00 2001
From: Martin Goik <goik@hdm-stuttgart.de>
Date: Wed, 24 Apr 2013 13:15:35 +0200
Subject: [PATCH] Bi-directional navigation

---
 Doc/course.xml                                | 244 +++++++++++++-----
 .../main/java/entity/company4/Employee.java   |  71 +++++
 .../src/main/java/entity/company4/Laptop.java |  63 +++++
 .../main/java/entity/company4/Persist.java    |  38 +++
 .../java/entity/company4/hibernate.cfg.xml    |  21 ++
 .../main/java/entity/company5/Persist.java    |  41 +++
 .../java/entity/company5/hibernate.cfg.xml    |  21 ++
 .../java/entity/company5/model/Employee.java  |  83 ++++++
 .../java/entity/company5/model/Laptop.java    |  64 +++++
 9 files changed, 577 insertions(+), 69 deletions(-)
 create mode 100644 ws/eclipse/HibIntro/src/main/java/entity/company4/Employee.java
 create mode 100644 ws/eclipse/HibIntro/src/main/java/entity/company4/Laptop.java
 create mode 100644 ws/eclipse/HibIntro/src/main/java/entity/company4/Persist.java
 create mode 100644 ws/eclipse/HibIntro/src/main/java/entity/company4/hibernate.cfg.xml
 create mode 100644 ws/eclipse/HibIntro/src/main/java/entity/company5/Persist.java
 create mode 100644 ws/eclipse/HibIntro/src/main/java/entity/company5/hibernate.cfg.xml
 create mode 100644 ws/eclipse/HibIntro/src/main/java/entity/company5/model/Employee.java
 create mode 100644 ws/eclipse/HibIntro/src/main/java/entity/company5/model/Laptop.java

diff --git a/Doc/course.xml b/Doc/course.xml
index 53a3ce009..be2229636 100644
--- a/Doc/course.xml
+++ b/Doc/course.xml
@@ -18648,20 +18648,23 @@ public class PersistUser {
       <section xml:id="mappingEntities">
         <title>Mapping entities</title>
 
-        <para>In contrast to mapping an entity having components we may also
-        persist relations between entities. We start with
-        <classname>entity.company1.Employee</classname>s possibly having
-        <classname>entity.company1.Laptop</classname>s assigned. So an
-        employee may or may not have one device at its disposal.</para>
-
-        <para>There may be other references to each laptop. So in contrast to
-        our email component examples
-        <classname>entity.company1.Laptop</classname> instances are
-        identifiable database objects and hence entities. Therefore we have to
-        add a <code>@OneToOne</code> annotation <coref
-        linkend="laptopOneToOne"/>:</para>
-
-        <programlisting>package entity.company1;
+        <section xml:id="mappingOneToOne">
+          <title>One to one</title>
+
+          <para>In contrast to mapping an entity having components we may also
+          persist relations between entities. We start with
+          <classname>entity.company1.Employee</classname>s possibly having
+          <classname>entity.company1.Laptop</classname>s assigned. So an
+          employee may or may not have one device at its disposal.</para>
+
+          <para>There may be other references to each laptop. So in contrast
+          to our email component examples
+          <classname>entity.company1.Laptop</classname> instances are
+          identifiable database objects and hence entities. Therefore we have
+          to add a <code>@OneToOne</code> annotation <coref
+          linkend="laptopOneToOne"/>:</para>
+
+          <programlisting>package entity.company1;
 ...
 @Entity ...
 public class Employee {
@@ -18680,10 +18683,10 @@ public class Employee {
   private Laptop laptop;
 ...</programlisting>
 
-        <para>Populating the database with data seems to be quite
-        straightforward:</para>
+          <para>Populating the database with data seems to be quite
+          straightforward:</para>
 
-        <programlisting>package entity.company1;
+          <programlisting>package entity.company1;
 ...
 public class Persist1 {
 ...
@@ -18696,24 +18699,24 @@ public class Persist1 {
       session.save(eve);
       transaction.commit(); ...</programlisting>
 
-        <para>This actually fails:</para>
+          <para>This actually fails:</para>
 
-        <programlisting> <classname>org.hibernate.TransientObjectException</classname>: 
+          <programlisting> <classname>org.hibernate.TransientObjectException</classname>: 
   object references an unsaved transient instance -
   save the transient instance before flushing: <classname>entity.company1.Laptop</classname></programlisting>
 
-        <para>There are at least two possible solutions:</para>
+          <para>There are at least two possible solutions:</para>
 
-        <orderedlist>
-          <listitem>
-            <para>We may save the dependent transient
-            <classname>entity.company1.Laptop</classname> instance along with
-            the two <classname>entity.company1.Employee</classname> instances.
-            There is no restriction regarding the order of these three save
-            operations but they must happen within the same
-            transaction:</para>
-
-            <programlisting>package entity.company1;
+          <orderedlist>
+            <listitem>
+              <para>We may save the dependent transient
+              <classname>entity.company1.Laptop</classname> instance along
+              with the two <classname>entity.company1.Employee</classname>
+              instances. There is no restriction regarding the order of these
+              three save operations but they must happen within the same
+              transaction:</para>
+
+              <programlisting>package entity.company1;
 ...
 public class Persist2 {
 ...
@@ -18728,17 +18731,17 @@ public class Persist2 {
       session.save(superCoolGadget);
 
       transaction.commit(); ...</programlisting>
-          </listitem>
+            </listitem>
 
-          <listitem>
-            <para>Hibernate supports the <quote>persistence by
-            reachability</quote> concept. Thus saving an
-            <classname>entity.company1.Employee</classname> instance should
-            save possibly referenced
-            <classname>entity.company1.Laptop</classname> instances as
-            well:</para>
-
-            <programlisting>package entity.company2;
+            <listitem>
+              <para>Hibernate supports the <quote>persistence by
+              reachability</quote> concept. Thus saving an
+              <classname>entity.company1.Employee</classname> instance should
+              save possibly referenced
+              <classname>entity.company1.Laptop</classname> instances as
+              well:</para>
+
+              <programlisting>package entity.company2;
 ...
 public class Employee {
 ...
@@ -18746,10 +18749,10 @@ public class Employee {
   public Laptop getLaptop() { return laptop;}
   ...</programlisting>
 
-            <para>No we do no longer have to care about referenced
-            <classname>entity.company1.Laptop</classname>s:</para>
+              <para>No we do no longer have to care about referenced
+              <classname>entity.company1.Laptop</classname>s:</para>
 
-            <programlisting>package entity.company2;
+              <programlisting>package entity.company2;
 ...
 public class Persist {
 ...
@@ -18764,14 +18767,14 @@ public class Persist {
       session.save(martin);
       session.save(eve);
       transaction.commit();...</programlisting>
-          </listitem>
-        </orderedlist>
+            </listitem>
+          </orderedlist>
 
-        <para>After successfully inserting these three entities
-        <personname>Eve Blix</personname> shall no longer be entitled to use
-        her laptop. We thus decouple these two entities:</para>
+          <para>After successfully inserting these three entities
+          <personname>Eve Blix</personname> shall no longer be entitled to use
+          her laptop. We thus decouple these two entities:</para>
 
-        <programlisting>package entity.company2;
+          <programlisting>package entity.company2;
 public class LoadAndDeRegister {
 ...
       final Transaction transaction = session.beginTransaction();
@@ -18781,29 +18784,132 @@ public class LoadAndDeRegister {
 
       transaction.commit();</programlisting>
 
-        <para>All three entities are still present in our database but the
-        corresponding foreign key has been set to <code>NULL</code>.</para>
+          <para>All three entities are still present in our database but the
+          corresponding foreign key has been set to <code>NULL</code>.</para>
 
-        <qandaset role="exercise">
-          <title>One laptop per employee</title>
+          <qandaset role="exercise">
+            <title>One laptop per employee</title>
 
-          <qandadiv>
-            <qandaentry>
-              <question>
-                <para>Consider a company where each employee must have a
-                laptop being assigned and modify the mapping in
-                <classname>entity.company2.Employee</classname>
-                accordingly.</para>
-              </question>
+            <qandadiv>
+              <qandaentry>
+                <question>
+                  <para>Consider a company where each employee must have a
+                  laptop being assigned and modify the mapping in
+                  <classname>entity.company2.Employee</classname>
+                  accordingly.</para>
+                </question>
 
-              <answer>
-                <para>See <emphasis
-                role="bold">@OneToOne(optional=false,...</emphasis> in
-                <classname>entity.company3.Employee</classname>.</para>
-              </answer>
-            </qandaentry>
-          </qandadiv>
-        </qandaset>
+                <answer>
+                  <para>See <emphasis
+                  role="bold">@OneToOne(optional=false,...</emphasis> in
+                  <classname>entity.company3.Employee</classname>.</para>
+                </answer>
+              </qandaentry>
+            </qandadiv>
+          </qandaset>
+        </section>
+
+        <section xml:id="oneToOneBidirectionalNavigate">
+          <title>Bi-directional navigation</title>
+
+          <para>So far instances of
+          <classname>entity.company3.Laptop</classname> have no references to
+          their possible respective <quote>owner</quote>: A <abbrev
+          role="g">Java</abbrev> developer may navigate from
+          <classname>entity.company3.Employee</classname> to
+          <classname>entity.company3.Laptop</classname> via
+          <methodname>entity.company3.Employee.getLaptop()</methodname> but
+          there is no path back. This requirement may be implemented by
+          introducing a new property owner <coref
+          linkend="introduceOwnerBiDirect"/>:</para>
+
+          <programlisting>package entity.company4;
+...
+@Entity
+public class Laptop {
+
+  @OneToOne
+  public Employee getOwner() <co xml:id="introduceOwnerBiDirect"/> { return owner;}
+  public void setOwner(Employee owner) { this.owner = owner;}
+  Employee owner;  ...</programlisting>
+
+          <para>We may insert suitable instances:</para>
+
+          <programlisting>package entity.company4;
+...
+public class Persist {
+...
+      final Employee  martin = new Employee(123, "Martin Goik"),
+                      eve = new Employee(140, "Eve Blix");
+      
+      final Laptop superCoolGadget = new Laptop(2213),
+          g2 = new Laptop(2214),
+          g3 = new Laptop(2215); // Not being referenced later
+      
+      martin.setLaptop(g2);
+      eve.setLaptop(superCoolGadget);
+      // Now only save the employee transients,
+      // superCoolGadget will be saved automatically due to
+      // CascadeType.ALL 
+      session.save(martin);
+      session.save(eve);
+      session.save(g3);
+      transaction.commit();...</programlisting>
+
+          <para>This codes executes but does not yield a correct database
+          state:</para>
+
+          <programlisting>mysql&gt; select * from Employee; select * from Laptop;
+| id | cname       | staffNumber | laptop_id |
++----+-------------+-------------+-----------+
+|  1 | Martin Goik |         123 |         1 |
+|  2 | Eve Blix    |         140 |         2 |
+
+| id | idNumber | owner_id |
++----+----------+----------+
+|  1 |     2214 |     NULL | <emphasis role="bold">&lt;-- Expectded to be 1 (Martin Goik)</emphasis>
+|  2 |     2213 |     NULL | <emphasis role="bold">&lt;-- Expectded to be 2 (Eve Blix)</emphasis>
+|  3 |     2215 |     NULL |</programlisting>
+
+          <para>This problem is actually <abbrev role="g">pojo</abbrev>
+          related. In contrast to Container Managed Persistence (<abbrev
+          role="g">CMP</abbrev>) the <link linkend="gloss_JPA">JPA</link>
+          standard does not care about implementing bi directional navigation.
+          Instead this task is left to the user. We may change the setter on
+          the <classname>entity.company4.Employee</classname> side of the
+          relationship:</para>
+
+          <programlisting>package entity.company5.model;
+...
+public class Employee {
+
+@OneToOne(optional=false, cascade={CascadeType.ALL})
+  public Laptop getLaptop() { return laptop;}
+  public void setLaptop(final Laptop laptop) {
+    if (getLaptop() == laptop) {
+      return;
+    }
+    if (null != this.laptop) {
+      this.laptop.setOwner(null);
+    }
+    this.laptop = laptop;
+    if (null != this.laptop) {
+      this.laptop.setOwner(this);
+    }
+  } ...</programlisting>
+
+          <para>There is a database issue present here as well. Having just a
+          <code>@OneToOne</code> relationship it suffices to have just on
+          foreign key in our database. This is achieved by adding a
+          <code>mappedBy</code> attribute:</para>
+
+          <programlisting>package entity.company5.model;
+...
+public class Laptop {
+...
+@OneToOne (<emphasis role="bold">mappedBy="laptop"</emphasis>)
+  public Employee getOwner() { return owner;} ...</programlisting>
+        </section>
       </section>
     </chapter>
   </part>
diff --git a/ws/eclipse/HibIntro/src/main/java/entity/company4/Employee.java b/ws/eclipse/HibIntro/src/main/java/entity/company4/Employee.java
new file mode 100644
index 000000000..208d8537e
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/entity/company4/Employee.java
@@ -0,0 +1,71 @@
+package entity.company4;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+/**
+ * 
+ *         {@link Employee} instances related to {@link Department}
+ * 
+ */
+@Entity
+@Table(uniqueConstraints={@UniqueConstraint(columnNames={"staffNumber"})})
+public class Employee {
+
+  @Id
+  @GeneratedValue
+  public Long getId() {return id;}
+  protected void setId(Long id) {this.id = id;}
+  private Long id;
+
+  public int getStaffNumber() { return staffNumber;}
+  public void setStaffNumber(int staffNumber) { this.staffNumber = staffNumber;}
+  int staffNumber = Integer.MIN_VALUE;
+
+  public String getCname() { return cname;}
+  public void setCname(String cname) { this.cname = cname;}
+  private String cname; // Common name
+  
+  /**
+   * One laptop per {@link Employee}.
+   * 
+   * @return Laptop  "owned" by {@link Employee}.
+   */
+  @OneToOne(optional=false, cascade={CascadeType.ALL})
+  public Laptop getLaptop() { return laptop;}
+  public void setLaptop(Laptop laptop) {this.laptop = laptop;}
+  private Laptop laptop;// Workstation for employees.
+  
+  protected Employee(){}
+  
+  public Employee(final int staffNumber, final String cname) {
+    setStaffNumber(staffNumber);
+    setCname(cname);
+  }
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    } else if (Integer.MIN_VALUE == getStaffNumber()) {
+      return false;
+    } else if (other instanceof Employee) {
+      final Employee that = (Employee) other;
+      return this.getStaffNumber() == that.getStaffNumber();
+    } else {
+      return false;
+    }
+  }
+  @Override
+  public int hashCode() {
+    if (Integer.MIN_VALUE == getStaffNumber()) {
+      return System.identityHashCode(this);
+    } else {
+      return getStaffNumber();
+    }
+  }
+}
\ No newline at end of file
diff --git a/ws/eclipse/HibIntro/src/main/java/entity/company4/Laptop.java b/ws/eclipse/HibIntro/src/main/java/entity/company4/Laptop.java
new file mode 100644
index 000000000..e390f0c91
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/entity/company4/Laptop.java
@@ -0,0 +1,63 @@
+package entity.company4;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+/**
+ * 
+ * Laptops possibly being assigned to {@link Employee}   
+ * 
+ */
+@Entity
+@Table(uniqueConstraints={@UniqueConstraint(columnNames={"idNumber"})})
+public class Laptop {
+
+  @Id
+  @GeneratedValue
+  public Long getId() {return id;}
+  protected void setId(Long id) {this.id = id;}
+  private Long id;
+
+  /**
+   * @return The device's unique part number
+   */
+  public int getIdNumber() { return idNumber;}
+  public void setIdNumber(int idNumber) { this.idNumber = idNumber;}
+  int idNumber;
+  
+  @OneToOne
+  public Employee getOwner() { return owner;}
+  public void setOwner(Employee owner) { this.owner = owner;}
+  Employee owner;
+  
+  protected Laptop(){}
+  
+  public Laptop(final int idNumber) {
+    setIdNumber(idNumber);
+  }
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    } else if (Integer.MIN_VALUE == getIdNumber()) {
+      return false;
+    } else if (other instanceof Laptop) {
+      final Laptop that = (Laptop) other;
+      return this.getIdNumber() == that.getIdNumber();
+    } else {
+      return false;
+    }
+  }
+  @Override
+  public int hashCode() {
+    if (Integer.MIN_VALUE == getIdNumber()) {
+      return System.identityHashCode(this);
+    } else {
+      return getIdNumber();
+    }
+  }
+}
\ No newline at end of file
diff --git a/ws/eclipse/HibIntro/src/main/java/entity/company4/Persist.java b/ws/eclipse/HibIntro/src/main/java/entity/company4/Persist.java
new file mode 100644
index 000000000..ae153a163
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/entity/company4/Persist.java
@@ -0,0 +1,38 @@
+package entity.company4;
+
+import hibintro.util.HibernateUtil;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+
+/**
+ * Persisting {@link Employee} possibly along with
+ * personal assigned laptops.
+ */
+public class Persist {
+  /**
+   * @param args not used.
+   */
+  public static void main(String[] args) {
+    final Session session = HibernateUtil.createSessionFactory("entity/company4/hibernate.cfg.xml").openSession();
+    {
+      final Transaction transaction = session.beginTransaction();
+      final Employee  martin = new Employee(123, "Martin Goik"),
+                      eve = new Employee(140, "Eve Blix");
+      
+      final Laptop superCoolGadget = new Laptop(2213),
+          g2 = new Laptop(2214),
+          g3 = new Laptop(2215); // Not being referenced later
+      
+      martin.setLaptop(g2);
+      eve.setLaptop(superCoolGadget);
+      // Now only save the employee transients,
+      // superCoolGadget will be saved automatically due to
+      // CascadeType.ALL 
+      session.save(martin);
+      session.save(eve);
+      session.save(g3);
+      transaction.commit();
+    }
+  }
+}
\ No newline at end of file
diff --git a/ws/eclipse/HibIntro/src/main/java/entity/company4/hibernate.cfg.xml b/ws/eclipse/HibIntro/src/main/java/entity/company4/hibernate.cfg.xml
new file mode 100644
index 000000000..b07752abc
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/entity/company4/hibernate.cfg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE hibernate-configuration PUBLIC
+	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+ <session-factory >
+  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
+  <property name="hibernate.connection.password">XYZ</property>
+  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hdm</property>
+  <property name="hibernate.connection.username">hdmuser</property>
+  <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
+  <property name="hibernate.show_sql">false</property>
+  <property name="hibernate.format_sql">true</property>
+  <property name="hibernate.hbm2ddl.auto">update</property>
+  
+  <mapping class="entity.company4.Laptop"/>
+  <mapping class="entity.company4.Employee"/>
+ </session-factory>
+</hibernate-configuration>
diff --git a/ws/eclipse/HibIntro/src/main/java/entity/company5/Persist.java b/ws/eclipse/HibIntro/src/main/java/entity/company5/Persist.java
new file mode 100644
index 000000000..92d009745
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/entity/company5/Persist.java
@@ -0,0 +1,41 @@
+package entity.company5;
+
+import hibintro.util.HibernateUtil;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+
+import entity.company5.model.Employee;
+import entity.company5.model.Laptop;
+
+/**
+ * Persisting {@link Employee} possibly along with
+ * personal assigned laptops.
+ */
+public class Persist {
+  /**
+   * @param args not used.
+   */
+  public static void main(String[] args) {
+    final Session session = HibernateUtil.createSessionFactory("entity/company5/hibernate.cfg.xml").openSession();
+    {
+      final Transaction transaction = session.beginTransaction();
+      final Employee  martin = new Employee(123, "Martin Goik"),
+                      eve = new Employee(140, "Eve Blix");
+      
+      final Laptop superCoolGadget = new Laptop(2213),
+          g2 = new Laptop(2214),
+          g3 = new Laptop(2215); // Not being referenced later
+      
+      martin.setLaptop(g2);
+      eve.setLaptop(superCoolGadget);
+      // Now only save the employee transients,
+      // superCoolGadget will be saved automatically due to
+      // CascadeType.ALL 
+      session.save(martin);
+      session.save(eve);
+      session.save(g3);
+      transaction.commit();
+    }
+  }
+}
\ No newline at end of file
diff --git a/ws/eclipse/HibIntro/src/main/java/entity/company5/hibernate.cfg.xml b/ws/eclipse/HibIntro/src/main/java/entity/company5/hibernate.cfg.xml
new file mode 100644
index 000000000..bbc11212f
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/entity/company5/hibernate.cfg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE hibernate-configuration PUBLIC
+	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+ <session-factory >
+  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
+  <property name="hibernate.connection.password">XYZ</property>
+  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hdm</property>
+  <property name="hibernate.connection.username">hdmuser</property>
+  <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
+  <property name="hibernate.show_sql">false</property>
+  <property name="hibernate.format_sql">true</property>
+  <property name="hibernate.hbm2ddl.auto">update</property>
+  
+  <mapping class="entity.company5.Laptop"/>
+  <mapping class="entity.company5.Employee"/>
+ </session-factory>
+</hibernate-configuration>
diff --git a/ws/eclipse/HibIntro/src/main/java/entity/company5/model/Employee.java b/ws/eclipse/HibIntro/src/main/java/entity/company5/model/Employee.java
new file mode 100644
index 000000000..4d0744658
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/entity/company5/model/Employee.java
@@ -0,0 +1,83 @@
+package entity.company5.model;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+
+/**
+ * 
+ *         {@link Employee} instances related to {@link Department}
+ * 
+ */
+@Entity
+@Table(uniqueConstraints={@UniqueConstraint(columnNames={"staffNumber"})})
+public class Employee {
+
+  @Id
+  @GeneratedValue
+  public Long getId() {return id;}
+  protected void setId(Long id) {this.id = id;}
+  private Long id;
+
+  public int getStaffNumber() { return staffNumber;}
+  public void setStaffNumber(int staffNumber) { this.staffNumber = staffNumber;}
+  int staffNumber = Integer.MIN_VALUE;
+
+  public String getCname() { return cname;}
+  public void setCname(String cname) { this.cname = cname;}
+  private String cname; // Common name
+
+  /**
+   * One {@link Laptop} per {@link Employee}.
+   * 
+   * @return {@link Laptop}  "owned" by {@link Employee}.
+   */
+  @OneToOne(optional=false, cascade={CascadeType.ALL})
+  public Laptop getLaptop() { return laptop;}
+  public void setLaptop(final Laptop laptop) {
+    if (getLaptop() == laptop) {
+      return;
+    }
+    if (null != this.laptop) {
+      this.laptop.setOwner(null);
+    }
+    this.laptop = laptop;
+    if (null != this.laptop) {
+      this.laptop.setOwner(this);
+    }
+  }
+  private Laptop laptop;// Workstation for employees.
+
+  protected Employee(){}
+
+  public Employee(final int staffNumber, final String cname) {
+    setStaffNumber(staffNumber);
+    setCname(cname);
+  }
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    } else if (Integer.MIN_VALUE == getStaffNumber()) {
+      return false;
+    } else if (other instanceof Employee) {
+      final Employee that = (Employee) other;
+      return this.getStaffNumber() == that.getStaffNumber();
+    } else {
+      return false;
+    }
+  }
+  @Override
+  public int hashCode() {
+    if (Integer.MIN_VALUE == getStaffNumber()) {
+      return System.identityHashCode(this);
+    } else {
+      return getStaffNumber();
+    }
+  }
+}
\ No newline at end of file
diff --git a/ws/eclipse/HibIntro/src/main/java/entity/company5/model/Laptop.java b/ws/eclipse/HibIntro/src/main/java/entity/company5/model/Laptop.java
new file mode 100644
index 000000000..933c4292c
--- /dev/null
+++ b/ws/eclipse/HibIntro/src/main/java/entity/company5/model/Laptop.java
@@ -0,0 +1,64 @@
+package entity.company5.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+
+/**
+ * 
+ * Laptops possibly being assigned to {@link Employee}   
+ * 
+ */
+@Entity
+@Table(uniqueConstraints={@UniqueConstraint(columnNames={"idNumber"})})
+public class Laptop {
+
+  @Id
+  @GeneratedValue
+  public Long getId() {return id;}
+  protected void setId(Long id) {this.id = id;}
+  private Long id;
+
+  /**
+   * @return The device's unique part number
+   */
+  public int getIdNumber() { return idNumber;}
+  public void setIdNumber(int idNumber) { this.idNumber = idNumber;}
+  int idNumber;
+  
+  @OneToOne (mappedBy="laptop")
+  public Employee getOwner() { return owner;}
+  protected void setOwner(Employee owner) { this.owner = owner;}
+  Employee owner;
+  
+  protected Laptop(){}
+  
+  public Laptop(final int idNumber) {
+    setIdNumber(idNumber);
+  }
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    } else if (Integer.MIN_VALUE == getIdNumber()) {
+      return false;
+    } else if (other instanceof Laptop) {
+      final Laptop that = (Laptop) other;
+      return this.getIdNumber() == that.getIdNumber();
+    } else {
+      return false;
+    }
+  }
+  @Override
+  public int hashCode() {
+    if (Integer.MIN_VALUE == getIdNumber()) {
+      return System.identityHashCode(this);
+    } else {
+      return getIdNumber();
+    }
+  }
+}
\ No newline at end of file
-- 
GitLab