|
An identifying relationship (or "compound identity relationship" in JDO) is a relationship between two
objects of two classes in which the child object must coexist with the parent object and where the primary
key of the child includes the PersistenceCapable object of the parent. So effectively the key aspect of this
type of relationship is that the primary key of one of the classes includes a PersistenceCapable field
(hence why is is referred to as
Compound Identity
). This type of relation is available in the following
forms
Lets take the same classes as we have in the 1-1 Relationships.
In the 1-1 relationships guide we note that in the datastore representation of the
User
and
Account
the
ACCOUNT
table has a primary key as well as a foreign-key to
USER
.
In our example here we want to just have a primary key that is also a foreign-key to
USER
.
To do this we need to modify the classes slightly and add primary-key fields and use
"application-identity".
In addition we need to define primary key classes for our
User
and
Account
classes
public class User
{
long id;
... (remainder of User class)
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
public String toString()
{
return "" + id;
}
public int hashCode()
{
return (int)id;
}
public boolean equals(Object other)
{
if (other != null && (other instanceof PK))
{
PK otherPK = (PK)other;
return otherPK.id == this.id;
}
return false;
}
}
}
public class Account
{
User user;
... (remainder of Account class)
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public User.PK user; // Use same name as the real field above
public PK()
{
}
public PK(String s)
{
StringTokenizer token = new StringTokenizer(s,"::");
this.user = new User.PK(token.nextToken());
}
public String toString()
{
return "" + this.user.toString();
}
public int hashCode()
{
return user.hashCode();
}
public boolean equals(Object other)
{
if (other != null && (other instanceof PK))
{
PK otherPK = (PK)other;
return this.user.equals(otherPK.user);
}
return false;
}
}
}
To achieve what we want with the datastore schema we define the MetaData like this
<package name="mydomain">
<class name="User" identity-type="application" objectid-class="User$PK">
<field name="id" primary-key="true"/>
<field name="login" persistence-modifier="persistent">
<column length="20" jdbc-type="VARCHAR"/>
</field>
</class>
<class name="Account" identity-type="application" objectid-class="Account$PK">
<field name="user" persistence-modifier="persistent" primary-key="true">
<column name="USER_ID"/>
</field>
<field name="firstName" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="secondName" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
</class>
</package>
So now we have the following datastore schema
Things to note :-
-
You must use "application-identity" in both parent and child classes
-
In the child Primary Key class, you must have a field with the same name as the relationship in the
child class, and the field in the child Primary Key class must be the same type as the Primary Key
class of the parent
-
See also the general instructions for Primary Key classes
-
You can only have one "Account" object linked to a particular "User" object since the FK to the "User"
is now the primary key of "Account". To remove this restriction you could also add a "long id" to
"Account" and make the "Account.PK" a composite primary-key
Lets take the same classes as we have in the 1-N Relationships
(FK). In the 1-N relationships guide we note that in the datastore representation of the
Account
and
Address
classes the
ADDRESS
table has a primary key as well as a
foreign-key to
ACCOUNT
. In our example here we want to have the primary-key to
ACCOUNT
to
include
the foreign-key. To do this we need to modify the classes slightly, adding primary-key
fields to both classes, and use "application-identity" for both.
In addition we need to define primary key classes for our
Account
and
Address
classes
public class Account
{
long id; // PK field
Set addresses = new HashSet();
... (remainder of Account class)
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
public String toString()
{
return "" + id;
}
public int hashCode()
{
return (int)id;
}
public boolean equals(Object other)
{
if (other != null && (other instanceof PK))
{
PK otherPK = (PK)other;
return otherPK.id == this.id;
}
return false;
}
}
}
public class Address
{
long id;
Account account;
.. (remainder of Address class)
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id; // Same name as real field above
public Account.PK account; // Same name as the real field above
public PK()
{
}
public PK(String s)
{
StringTokenizer token = new StringTokenizer(s,"::");
this.id = Long.valueOf(token.nextToken()).longValue();
this.account = new Account.PK(token.nextToken());
}
public String toString()
{
return "" + id + "::" + this.account.toString();
}
public int hashCode()
{
return (int)id ^ account.hashCode();
}
public boolean equals(Object other)
{
if (other != null && (other instanceof PK))
{
PK otherPK = (PK)other;
return otherPK.id == this.id && this.account.equals(otherPK.account);
}
return false;
}
}
}
To achieve what we want with the datastore schema we define the MetaData like this
<package name="mydomain">
<class name="Account" identity-type="application" objectid-class="Account$PK">
<field name="id" primary-key="true"/>
<field name="firstName" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="secondName" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent" mapped-by="account">
<collection element-type="Address"/>
</field>
</class>
<class name="Address" identity-type="application" objectid-class="Address$PK">
<field name="id" primary-key="true"/>
<field name="account" persistence-modifier="persistent" primary-key="true">
<column name="ACCOUNT_ID"/>
</field>
<field name="city" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="street" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
</class>
</package>
So now we have the following datastore schema
Things to note :-
-
You must use "application-identity" in both parent and child classes
-
In the child Primary Key class, you must have a field with the same name as the relationship in the
child class, and the field in the child Primary Key class must be the same type as the Primary Key
class of the parent
-
See also the general instructions for Primary Key classes
-
If we had omitted the "id" field from "Address" it would have only been possible to have one "Address"
in the "Account" "addresses" collection due to PK constraints. For that reason we have the "id" field
too.
Lets take the same classes as we have in the 1-N Relationships
(FK). In this guide we note that in the datastore representation of the
Account
and
Address
classes the
ADDRESS
table has a primary key as well as a foreign-key to
ACCOUNT
. In our example here we want to have the primary-key to
ACCOUNT
to
include
the foreign-key. To do this we need to modify the classes slightly, adding primary-key
fields to both classes, and use "application-identity" for both.
In addition we need to define primary key classes for our
Account
and
Address
classes
public class Account
{
long id; // PK field
Set addresses = new HashSet();
... (remainder of Account class)
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public long id;
public PK()
{
}
public PK(String s)
{
this.id = Long.valueOf(s).longValue();
}
public String toString()
{
return "" + id;
}
public int hashCode()
{
return (int)id;
}
public boolean equals(Object other)
{
if (other != null && (other instanceof PK))
{
PK otherPK = (PK)other;
return otherPK.id == this.id;
}
return false;
}
}
}
public class Address
{
String alias;
Account account;
.. (remainder of Address class)
/**
* Inner class representing Primary Key
*/
public static class PK implements Serializable
{
public String alias; // Same name as real field above
public Account.PK account; // Same name as the real field above
public PK()
{
}
public PK(String s)
{
StringTokenizer token = new StringTokenizer(s,"::");
this.alias = Long.valueOf(token.nextToken()).longValue();
this.account = new Account.PK(token.nextToken());
}
public String toString()
{
return alias + "::" + this.account.toString();
}
public int hashCode()
{
return alias.hashCode() ^ account.hashCode();
}
public boolean equals(Object other)
{
if (other != null && (other instanceof PK))
{
PK otherPK = (PK)other;
return otherPK.alias.equals(this.alias) && this.account.equals(otherPK.account);
}
return false;
}
}
}
To achieve what we want with the datastore schema we define the MetaData like this
<package name="com.mydomain">
<class name="Account" objectid-class="Account$PK">
<field name="id" primary-key="true"/>
<field name="firstname" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="lastname" persistence-modifier="persistent">
<column length="100" jdbc-type="VARCHAR"/>
</field>
<field name="addresses" persistence-modifier="persistent" mapped-by="account">
<map key-type="java.lang.String" value-type="com.mydomain.Address"/>
<key mapped-by="alias"/>
</field>
</class>
<class name="Address" objectid-class="Address$PK>
<field name="account" persistence-modifier="persistent" primary-key="true"/>
<field name="alias" null-value="exception" primary-key="true">
<column name="KEY" length="20" jdbc-type="VARCHAR"/>
</field>
<field name="city" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
<field name="street" persistence-modifier="persistent">
<column length="50" jdbc-type="VARCHAR"/>
</field>
</class>
</package>
So now we have the following datastore schema
Things to note :-
-
You must use "application-identity" in both parent and child classes
-
In the child Primary Key class, you must have a field with the same name as the relationship in the
child class, and the field in the child Primary Key class must be the same type as the Primary Key
class of the parent
-
See also the general instructions for Primary Key classes
-
If we had omitted the "alias" field from "Address" it would have only been possible to have one
"Address" in the "Account" "addresses" collection due to PK constraints. For that reason we have the
"alias" field too as part of the PK.
|
|