Relationships

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..*] meaning [lowerBound..upperBound] at the car's association end property.

In UML, relationships are modeled using associations, and associations themselves have different properties, which will be discussed here. In UML2, attributes and associations belonging to a class are the class properties. All associations from an Entity must be to another Entity unless it is Transient, an Enumeration, or an Embedded Value Object.

Association ends that are public or private will generate public get/set accessor methods with a private attribute. Association ends that are protected will generate protected get/set accessor methods with a protected attribute.

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'.

images/org/andromda/test/2/a/uml.gif

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

  • name of type datatype::String (or we can use UMLPrimitiveTypes::String from the UML Standard library)
  • birthDate of type datatype::Date

For this entity an identifier will be added by default because no properties were labeled as <<Identifier>>. If you explicitly want to specify an identifier you should model the <<Identifier>> stereotype on a property. Refer to Entities for more information.

The Embeddable class is generated because the Car entity has a classifier operation isRented(). If we run AndroMDA over your model, this is what we expect to see.

  • 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

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

  • Person.getCars() : List<Car>

    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', which indicates the other end of the association is the owning side of the relationship and is the only side of the relationship responsible for mapping the attribute to a table and column. Person.getCars will not have a @Column annotation. The owner property on Car refers to the Person entity. The Many side of a bidirectional 1:M relationship will always be the owner of the association relationship. Only one side of a relationship can have the mappedBy annotation, while the other side will have the @JoinColumn annotation.

  • 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, automatically pluralizing * relationship names. 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() : List<Car>
  • Car.getPerson() : Person

You can turn off pluralization in the andromda.xml default namespace:

<property name="pluralizeAssociationEndNames">false</property>
.

Cascading

You can set the cascade option on all association ends by modeling the <<PersistentAssociationEnd>> 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.

images/org/andromda/test/2/b/uml.gif

  • 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

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 <<PersistentAssociationEnd>> 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. This tagged value accepts a comma separated list of fully qualified cascade types.

Fetch Type

The fetch type tagged value <<PersistentProperty>> 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.

images/org/andromda/test/2/c/uml.gif

  • 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

Aggregation and Composition

To model the owning side of a One-To-One or Many-To-Many bidirectional relationship, you indicate 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. You can order the elements of a collection valued association by modeling the <<PersistentAssociationEnd>> andromda_persistence_orderBy tagged value on the target association end. A few notes to consider:

By default, the EJB3 cartridge enables the following via the compositionDefinesEagerLoading namespace property in andromda.xml. 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)
  • none: defaults for the multiplicity relationship

The following example illustrates the Many-To-Many bidirectional relationship between Car and Person entities. The aggregate end on the Person entity indicates that Person is the owning entity. The Car.getOwners() is on CarEmbeddable class while Person.getCompanyCars() is on the Person class.

images/org/andromda/test/2/e/uml.gif

  • 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

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

For a Many-To-Many and One-To-Many associations, 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 explicitly defined, AndroMDA follows a set of rules to determine which side owns the relationship:

  • (1) If one side is explicitly designated as the owner through the PersistentAssociationEnd <<PersistentAssociationEnd>> andromda_persistence_associationEnd_primary;
  • (2) The aggregate/composite side owns the relationship;
  • (3)The many side of a 1:M relationship is the owner;
  • (4) The type with the longest name owns the relationship (i.e. an association table usually has a longer name because it typically contains the name of the tables it is associated with);
  • (5) If all else fails, the 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 <<PersistentAssociation>>andromda_persistence_table tagged value on the association 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>

Ordering Collection Valued Association

To model the owning side of a One-To-One or Many-To-Many bidirectional relationship, you indicate 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. You can order the elements of a collection valued association by modeling the <<PersistentAssociationEnd>> 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.

images/org/andromda/test/2/d/uml.gif

  • 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

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, i.e. the foreign key and the primary key column names are the same. This is where you may need the flexibility to control the generated 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 <<PersistentProperty>> 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 <<PersistentProperty>> 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 default column name for you on the JoinColumn annotation. Using the above solution, you are effectively overwriting the auto-generated foreign key column names. In this example, the column name for the Car.owner attribute is annotated as @JoinColumn(name="OWNER_FK"), so the OWNER_FK column must exist on the CAR table.

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 <<PersistentAssociationEnd>> andromda_persistence_foreignkey_constraint tagged value on the appropriate association ends to explicitly define your foreign key constraint names. This will generate the @org.hibernate.annotations.ForeignKey(name="FK_PARENT") annotation for the @ManyToOne attribute.

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.

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.

For identifier associations, the order of the columns in the @JoinColumn annotation MUST match the order of the columns in the table foreign key definition which refers to the non-owning table primary key definition. In AndroMDA, JoinColumn order is determined by the order of the properties in the Entity, however if multiple tables contribute identifier properties then we must force a specific order of the columns in the @JoinColumn list. This is done by setting the column order in the <<Entity>> andromda_persistence_joincolumn_order value.

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.