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> 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"><-- Expectded to be 1 (Martin Goik)</emphasis> +| 2 | 2213 | NULL | <emphasis role="bold"><-- 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