Skip to content
Snippets Groups Projects
Commit 5ad61b2a authored by Goik Martin's avatar Goik Martin
Browse files

surrogate keys, composed business keys

parent e48241a8
No related branches found
No related tags found
No related merge requests found
......@@ -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>
......
......@@ -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
......
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
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
<?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>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment