Relationships

Most of the entities that appear in real applications have relationships with other entities; consider the simple case of a person owning a car. Here we say Person has an owning relationship with the Car, and from the perspective of the person there can be any number of cars that are owned, denoted in UML by [0..*] at the car's end.

Make sure the association ends are public; some tools allow you to specify the visibility of an association end.

In UML, relationships are modeled using associations, and associations themselves have different properties, which will be discussed here.

Let's model another entity, call it Person and give it a few attributes, just make sure you give them one of the platform independent datatypes that can be mapped onto a platform specific datatype (you can find them in the datatype package).

Draw an association between both entities you have just modeled. Set the multiplicity at the end of the car to [0..*] and name the other end 'owner'. If we run AndroMDA over your model, this is what we expect to see.

In this example we have added two attributes to the Person entity.

  • name of type datatype::String
  • birthDate of type datatype::Date

Please note that also for this entity an identifier will be added by default. If you explicitly want to add an identifier you should model the <<Identifier>> stereotype on an attribute. Refer to Entities for more information.

  • Auto-generated source that does not need manual editing
  • Auto-generated source that should be edited manually
  • File that is affected by the modifications applied in this section
  • CarEmbeddable.java
  • Car.java
  • CarDao.java
  • CarDaoBase.java
  • CarDaoImpl.java
  • CarDaoException.java
  • Person.java
  • PersonDao.java
  • PersonDaoBase.java
  • PersonDaoImpl.java
  • PersonDaoException.java

The relationship mapping metadata (annotations) have been generated in the root class of the entity hierarchy. The metadata for the Car entity is within the mapped superclass. The metadata for the Person entity exists in the auto generated Person class. Since both ends of the association are navigable, we have a bi-directional relationship.

  • Person.getCars() : Collection

    A One-To-Many relationship has been defined from the Person entity to the Car entity. This adds the @javax.persistence.OneToMany annotation to the getter method and sets the mappedBy property to 'owner'. The mappedBy property indicates the other end of the association is the owning side of the relationship.

  • Car.getOwner() : Person

    A Many-To-One relationship has been defined from the Car entity to the Person entity. This adds the @javax.persistence.ManyToOne annotation to the getter method getOwner in the Car entity. Since the multiplicity on the Person end of the association is set to 1, the annotation has defined the optional property to false which indicates non-null entries cannot exists in this foreign key column in the relational database table.

    Since the Car entity end is the owning end, the @javax.persistence.JoinColumn annotation has been defined with a name property. This annotation is used to indicate a mapped column for joining an entity association. The name property defines the foreign key column name.

By default AndroMDA will look at the multiplicity to generate a good name for the relationship, a few examples where the multiplicity is greater than one:

  • car: cars
  • property: properties
  • toy: toys
  • animal: animals
  • bus: busses

You can override these names by adding your own names to the association ends, in our example you might set the name of the association end at the side of the person to person, this will emit the following output during generation:

  • Person.getCars() : Collection
  • Car.getPerson() : Person

Cascading

You can set the cascade option on all association ends by modeling the andromda_persistence_cascade_type tagged value on the target association end. The cascadable options are:

  • ALL
  • PERSIST
  • MERGE
  • REMOVE
  • REFRESH

You can set multiple cascade options on the target association end which will define the cascade property of the annotation as array.

  • Auto-generated source that does not need manual editing
  • Auto-generated source that should be edited manually
  • File that is affected by the modifications applied in this section
  • CarEmbeddable.java
  • Car.java
  • CarDao.java
  • CarDaoBase.java
  • CarDaoImpl.java
  • CarDaoException.java
  • Person.java
  • PersonDao.java
  • PersonDaoBase.java
  • PersonDaoImpl.java
  • PersonDaoException.java

If you enable Hibernate extensions by setting the value of the persistenceProviderExtensions namespace property in your andromda.xml to hibernate, you can also model the andromda_hibernate_cascade tagged value on your association ends. This way, you can further customize your cascade solution to use Hibernate specific cascade types such as SAVE_UPDATE and DELETE_ORPHAN. Remember, this tagged value accepts a comma separated list of fully qualified cascade types.

Fetch Type

The fetch type tagged value andromda_persistence_fetch_type can be modeled on all association ends. To set the fetch type on an end, you model this tagged value on the target association end. The following default are the default fetch types for the available relationships:

  • Many-To-One : EAGER
  • One-To-Many : LAZY
  • One-To-One : EAGER
  • Many-To-Many : LAZY

Therefore, you only need to specify the latter tagged value if you wish to change the fetch type property from the default.

  • Auto-generated source that does not need manual editing
  • Auto-generated source that should be edited manually
  • File that is affected by the modifications applied in this section
  • CarEmbeddable.java
  • Car.java
  • CarDao.java
  • CarDaoBase.java
  • CarDaoImpl.java
  • CarDaoException.java
  • Person.java
  • PersonDao.java
  • PersonDaoBase.java
  • PersonDaoImpl.java
  • PersonDaoException.java

Ordering Collection Valued Association

You can order the elements of a collection valued association by modeling the andromda_persistence_orderBy tagged value on the target association end. A few notes to consider:

  • If ASC or DESC is not specified, ASC order is assumed.

  • If the ordering tagged value is modeled but no ordering element is supplied, this renders an empty @javax.persistence.OrderBy annotation which indicates to the container to assume ordering by primary key as per the spec.

  • The property used in the order by clause must correspond to a persistent field of the associated class and that corresponding column must support comparison operators.

  • Auto-generated source that does not need manual editing
  • Auto-generated source that should be edited manually
  • File that is affected by the modifications applied in this section
  • CarEmbeddable.java
  • Car.java
  • CarDao.java
  • CarDaoBase.java
  • CarDaoImpl.java
  • CarDaoException.java
  • Person.java
  • PersonDao.java
  • PersonDaoBase.java
  • PersonDaoImpl.java
  • PersonDaoException.java

Join Table for Many-To-Many and One-To-Many Relationship

For a Many-To-Many and now One-To-Many association, the @javax.persistence.JoinTable annotation is specified on the OWNING side of the association. This is determined by modeling the owning side as an aggregation or composition.

If there is no owning side defined, then the AndroMDA default convention of an alphabetical name ordering is adopted.

The join table name is defined by default to be the table name of the primary table of the owning side concatenated with the table name of the primary table of the inverse side. You can override the name property by modeling the andromda_persistence_table tagged value on the assocation or alternatively, specify a name for the association.

The default relation name separator is adopted from the AndroMDA metafacade namespace layer. You can simply change this in your application's andromda.xml by specifying the following property in the ejb3 namespace.

    <property name="relationNameSeparator">_</property>

Aggregation and Composition

To model the owning side of a One-To-One or Many-To-Many bidirectional relationship, you indicate that end (the owning end) of the relationship as an aggregate or composite end. You should use aggregation when an entity is part of another one but the latter one does not need the former to exist.

By default, the EJB3 cartridge enables the following via the compositionDefinesEagerLoading namespace property. These are employed if no andromda_persistence_fetch_type tagged value exists.

  • aggregation: lazy-loaded, no cascade
  • composition: eager-loaded, cascade update (cascade option not yet implemented)

The following example illustrates the Many-To-Many bidirection relationship between Car and Person entities. The aggregate end on the Person entity indicates that Person is the owning entity.

  • Auto-generated source that does not need manual editing
  • Auto-generated source that should be edited manually
  • File that is affected by the modifications applied in this section
  • CarEmbeddable.java
  • Car.java
  • CarDao.java
  • CarDaoBase.java
  • CarDaoImpl.java
  • CarDaoException.java
  • Person.java
  • PersonDao.java
  • PersonDaoBase.java
  • PersonDaoImpl.java
  • PersonDaoException.java

Transient Relationships

To indicate an association relationship target as transient (not persisted by the persistence container), you model the <<Transient>> stereotype on the target end. This adds the @javax.persistence.Transient annotation to the relationship getter.

Foreign Key Column Names and Foreign Key Constraint Names

By default, the foreign key column names are defined by the cartridge and foreign key constraint names are defined by your database on creation. This way, we don't impose any complexity on any project. However, there are always certain environments and projects which require a strict architecture. This is where you may need the flexibility to control the foreign key names and foreign key constraint names.

Controlling foreign key names is relatively simple as long as you know where to model the right tagged value. In all four association types, you can use the andromda_persistence_column tagged value on an association end to explicitly define the foreign key name.

In a many-to-one or one-to-one association, model the andromda_persistence_column tagged value on the target association ends. In a one-to-many unidirection or many-to-many association, you can model the andromda_persistence_column tagged value on either one or both ends of the association.

Remember that you don't have to explicitly define the foreign key column names. The cartridge will create a suitable name for you using either the JoinColumn annotation. Using the above solution, you are effectively overwriting the auto-generated foreign key column names.

Explicitly defining foreign key constraint names are slightly more tricky. There is no EJB 3.0 defined solution to setting the foreign key constraint names. Hibernate has provided an extension to the EJB 3.0 annotations which solves this limitation. This is convenient if you are using a JEE container with Hibernate as your persistence provider. If not, you need to check with your provider and determine a corresponding annotation. Fortunately, the EJB3 cartridge currently works well with JBoss and since JBoss uses Hibernate, we are almost home free.

The first thing you need to do to be able to explicitly define your foreign key constraint names is to set the value of the persistenceProviderExtensions namespace property to hibernate in your andromda.xml. You can then model the andromda_persistence_foreignkey_constraint tagged value on the appropriate association ends to explicitly define your foreign key constraint names.

For many-to-one and one-to-one associations, you model the andromda_persistence_foreignkey_constraint tagged value on the target association ends. For one-to-many unidirectional and many-to-many associations, you can model the andromda_persistence_foreignkey_constraint tagged value on either end or both ends of the association depending on what you want.

Keep in mind that for one-to-many unidirectional and many-to-many associations, you must model the foreign key constraint name on the source association end if you want to define the foreign key constraint name on the target association end. If you don't, then neither one will be considered during generation. You can however only define the foreign key constraint name on the source end and not the target end.

Tips

Don't forget to properly set the multiplicity on the association ends and whether an end is navigable. This will ensure the proper code is generated.

Next

In the next section we'll learn about services, click here to continue.