31.3.4. Data-layer rules

The coding guidelines for this package has been slightly modified from the the general coding guidelines. Here is a short list with the changes.

Class and interface names

Class names should follow the general guidelines, but should in most cases end with Data.

public class SampleData
   extends CommonData
   implements DiskConsumableData
{
   ...
}
Attributes and methods order

Inside a class, attributes and methods should be organised in related groups, ie. the private attribute is together with the getter and setter methods that uses that attribute. This makes it easy to re-use existing code with copy-and-paste operations.

public static int long MAX_ADDRESS_LENGTH = 255;
private String address;
/**
   @hibernate.property column="`address`" type="string" length="255" not-null="false"
*/
public String getAddress()
{
   return address;
}
public void setAddress(String address)
{
   this.address = address;
}

private int row;
/**
   @hibernate.property column="`row`" type="int"
*/
public int getRow()
{
   return row;
}
public void setRow(int row)
{
   this.row = row;
}				
Extend/implement the basic classes and interfaces

Each data-class must inherit from one of the already existing abstract base classes. They contain code that is common to all classes, for example implementations of the equals() and hashCode() methods or how to link with the owner of an item. For information about which classes/interfaces that can be used see Section 29.2.1, “Basic classes and interfaces”.

Define a public no-argument constructor

Always define a public no-argument constructor. No other constructors are needed. If we want to use other persistence mechanisms or serializability in the future this type of constructor is probably the most compatible. The constructor should be empty and not contain any code. Do not initialise properties or create new objects for internal use. Most of the time the object is loaded by Hibernate and Hibernate will ensure that it is properly initialised by calling all setter methods.

For example, a many-to-many relation usually has a Set or a Map to hold the links to the other objects. Do not create a new HashSet or HashMap in the constructor. Wait until the getter method is called and only create a new object if Hibernate hasn't already called the setter method with it's own object. See the code example below. There is also more information about this in Many-to-many and one-to-many mappings.


// From GroupData.java
public GroupData() 
{}

private Set<UserData> users;
public Set<UserData> getUsers()
{
   if (users == null) users = new HashSet<UserData>();
   return users;
}

See also:

Object identity

We use database identity to compare objects, ie. two objects are considered equal if they are of the same class and have the same id, thus representing the same database row. All this stuff is implemented by the BasicData class. Therefore it is required that all classes are subclasses of this class. It is recommended that the equals() or hashCode() methods are not overridden by any of the subclasses. We would have liked to make them final, but then the proxy feature of Hibernate would not work.

[Warning] Avoid mixing saved and unsaved objects

The approch used for object identity may give us a problem if we mix objects which hasn't been saved to the database, with objects loaded from the database. Our recommendation is to avoid that, and save any objects to the database before adding them to sets, maps or any other structure that uses the equals() and hashCode() methods.

To be more specific, the problem arises because the following two rules for hash codes are contradicting when the hash code is based on the database id:

  1. The hash code of an object mustn't change.

  2. Equal objects must have equal hash code.

For objects in the database, the hash code is based on the id. For new objects, which doesn't have an id yet, we fall back to the system hash code. But, what happens when we save the new object to the database? If nobody has asked for the hash code it is safe to use the id, otherwise we must stick with the system hash code. Now, imagine that we load the same object from the database in another Hibernate session. What will now happen? The loaded object will have it's hash code based on the id but the original object is still using the system hash code, which most likely is not the same as the id. Yet, the equals() method returns true. This is a violation of the contract for the equals method. If these two objects are used in a set it may cause unexpected behaviour. Therefore, do not put new objects in a set, or other collection, that calls the hashCode() method before the object is saved to the database.

See also:

  • "Hibernate in action", chapter 3.4 "Understanding object identity", page 87-90

  • "Hibernate in action", chapter 4.1.4 "The scope of object identity", page 119-121

  • "Hibernate in action", chapter 4.1.6 "Implementing equals() and hashCode(), page 122-126

  • Hibernate reference documentation: 4.3. Implementing equals() and hashCode()

No final methods

No methods should be tagged with the final keyword. This is a requirement to be able to use the proxy feature of Hibernate, which we need for performance reasons.

See also:

Second-level cache

To gain performance we use the second-level cache of Hibernate. It is a transparent feature that doesn't affect the code in any way. The second-level cache is configured in the hibernate.cfg.xml and ehcache.xml configuration files and not in the individual class mapping files. BASE is shipped with a standard configuration, but different deployment scenarios may have to fine-tune the cache settings for that particular hardware/software setup. It is beyond the scope of this document to discuss this issue.

The second-level cache is suitable for objects that are rarely modified but are often needed. For example, we do not expect the user information represented by the UserData object to change very often, but it is displayed all the time as the owner of various items. Before coming up with a good caching strategy we have to answer the following questions:

  1. Should objects of this class be cached at all?

  2. How long timeout should we use?

  3. How many objects should we keep in memory or on disk?

The first question is the most important. Good candidates are classes with few objects that change rarely, but are read often. Also, objects which are linked to by many other objects are good candidates. The UserData class is an example which matches all three requirements. The TagData class is an example which fulfils the first two. The BioMaterialEventData class is on the other hand a bad cache candidate, since it is not linked to any other object than a BioMaterialData object.

The answer to the second question depends on how often an object is modified. For most objects this time is probably several days or months, but we would not gain much by keeping objects in the cache for so long. Suddenly, the information has changed and we won't risk that old information is kept that long. We have set the timeout to 1 hour for all classes so far, and we don't recommend a longer timeout. The only exception is for immutable objects, that cannot be changed at all, which may have an infinite timeout.

The answer to the third question depends a lot on the hardware (available memory). With lots of memory we can afford to cache more objects. Caching to disk is not really necessary if the database is on the same machine as the web server, but if it is on another machine we have to consider the network delay to connect to the database versus the disk access time. The default configuration does not use disk cache.

See also:

  • "Hibernate in action", chapter 5.3 "Caching theory and practice", page 175-194.

  • Hibernate reference documentation: 21.2. The Second Level Cache

Proxies

Proxies are also used to gain performance, and they may have some impact on the code. Proxies are created at runtime (by Hibernate) as a subclass of the actual class but are not populated with data until some method of the object is called. The data is loaded from the database the first time a method other than getId() is called. Thus, we can avoid loading data that is not needed at a particular time.

There can be a problem with using the instanceof operator with proxies and the table-per-class-hierarchy mapping. For example, if we have the abstract class Animal and subclasses Cat and Dog. The proxy of an Animal is a runtime generated subclass of Animal, since we do not know if it is a Cat or Dog. So, x instanceof Dog and x instanceof Cat would both return false. If we hadn't used a proxy, at least one of them would always be true.

Proxies are only used when a not-null object is linked with many-to-one or one-to-one from another object. If we ask for a specific object by id, or by a query, we will never get a proxy. Therefore, it only makes sense to enable proxies for classes that can be linked from other classes. One-to-one links on the primary key where null is allowed silently disables the proxy feature, since Hibernate doesn't know if there is an object or not without querying the database.

Proxy vs. cache

The goal of a proxy and the second-level cache are the same: to avoid hitting the database. It is perfectly possible to enable both proxies and the cache for a class. Then we would start with a proxy and as soon as a method is called Hibernate would look in the second-level cache. Only if it is not there it would be loaded from the database. But, do we really need a proxy in the first place? Well, I think it might be better to use only the cache or only proxies. But, this also makes it even more important that the cache is configured correctly so there is a high probability that the object is already in the cache.

If a class has been configured to use the second-level cache, we recommend that proxies are disabled. For child objects in a parent-child relationship proxies should be disabled, since they have no other links to them than from the parent. If a class can be linked as many-to-one from several other classes it makes sense to enable proxies. If we have a long chain of many-to-one relations it may also make sense to enable proxies at some level, even if the second-level cache is used. In that case we only need to create one proxy instead of looking up several objects in the cache. Also, think about how a particular class most commonly will be used in a client application. For example, it is very common to display the name of the owner of an item, but we are probably not interested in displaying quota information for that user. So, it makes sense to put users in the second-level cache and use proxies for quota information.

[Warning] Batchable classes and stateless sessions

Hibernate has a stateless session feature. A stateless session has no first-level cache and doesn't use the second-level cache either. This means that if we load an item with a stateless session Hibernate will always traverse many-to-one and one-to-one associations and load those objects as well, unless they are configured to use proxies.

BASE use stateless sessions for loading BatchableData items (reporters, raw data and features) since they are many and we want to use as little memory as possible. Here it is required that proxies are enabled for all items that are linked from any of the batchable items, ie. RawBioAssay, ReporterType, ArrayDesignBlock, etc. If we don't do this Hibernate will generate multiple additional select statements for the same parent item which will affect performance in a bad way.

On the other hand, the proxies created from a stateless session cannot later be initialised. We have to get the ID from the proxy and the load the object using the regular session. But this can also results in lots of additional select statements so if it is known before that we need some information it is recommended that a FETCH JOIN query is used so that we get fully initialized objects instead of proxies to begin with.

Here is a table which summarises different settings for the second-level cache, proxies, batch fetching and many-to-one links. Batch fetching and many-to-one links are discussed later in this document.

First, decide if the second-level cache should be enabled or not. Then, if proxies should be enabled or not. The table then gives a reasonable setting for the batch size and many-to-one mappings. NOTE! The many-to-one mappings are the links from other classes to this one, not links from this class.

The settings in this table are not absolute rules. In some cases there might be a good reason for another combination. Please, write a comment about why the recommendations were not followed.

Table 31.1. Choosing cache and proxy settings

Global configuration Class mapping Many-to-one mapping
Cache Proxy Batch-size Outer-join
no no* yes true
yes no* no false
no yes yes false
yes yes no false

* = Do not use this setting for classes which are many-to-one linked from a batchable class.

See also:

  • "Hibernate in action", chapter 4.4.6 "Selecting a fetching strategy in mappings", page 146-147

  • "Hibernate in action", chapter 6.4.1 "Polymorphic many-to-one associations", page 234-236

  • Hibernate reference documentation: 21.1.3. Single-ended association proxies

Hibernate mappings

We use Javadoc tags to specify the database mapping needed by Hibernate. The tags are processed by XDoclet at build time which generates the XML-based Hibernate mapping files.

[Note] XDoclet doesn't support all mappings

The XDoclet that we use was developed to generate mapping files for Hibernate 2.x. Since then, Hibernate has released several 3.x versions, and the mapping file structure has changed. Some changes can be handled by generating a corresponding 2.x mapping and then converting it to a 3.x mapping at build time using simple search-and-replace operations. One such case is to update the DTD reference to the 3.0 version instead of the 2.0 version. Other changes can't use this approach. Instead we have to provide extra mappings inside an XML files. This is also needed if we need to use some of the new 3.x features that has no 2.x counterpart.

Class mapping
/**
   This class holds information about any data...
   @author Your name
   @since 3.0
   @hibernate.class table="`Anys`" lazy="false" batch-size="10"
*/
public class AnyData
   extends CommonData
{
   // Rest of class code...
}

The class declaration must contain a @hibernate.class Javadoc entry where Hibernate can find the name of the table where items of this type are stored. The table name should generally be the same as the class name, without the ending Data and in a plural form. For example UserDataUsers. The back-ticks (`) around the table name tells Hibernate to enclose the name in whatever the actual database manager uses for such things (back-ticks in MySQL, quotes for an ANSI-compatible database).

[Important] Always set the lazy attribute

The lazy attribute enables/disables proxies for the class. Do not forget to specify this attribute since the default value is true. If proxies are enabled, it may also make sense to specify a batch-size attribute. Then Hibernate will load the specified number of items in each SELECT statement instead of loading them one by one. It may also make sense to specify a batch size when proxies are disabled, but then it would probably be even better to use eager fetching by setting outer-join="true" (see many-to-one mapping).

Classes that are linked with a many-to-one association from a batchable class must specify lazy="true". Otherwise the stateless session feature of Hibernate may result in a large number of SELECT:s for the same item, or even circular loops if two or more items references each other.

[Important] Remember to enable the second-level cache

Do not forget to configure settings for the second-level cache if this should be enabled. This is done in the hibernate.cfg.xml and ehcache.xml.

See also:

  • "Hibernate in action", chapter 3.3 "Defining the mapping metadata", page 75-87

  • Hibernate reference documentation: 5.1.3. Entity

Property mappings

Properties such as strings, integers, dates, etc. are mapped with the @hibernate.property Javadoc tag. The main purpose is to define the database column name. The column names should generally be the same as the get/set method name without the get/set prefix, and with upper-case letters converted to lower-case and an underscore inserted. Examples:

  • getAddress()column="`address`"

  • getLoginComment()column="`login_comment`"

The back-ticks (`) around the column name tells Hibernate to enclose the name in whatever the actual database manager uses for such things (back-ticks in MySQL, quotes for an ANSI-compatible database).

String properties

public static int long MAX_STRINGPROPERTY_LENGTH = 255;
private String stringProperty;
/**
   Get the string property.
   @hibernate.property column="`string_property`" type="string" 
      length="255" not-null="true"
*/
public String getStringProperty()
{
   return stringProperty;
}
public void setStringProperty(String stringProperty)
{
   this.stringProperty = stringProperty;
}

Do not use a greater value than 255 for the length attribute. Some databases has that as the maximum length for character columns (ie. MySQL). If you need to store longer texts use type="text" instead. You can then skip the length attribute. Most databases will allow up to 65535 characters or more in a text field. Do not forget to specify the not-null attribute.

You should also define a public constant MAX_STRINGPROPERTY_LENGTH containing the maximum allowed length of the string.

Numerical properties

private int intProperty;
/**
   Get the int property.
   @hibernate.property column="`int_property`" type="int" not-null="true"
*/
public int getIntProperty()
{
   return intProperty;
}
public void setIntProperty(int intProperty)
{
   this.intProperty = intProperty;
}

It is also possible to use Integer, Long or Float objects instead of int, long and float. We have only used it if null values have some meaning.

Boolean properties

private boolean booleanProperty;
/**
   Get the boolean property.
   @hibernate.property column="`boolean_property`" 
      type="boolean" not-null="true"
*/
public boolean isBooleanProperty()
{
   return booleanProperty;
}
public void setBooleanProperty(boolean booleanProperty)
{
   this.booleanProperty = booleanProperty;
}

It is also possible to use a Boolean object instead of boolean. It is only required if you absolutely need null values to handle special cases.

Date values

private Date dateProperty;
/**
   Get the date property. Null is allowed.
   @hibernate.property column="`date_property`" type="date" not-null="false"
*/
public Date getDateProperty()
{
   return dateProperty;
}
public void setDateProperty(Date dateProperty)
{
   this.dateProperty = dateProperty;
}

Hibernate defines several other date and time types. We have decided to use the type="date" type when we are only interested in the date and the type="timestamp" when we are interested in both the date and time.

See also:

  • "Hibernate in action", chapter 3.3.2 "Basic property and class mappings", page 78-84

  • "Hibernate in action", chapter 6.1.1 "Built-in mapping types", page 198-200

  • Hibernate reference documentation: 5.1.4. property

  • Hibernate reference documentation: 5.2.2. Basic value types

Many-to-one mappings
private OtherData other;
/**
   Get the other object.
   @hibernate.many-to-one column="`other_id`" not-null="true" outer-join="false"
*/
public OtherData getOther()
{
   return other;
}
public void setOther(OtherData other)
{
   this.other = other;
}

We create a many-to-one mapping with the @hibernate.many-to-one tag. The most important attribute is the column attribute which specifies the name of the database column to use for the id of the other item. The back-ticks (`) around the column name tells Hibernate to enclose the name in whatever the actual database manager uses for such things (back-ticks in MySQL, quotes for an ANSI-compatible database).

We also recommend that the not-null attribute is specified. Hibernate will not check for null values, but it will generate table columns that allow or disallow null values. See it as en extra safety feature while debugging. It is also used to determine if Hibernate uses LEFT JOIN or INNER JOIN in SQL statements.

The outer-join attribute is important and affects how the cache and proxies are used. It can take three values: auto, true or false. If the value is true Hibernate will always use a join to load the linked object in a single select statement, overriding the cache and proxy settings. This value should only be used if the class being linked has disabled both proxies and the second-level cache, or if it is a link between a child and parent in a parent-child relationship. A false value is best when we expect the associated object to be in the second-level cache or proxying is enabled. This is probably the most common case. The auto setting uses a join if proxying is disabled otherwise it uses a proxy. Since we always know if proxying is enabled or not, this setting is not very useful. See Table 31.1, “Choosing cache and proxy settings” for the recommended settings.

See also:

  • "Hibernate in action", chapter 3.7 "Introducing associations", page 105-112

  • "Hibernate in action", chapter 4.4.5-4.4.6 "Fetching strategies", page 143-151

  • "Hibernate in action", chapter 6.4.1 "Polymorphic many-to-one associations", page 234-236

  • Hibernate reference documentation: 5.1.7. Mapping one to one and many to one associations

Many-to-many and one-to-many mappings

There are many variants of mapping many-to-many or one-to-many, and it is not possible to give examples of all of them. In the code these mappings are represented by Set:s, Map:s, List:s, or some other collection object. The most important thing to remember is that (in our application) the collections are only used to maintain the links between objects. They are (in most cases) not used for returning objects to client applications, as is the case with the many-to-one mapping.

For example, if we want to find all members of a group we do not use the GroupData.getUsers() method, instead we will execute a database query to retrieve them. The reason for this design is that the logged in user may not have access to all users and we must add a permission checking filter before returning the user objects to the client application. Using a query will also allow client applications to specify sorting and filtering options for the users that are returned.


// RoleData.java
private Set<UserData> users;
/**
   Many-to-many from roles to users.
   @hibernate.set table="`UserRoles`" lazy="true"
   @hibernate.collection-key column="`role_id`"
   @hibernate.collection-many-to-many column="`user_id`" 
      class="net.sf.basedb.core.data.UserData"
*/
public Set<UserData> getUsers()
{
   if (users == null) users = new HashSet<UserData>();
   return users;
}
void setUsers(Set&lt;UserData&gt; users)
{
   this.users = users;
}

As you can see this mapping is a lot more complicated than what we have seen before. The most important thing is the lazy attribute. It tells Hibernate to delay the loading of the related objects until the set is accessed. If the value is false or missing, Hibernate will load all objects immediately. There is almost never a good reason to specify something other than lazy="true".

Another important thing to remember is that the getter method must always return the same object that Hibernate passed to the setter method. Otherwise, Hibernate will not be able to detect changes made to the collection and as a result will have to delete and then recreate all links. To ensure that the collection object is not changed we have made the setUsers() method package private, and the getUsers() will create a new HashSet for us only if Hibernate didn't pass one in the first place.

Let's also have a look at the reverse mapping:


// UserData.java
private Set<RoleData> roles;
/**
   Many-to-many from users to roles
   @hibernate.set table="`UserRoles`" lazy="true"
   @hibernate.collection-key column="`user_id`"
   @hibernate.collection-many-to-many column="`role_id`" 
      class="net.sf.basedb.core.data.RoleData"
*/
Set<RoleData> getRoles()
{
   return roles;
}
void setRoles(Set<RoleData> roles)
{
   this.roles = roles;
}

The only real difference here is that both the setter and the getter methods are package private. This is a safety measure because Hibernate will get confused if we modify both ends. Thus, we are forced to always add/remove users to/from the set in the RoleData object. The methods in the UserData class are never used by us. Note that we do not have to check for null and create a new set since Hibernate will handle null values as an empty set.

So, why do we need the second collection at all? It is never accessed except by Hibernate, and since it is lazy it will always be "empty". The answer is that we want to use the relation in HQL statements. For example:

SELECT ... FROM RoleData rle WHERE rle.users ...
SELECT ... FROM UserData usr WHERE usr.roles ...

Without the second mapping, it would not have been possible to execute the second HQL statement. The inverse mapping is also important in parent-child relationships, where it is used to cascade delete the children if a parent is deleted (see below).

[Warning] Do not use the inverse="true" setting

Hibernate defines an inverse="true" setting that can be used with the @hibernate.set tag. If specified, Hibernate will ignore changes made to that collection. However, there is one problem with specifying this attribute. Hibernate doesn't delete entries in the association table, leading to foreign key violations if we try to delete a user. The only solutions are to skip the inverse="true" attribute or to manually delete the object from all collections on the non-inverse end. The first alternative is the most efficient since it only requires a single SQL statement. The second alternative must first load all associated objects and then issue a single delete statement for each association.

In the "Hibernate in action" book they have a very different design where they recommend that changes are made in both collections. We don't have to do this since we are only interested in maintaining the links, which is always done in one of the collections.

Parent-child relationships

When one or more objects are tightly linked to some other object we talk about a parent-child relationship. This kind of relationship becomes important when we are about to delete a parent object. The children cannot exist without the parent so they must also be deleted. Luckily, Hibernate can do this for us if we specify a cascade="delete" option for the link. This example is a one-to-many link between client and help texts.


// ClientData.java
private Set<HelpData> helpTexts;
/**
   This is the inverse end.
   @see HelpData#getClient()
   @hibernate.set lazy="true" inverse="true" cascade="delete"
   @hibernate.collection-key column="`client_id`"
   @hibernate.collection-one-to-many class="net.sf.basedb.core.data.HelpData"
*/
Set<HelpData> getHelpTexts()
{
   return helpTexts;
}

void setHelpTexts(Set<HelpData> helpTexts)
{
   this.helpTexts = helpTexts;
}

// HelpData.java
private ClientData client;
/**
   Get the client for this help text.
   @hibernate.many-to-one column="`client_id`" not-null="true" 
      update="false" outer-join="false" unique-key="uniquehelp"
*/
public ClientData getClient()
{
   return client;
}
public void setClient(ClientData client)
{
   this.client = client;
}

This show both sides of the one-to-many mapping between parent and children. As you can see the @hibernate.set doesn't specify a table, since it is given by the class attribute of the @hibernate.collection-one-to-many tag.

In a one-to-many mapping, it is always the "one" side that handles the link so the "many" side should always be mapped with inverse="true".

Maps

Another type of many-to-many mapping uses a Map for the collection. This kind of mapping is needed when the association between two objects needs additional data to be kept as part of the association. For example, the permission (stored as an integer value) given to users that are members of a project. Note that you should use a Set for mapping the inverse end.


// ProjectData.java
private Map<UserData, Integer> users;
/**
   Many-to-many mapping between projects and users including permission values.
   @hibernate.map table="`UserProjects`" lazy="true"
   @hibernate.collection-key column="`project_id`"
   @hibernate.index-many-to-many column="`user_id`" 
      class="net.sf.basedb.core.data.UserData"
   @hibernate.collection-element column="`permission`" type="int" not-null="true"
*/
public Map<UserData, Integer> getUsers()
{
   if (users == null) users = new HashMap<UserData, Integer>();
   return users;
}
void setUsers(Map<UserData, Integer> users)
{
   this.users = users;
}

// UserData.java
private Set<ProjectData> projects;
/**
   This is the inverse end.
   @see ProjectData#getUsers()
   @hibernate.set table="`UserProjects`" lazy="true"
   @hibernate.collection-key column="`user_id`"
   @hibernate.collection-many-to-many column="`project_id`"
      class="net.sf.basedb.core.data.ProjectData"
*/
Set<ProjectData> getProjects()
{
   return projects;
}
void setProjects(Set<ProjectData> projects)
{
   this.projects = projects;
}

See also:

  • "Hibernate in action", chapter 3.7 "Introducing associations", page 105-112

  • "Hibernate in action", chapter 6.2 "Mapping collections of value types", page 211-220

  • "Hibernate in action", chapter 6.3.2 "Many-to-many associations", page 225-233

  • Hibernate reference documentation: Chapter 7. Collection Mapping

  • Hibernate reference documentation: Chapter 24. Example: Parent/Child

One-to-one mappings

A one-to-one mapping can come in two different forms, depending on if both objects should have the same id or not. We start with the case were the objects can have different id:s and the link is done with an extra column in one of the tables. The example is from the mapping between physical bioassays and arrayslides.

// PhysicalBioAssayData.java
private ArraySlideData arrayslide;
/**
   Get the array slide
   @hibernate.many-to-one column="`arrayslide_id`" not-null="false" 
      unique="true" outer-join="false"
*/
public ArraySlideData getArraySlide()
{
   return arrayslide;
}
public void setArraySlide(ArraySlideData arrayslide)
{
   this.arrayslide = arrayslide;
}

// ArraySlideData.java
private PhysicalBioAssayData bioassay;
/**
   Get the bioassay.
   @hibernate.one-to-one property-ref="arraySlide"
*/
public PhysicalBioAssayData getPhysicalBioAssay()
{
   return hybridization;
}
public void setPhysicalBioAssay(PhysicalBioAssayData bioassay)
{
   this.bioassay = bioassay;
}					

As you can see, we use the @hibernate.many-to-one mapping with unique="true" for the bioassay side. This will force the database to only allow the same array slide to be linked once. Also note that since, not-null="false", null values are allowed and it doesn't matter which end of the relation that is inserted first into the database.

For the array slide end we use a @hibernate.one-to-one mapping and specify the name of the property on the other end that we are linking to. One important thing to remember is to keep both ends synchronized. This should usually be done at the core layer and not in the data layer. Doing it in the data layer may effectively disable lazy loading if the synchronization code causes proxy initialization.

The second form of a one-to-one mapping is used when both objects must have the same id (primary key). The example is from the mapping between users and passwords.

// UserData.java
/**
   @hibernate.id column="`id`" generator-class="foreign"
   @hibernate.generator-param name="property" value="password"
*/
public int getId()
{
   return super.getId();
}
private PasswordData password;
/**
   Get the password.
   @hibernate.one-to-one class="net.sf.basedb.core.data.PasswordData"
      cascade="all" outer-join="false" constrained="true"
*/
public PasswordData getPassword()
{
   if (password == null)
   {
      password = new PasswordData();
      password.setUser(this);
   }
   return password;
}
void setPassword(PasswordData user)
{
   this.password = password;
}

// PasswordData.java
private UserData user;
/**
   Get the user.
   @hibernate.one-to-one class="net.sf.basedb.core.data.UserData"
*/
public UserData getUser()
{
   return user;
}
void setUser(UserData user)
{
   this.user = user;
}

In this case, we use the @hibernate.one-to-one mapping in both classes. The constrained="true" tag in UserData tells Hibernate to always insert the password first, and then the user. The makes it possible to use the (auto-generated) id for the password as the id for the user. This is controlled by the mapping for the UserData.getId() method, which uses the foreign id generator. This generator will look at the password property, ie. call getPassword().getId() to find the id for the user. Also note the initialisation code and cascade="all" tag in the UserData.getPassword() method. This is needed to avoid NullPointerException:s and to make sure everything is created and deleted properly.

See also:

Class documentation

The documentation for the class doesn't have to be very lengthy. A single sentence is usually enough. Provide tags for the author, version, last modification date and a reference to the corresponding class in the net.sf.basedb.core package.

/**
   This class holds information about any items.

   @author Your name
   @since 2.0
   @see net.sf.basedb.core.AnyItem
   @base.modified $Date: 2007-08-17 09:18:29 +0200 (Fri, 17 Aug 2007) $
   @hibernate.class table="`Anys`" lazy="false"
*/
public class AnyData
   extends CommonData
{
...
}
Method documentation

Write a short one-sentence description for all public getter methods. You do not have document the parameters or the setter methods, since it would just be a repetition. Methods defined by interfaces are documented in the interface class. You should not have to write any documentation for those methods.

For the inverse end of an association, which has only package private methods, write a notice about this and provide a link to to non-inverse end.


// UserData.java
private String address;
/**
   Get the address for the user.
   @hibernate.property column="`address`" type="string" length="255"
*/
public String getAddress()
{
   return address;
}
public void setAddress(String address)
{
   this.address = address;
}

private Set<GroupData> groups;
/**
   This is the inverse end.
   @see GroupData#getUsers()
   @hibernate.set table="`UserGroups`" lazy="true" inverse="true"
   @hibernate.collection-key column="`user_id`"
   @hibernate.collection-many-to-many column="`group_id`"
      class="net.sf.basedb.core.data.GroupData"
*/
Set<GroupData> getGroups()
{
   return groups;
}
void setGroups(Set<GroupData> groups)
{
   this.groups = groups;
}

Field documentation

Write a short one-sentence description for public static final fields. Private fields does not have to be documented.

/**
   The maximum length of the name of an item that can be
   stored in the database.
   @see #setName(String)
*/
public static final int MAX_NAME_LENGTH = 255;
UML diagram

Groups of related classes should be included in an UML-like diagram to show how they are connected and work together. For example we group together users, groups, roles, etc. into an authentication UML diagram. It is also possible that a single class may appear in more than one diagram. For more information about how to create UML diagrams see Section 30.2, “Create UML diagrams with MagicDraw”.