Skip to main content

JPA: Supplement to the access type of @Embeddable class

Posted by guruwons on October 16, 2006 at 8:11 AM PDT

In Java Persistence API, Embeddable class is used to represent composite primary key or share common columns between entities. But when sharing Embeddable classes, you should be cautious whether its fields or properties are mapped to the database due to the unclear spec. I will introduce the supplementary rules to the spec which is implemented in GlassFish(TopLink Essentials).

First, guess what will happen if a Embeddable class is used as below?

@Embeddable public class E {
  // has no metadata. It has two fields, but one property!
  int i;
  int j;
  int getI() { return i; }
  void setI(int i) { this.i = i; }
}

@Entity public class A {
  @Id String id; // entity class uses FIELD access
  @Embedded E e;
}

@Entity public class B {
  String id;
  E e;
  @Id String getId() { return id; } // entity class uses PROPERTY access
  void setId(String id) { this.id = id; }

  @Embedded E getE() { return e; }
  void setE(E e) { this.e = e; }
}

And what will happen if the field E.i has @Basic annotation?

This situations can be confused and Java Persistence spec is not so clear about this.

Sanjeeb Kumar Sahoo nicely addressed this issue[1] and concluded with supplementary rules. These rules are confirmed by spec leaders and implemented in RI GlassFish.
The text here is written by Sahoo. I just added some examples.


Unclear specification

The spec seems to be not clear on this subject as the following sections show:

2.1.5 Embeddable Classes

...
Embeddable classes must adhere to the requirements specified in Section 2.1 for entities with the exception that embeddable classes are not annotated as Entity. Embeddable classes must be annotated as Embeddable or denoted in the XML descriptor as such. The access type for an embedded object is determined by the access type of the entity in which it is embedded. Support for only one level of embedding is required by this specification.

Note, it says is determined by as opposed to can be determined.
Yet, orm_1_0.xsd allows an access-type to be specified for an embeddable, there by contradicting the previous rule.

Moreover, only non-portable applications can have different access-type for an embeddable as the spec says in the following section:

10.1.5.2 access

The access attribute defines the access type for the embeddable class. The access attribute overrides any access type specified by the persistence-unit-defaults element or entity-mappings element for the given embeddable class.

Portable applications must not specify the access attribute if mapping annotations have been applied to the fields or properties of the embeddable class or the entity with which it is associated and the value differs from the access type defined by means of annotations.


Supplementary rules

Here are the rules governing access-type of an embeddable class that we have
agreed on:

Rule 1: In the absence of metadata in embeddable class, access-type of an embeddable is determined by the access-type of the enclosing entity.

Rule 2: In the presence of metadata in embeddable class, access-type of an
embeddable is determined using that metadata. This allows sharing of the
embeddable in entities with conflicting access-types.

Rule 3: It is an error to use a metadata-less embeddable class in entities with conflicting access-types as that might result in different database mapping for the same embeddable class.

Rule 4: It is an error if metadata-complete == false, and metadata is present both in annotations and XML, and access-type as determined by each of them is not same.

Rule 5: It is an error if both fields and properties of an embeddable class are annotated and metadata-complete == false.

These rules are different from what is stated in the spec right now, but that's
OK as the spec leads have acknoledged that the spec is not clear because some
very important lines accidentally did not go into the spec. They will be
clarified in a future version of the JPA spec.


Examples

Rule 1

@Entity public class A {
  @Id String id; // entity class uses FIELD access
  @Embedded E e;
}

// FIELD access according to Rule 1
@Embeddable public class E {
  int i;
  int j;

  int getI() { return i; }
  void setI(int i) { this.i = i; }

  int getJ() { return j; }
  void setJ(int j) { this.j = j; }
}

Rule 2:

// PROPERTY access regardless of embedding entities according to Rule 2
@Embeddable public class E {
  int i;
  int j;

  @Basic
  int getI() { return i; }
  void setI(int i) { this.i = i; }

  int getJ() { return j; }
  void setJ(int j) { this.j = j; }
}

@Entity public class A {
  @Id String id; // entity class uses FIELD access
  @Embedded E e; // E uses PROPERTY access
}

@Entity public class B {
  String id;
  E e;
  @Id String getId() { return id; } // entity class uses PROPERTY access
  void setId(String id) { this.id = id; }

  @Embedded E getE() { return e; } // E uses PROPERTY access
  void setE(E e) { this.e = e; }
}

Rule 3:

The following case is illegal:

@Embeddable public class E {
  // has no metadata. It has two fields, but one property!
  int i;
  int j;
  int getI() { return i; }
  void setI(int i) { this.i = i; }
}

@Entity public class A {
  @Id String id; // entity class uses FIELD access
  @Embedded E e;
}

@Entity public class B {
  String id;
  E e;
  @Id String getId() { return id; } // entity class uses PROPERTY access
  void setId(String id) { this.id = id; }

  @Embedded E getE() { return e; }
  void setE(E e) { this.e = e; }
}

If we allow this use case, then Class C will have two different
persistence views in the same PU. From A's point of view, it has two
persistence fields. From B's point of view, it has one persistence
property. This kind of sharing should be disallowed.
User could have annotated either a field or property of C and that would
have not allowed sharing of the embeddable in A & B, but it would have
provided a unique view of C to both the enclosing entity classes.

Rule 4:

The following case is illegal:

@Embeddable public class E {
  @Basic
  int i;
  int j;

  int getI() { return i; }
  void setI(int i) { this.i = i; }

  int getJ() { return j; }
  void setJ(int j) { this.j = j; }
}

<embeddable class="E" access="PROPERTY">
  <attributes>

    ...
  </attributes>
</embeddable>

Rule 5:

The following case is illegal:

@Embeddable public class E {
  @Basic
  int i;
  int j;

  @Basic
  int getI() { return i; }
  void setI(int i) { this.i = i; }

  int getJ() { return j; }
  void setJ(int j) { this.j = j; }
}


References

[1] https://glassfish.dev.java.net/issues/show_bug.cgi?id=831

Related Topics >>