diff --git a/Doc/course.xml b/Doc/course.xml index eeb72df40c14773624c16a5471234fe0a12a02d0..519527cf99bb01de4faeaf3a40101f2a98195ddf 100644 --- a/Doc/course.xml +++ b/Doc/course.xml @@ -15966,8 +15966,8 @@ Found user 'Fred Wings'</programlisting> </section> </section> - <section xml:id="mappingClasses"> - <title>Mapping classes and database tables</title> + <section xml:id="mappingSingleClasses"> + <title>Mapping single classes and database tables</title> <section xml:id="transientProperties"> <title>Transient properties</title> @@ -16113,10 +16113,175 @@ Exception in thread "main" org.hibernate.exception.ConstraintViolationException: driver as the result of a constraint violation rather than by the hibernate framework itself prior to attempting the insert.</para> </section> + + <section xml:id="mappingKeys"> + <title>Defining multiple keys</title> + + <para>Frequently we need more than just a primary key. Starting from + <classname>hibintro.v4.User</classname> we may want to add a + property <code>uidNumber</code>. This is a common requirement. On + UNIX type operation systems each user does have both a unique login + name (like <quote>goik</quote>) and a unique numerical value (like + <quote>123</quote>). We choose our primary key to be numeric <coref + linkend="uidNumberIsPrimaryKey"/>and the login name to become a + second candidate key <coref linkend="uidIsUnique"/>:</para> + + <programlisting>package hibintro.v5; +... +@Entity +@Table(uniqueConstraints={@UniqueConstraint(columnNames={"uid"})}) <co + xml:id="uidIsUnique"/> +public class User { + + int uidNumber; + @Id <co xml:id="uidNumberIsPrimaryKey"/> public int getUidNumber() { + return uidNumber; + } + public void setUidNumber(int uidNumber) { + this.uidNumber = uidNumber; + } + + String uid; + @Column(nullable=false) public String getUid() { + return uid; + } + public void setUid(String uid) { + this.uid = uid; + } +...</programlisting> + + <para>Notice the slight difference: The <code>uid</code> property + needs a <code>nullable=false</code> annotation. This is + <emphasis>not</emphasis> automatically inferred from the constraint + definition <coref linkend="uidIsUnique"/>. In contrast the property + <code>uidNumber</code> is not referenced by the preceding + <classname>javax.persistence.Table</classname> annotation but is + itself annotated by <classname>javax.persistence.Id</classname>. + Hence a <code>nullable=false</code> is not needed.</para> + + <para>The <abbrev + xlink:href="http://en.wikipedia.org/wiki/Data_definition_language">DDL</abbrev> + reads:</para> + + <programlisting>CREATE TABLE User ( + uidNumber INT NOT NULL PRIMARY KEY, + cname VARCHAR(255) NOT NULL, + uid VARCHAR(255) NOT NULL UNIQUE +)</programlisting> + </section> + + <section xml:id="sect_ComposedKeys"> + <title>Composed keys</title> + + <para>Composed candidate keys are sometimes referred to as business + keys. The underlying logic defines which objects are identical based + on their values.</para> + + <para>As an example, we consider a company having several + departments. The following business rules shall apply:</para> + + <figure xml:id="projectBusinessRules"> + <title>Business rules for projects</title> + + <orderedlist> + <listitem> + <para>Each department must have a unique name.</para> + </listitem> + + <listitem> + <para>A project's name must be unique within the set of all + projects belonging to the same department.</para> + </listitem> + + <listitem> + <para>A project must be assigned to exactly one + department.</para> + </listitem> + </orderedlist> + </figure> + + <para>Right now we defer considerations of the n:1 relationship + between departments and projects to a later chapter. Instead we + focus just on project instances and represent departments just by + their integer id values which will later become foreign keys.</para> + + <para>In addition each project receives a unique integer id value as + well. This is in accordance with the <quote>best practice</quote> + rule of defining a <link + xlink:href="http://en.wikipedia.org/wiki/Surrogate_key">surrogate + key</link> <coref linkend="projectPrimaryKeyDefinition"/> to be used + as (primary) object identifier. This immutable key will then become + the target in foreign key definitions:</para> + + <informaltable border="1"> + <colgroup width="6%"/> + + <colgroup width="94%"/> + + <tr> + <td valign="top"><emphasis role="bold">Java</emphasis></td> + + <td valign="top"><programlisting>package hibintro.v6; +... +@Entity +@Table(uniqueConstraints={@UniqueConstraint(columnNames={"name", "department"})}) <co + xml:id="projectBusinessKey"/> +public class Project { + int id; + @Id <co xml:id="projectPrimaryKeyDefinition"/> public int getId() {return id;} + protected void setId(int id) {this.id = id;} + + String name; + @Column(nullable=false) public String getName() {return name;} + public void setName(String name) {this.name = name;} + + int department; + @Column(nullable=false) + public int getDepartment() {return department;} + public void setDepartment(int department) {this.department = department;} +...</programlisting></td> + </tr> + + <tr> + <td valign="top"><emphasis role="bold">Sql</emphasis></td> + + <td><programlisting>CREATE TABLE Project ( + id int(11) NOT NULL PRIMARY KEY <coref linkend="projectPrimaryKeyDefinition"/>, + department int(11) NOT NULL, + name varchar(255) NOT NULL, + UNIQUE KEY name (name,department) +)</programlisting></td> + </tr> + </informaltable> + + <calloutlist> + <callout arearefs="projectPrimaryKeyDefinition"> + <para>Defining the surrogate primary key.</para> + </callout> + + <callout arearefs="projectBusinessKey"> + <para>Defining a business key composed of project name and + department number. This implements our second business rule in + <xref linkend="projectBusinessRules"/>.</para> + </callout> + </calloutlist> + </section> + + <section xml:id="sect_RenameTablesAndAttributes"> + <title>Renaming tables and attributes</title> + + <para/> + </section> + + <section xml:id="sectChangeDefaultTypeMapping"> + <title>Changing the default type mapping</title> + + <para/> + </section> </section> <section xml:id="sect_hibernateValidation"> - <title>Hibernate validation </title> + <title>Hibernate validation</title> <para/> </section> diff --git a/ws/eclipse/HibIntro/src/main/java/hibintro/v5/User.java b/ws/eclipse/HibIntro/src/main/java/hibintro/v5/User.java index 9a09b0c39d01fbd2f6a97a254ba5851a20f4aaa4..d437d3b7b348c529315b984903a90421901006bf 100644 --- a/ws/eclipse/HibIntro/src/main/java/hibintro/v5/User.java +++ b/ws/eclipse/HibIntro/src/main/java/hibintro/v5/User.java @@ -9,7 +9,7 @@ import javax.persistence.UniqueConstraint; /** * @author goik * - * {@link User} instances with primary key and unique constrait + * {@link User} instances with primary key and unique constraint * */ @Entity diff --git a/ws/eclipse/HibIntro/src/main/java/hibintro/v6/PersistProject.java b/ws/eclipse/HibIntro/src/main/java/hibintro/v6/PersistProject.java new file mode 100644 index 0000000000000000000000000000000000000000..48851b058176d564b08c1923f839a8879c631c48 --- /dev/null +++ b/ws/eclipse/HibIntro/src/main/java/hibintro/v6/PersistProject.java @@ -0,0 +1,29 @@ +package hibintro.v6; + +import hibintro.util.HibernateUtil; + +import org.hibernate.Session; +import org.hibernate.Transaction; + +/** + * @author goik + * + * Persisting a simple instance of {@link Project} + * + */ +public class PersistProject { + + /** + * @param args not used. + */ + public static void main(String[] args) { + final Session session = HibernateUtil.createSessionFactory("hibintro/v6/hibernate.cfg.xml").openSession(); + + final Transaction transaction = session.beginTransaction(); + { + final Project u = new Project("New web-server", 3); + session.save(u); + } + transaction.commit(); + } +} \ No newline at end of file diff --git a/ws/eclipse/HibIntro/src/main/java/hibintro/v6/Project.java b/ws/eclipse/HibIntro/src/main/java/hibintro/v6/Project.java new file mode 100644 index 0000000000000000000000000000000000000000..fb6a81279c5afa4d550a0f3f2aeea1a2bc38a1c9 --- /dev/null +++ b/ws/eclipse/HibIntro/src/main/java/hibintro/v6/Project.java @@ -0,0 +1,53 @@ +package hibintro.v6; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +/** + * @author goik + * + * {@link Project} instances with surrogate primary key and composed business key + */ +@Entity +@Table(uniqueConstraints={@UniqueConstraint(columnNames={"name", "department"})}) +public class Project { + + int id; + /** + * @return The object's immutable database id + */ + @Id + public int getId() { + return id; + } + protected void setId(int id) { + this.id = id; + } + + String name; + @Column(nullable=false) + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + int department; + @Column(nullable=false) + public int getDepartment() { + return department; + } + public void setDepartment(int department) { + this.department = department; + } + + public Project() {} + public Project(String name, int department) { + setName(name); + setDepartment(department); + } +} \ No newline at end of file diff --git a/ws/eclipse/HibIntro/src/main/java/hibintro/v6/hibernate.cfg.xml b/ws/eclipse/HibIntro/src/main/java/hibintro/v6/hibernate.cfg.xml new file mode 100644 index 0000000000000000000000000000000000000000..20cc77c28bc2046f51bb29206235497148475c90 --- /dev/null +++ b/ws/eclipse/HibIntro/src/main/java/hibintro/v6/hibernate.cfg.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" + "http://hibernate.sourceforge.net/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">true</property> + <property name="hibernate.format_sql">true</property> + <property name="hibernate.hbm2ddl.auto">update</property> + + <mapping class="hibintro.v6.Project"/> + </session-factory> +</hibernate-configuration>