LDAP : Relationship Mapping by Hierarchy

As LDAP is a hierarchical data store it is possible to model relationships between LDAP entries using hierarchies. For example organisational structures like departments and their employees are often modeled hierarchical in LDAP. It is possible to map 1-1 and N-1/1-N relationships using LDAP hierarchies.

The main challenge with hierarchical mapping is that the distinguished name (DN) of children depends on the DN of their parent. Therefore each child class needs a reference to the parent class. The parent class metadata defines a (fixed) LDAP DN that is used as container for all objects of the parent type. The child class metadata contains a dynamic part in its DN definition. This dynamic part contains the name of the field holding the reference to the parent object, the name is surrounded by curly braces. This dynamic DN is the indicator for DataNucleus to use hierarchical mapping. The reference field itself won't be persisted as attribute because it is used as dynamic parameter. If you query for child objects DataNucleus starts a larger LDAP search to find the objects (the container DN of the parent class as search base and subtree scope).

Note: Child objects are automatically dependent. If you delete the parent object all child objects are automatically deleted. If you null out the child object reference in the parent object or if you remove the child object from the parents collection, the child object is automatically deleted.

N-1 Unidirectional

This kind of mapping could be used if your LDAP tree has a huge number of child objects and you only work with these child objects.

We use the following example LDAP tree and Java classes:

dc=example,dc=com                                       public class Department {
|                                                           String name;
|-- ou=Sales                                            }
|   |-- cn=Bugs Bunny                                   
|   |-- cn=Daffy Duck                                   public class Employee {
|   |-- ...                                                 String firstName;
|                                                           String lastName;
|-- ou=Engineering                                          String fullName;
|   |-- cn=Speedy Gonzales                                  Department department;
|   |-- ...                                             }
|                                                       
|-- ...                                                 

In the LDAP tree we have departments (Sales and Engineering) and each department holds some associated employees. In our Java classes each Employee object knows its Department but not vice-versa.

The JDO metadata looks like this:

<jdo>
    <package name="com.example">
        <class name="Department" table="dc=example,dc=com" schema="top,organizationalUnit">
            <field name="name" primary-key="true" column="ou" />
        </class>

        <class name="Employee" table="{department}" schema="top,person,organizationalPerson,inetOrgPerson">
            <field name="fullName" primary-key="true column="cn" />
            <field name="firstName" column="givenName" />
            <field name="lastName" column="sn" />
            <field name="department"/>
        </class>
    </package>
</jdo>

The Department objects are persisted directly under dc=example,dc=com. The Employee class has a dynamic DN definition {department}. So the DN of the Department instance is used as container for Employee objects.

N-1 (1-N) Bidirectional

If you need a reference from the parent object to the child objects you need to define a bidirectional relationship.

The example LDAP tree and Java classes looks like this:

dc=example,dc=com                                       public class Department {
|                                                           String name;
|-- ou=Sales                                                Set<Employee> employees;
|   |-- cn=Bugs Bunny                                   }
|   |-- cn=Daffy Duck                                   
|   |-- ...                                             public class Employee {
|                                                           String firstName;
|-- ou=Engineering                                          String lastName;
|   |-- cn=Speedy Gonzales                                  String fullName;
|   |-- ...                                                 Department department;
|                                                       }
|-- ...                                                 

Now the Department class has a Collection containing references to its Employees.

The JDO metadata looks like this:

<jdo>
    <package name="com.example">
        <class name="Department" table="dc=example,dc=com" schema="top,organizationalUnit">
            <field name="name" primary-key="true" column="ou" />
            <field name="employees" mapped-by="department"/>
        </class>

        <class name="Employee" table="{department}" schema="top,person,organizationalPerson,inetOrgPerson">
            <field name="fullName" primary-key="true column="cn" />
            <field name="firstName" column="givenName" />
            <field name="lastName" column="sn" />
            <field name="department"/>
        </class>
    </package>
</jdo>

We added a new employees field to the Department class that is mapped-by the department field of the Employee class.

Please note: When loading the parent object all child object are loaded immediately. For a large number of child entries this may lead to performance and/or memory problems.

1-1 Unidirectional

1-1 unidirectional mapping is very similar to N-1 unidirectional mapping.

We use the following example LDAP tree and Java classes:

dc=example,dc=com                                       public class Person {
|                                                           String firstName;
|-- ou=People                                               String lastName;
|   |-- cn=Bugs Bunny                                       String fullName;
|   |   |-- uid=bbunny                                  }
|   |                                                   
|   |-- cn=Daffy Duck                                   public class Account {
|   |   |-- uid=dduck                                       String uid;
|   |                                                       String password;
|   |-- ...                                                 Person person;
                                                        }

In the LDAP tree we have persons and each person has one account. Each Account object knows to which Person it belongs to, but not vice-versa.

The JDO metadata looks like this:

<jdo>
    <package name="com.example">
        <class name="Person" table="ou=People,dc=example,dc=com" schema="top,person,organizationalPerson,inetOrgPerson">
            <field name="fullName" primary-key="true column="cn" />
            <field name="firstName" column="givenName" />
            <field name="lastName" column="sn" />
        </class>
        
        <class name="Account" table="{person}" schema="top,account,simpleSecurityObject">
            <field name="uid" primary-key="true column="uid" />
            <field name="password" column="userPasword" />
            <field name="person" />
        </class>
    </package>
</jdo>

The Person objects are persisted directly under ou=People,dc=example,dc=com. The Account class has a dynamic DN definition {person}. So the DN of the Person instance is used as container for the Account object.

1-1 Bidirectional

If you need a reference from the parent class to the child class you need to define a bidirectional relationship.

The example LDAP tree and Java classes looks like this:

dc=example,dc=com                                       public class Person {
|                                                           String firstName;
|-- ou=People                                               String lastName;
|   |                                                       String fullName;
|   |-- cn=Bugs Bunny                                       Account account;
|   |   |-- uid=bbunny                                  }
|   |                                                   
|   |-- cn=Daffy Duck                                   public class Account {
|   |   |-- uid=dduck                                       String uid;
|   |                                                       String password;
|   |-- ...                                                 Person person;
                                                        }

Now the Person class has a reference to its Account.

The JDO metadata looks like this:

<jdo>
    <package name="com.example">
        <class name="Person" table="ou=People,dc=example,dc=com" schema="top,person,organizationalPerson,inetOrgPerson">
            <field name="fullName" primary-key="true column="cn" />
            <field name="firstName" column="givenName" />
            <field name="lastName" column="sn" />
            <field name="account" mapped-by="person" />
        </class>
        
        <class name="Account" table="{person}" schema="top,account,simpleSecurityObject">
            <field name="uid" primary-key="true column="uid" />
            <field name="password" column="userPasword" />
            <field name="person" />
        </class>
    </package>
</jdo>

We added a new account field to the Person class that is mapped-by the person field of the Account class.