Enterprise Java Development@TOPIC@
Advantages
Allows inheritance of non-entity classes
Disadvantages
No base entity to form queries across hierarchy (unlike TABLE_PER_CLASS)
Tables not normalized (like TABLE_PER_CLASS)
Parent columns repeated in each subclass table
More suitable for ...
Independent subclasses
Figure 66.2. Non-Entity Example Database Schema
create table ORMINH_ALBUM ( ALBUM_ID bigint generated by default as identity, ALBUM_VERSION bigint, artist varchar(255), title varchar(255), primary key (ALBUM_ID) ) create table ORMINH_TOOTHPASTE ( id bigint generated by default as identity, version bigint not null, size integer not null, primary key (id) )
Non-entity base class properties appear in subclass tables
Figure 66.3. Non-Entity Example Java Mapping (Parent Class)
@MappedSuperclass
public abstract class BaseObject {
private long id;
@Access(AccessType.FIELD)
private long version;
@Transient
public long getId() { return id; }
protected void setId(long id) {
this.id = id;
}
Parent class is not a legal entity -- has no @Id
In this example, the implementation of BaseObject actually has an id attribute that the derived classes make use of. However, it is marked as @Transient in the base class and @Id in the derived Entity classes since MappedSuperClasses do not have primary keys. This specific example could have also used TABLE_PER_CLASS because of the availability of an id property in the base class.
Figure 66.4. Non-Entity Example Java Mapping (Override Defaults)
@Entity
@Table(name="ORMINH_ALBUM") //this table holds both this entity and parent class
@AttributeOverrides({
@AttributeOverride(name="version", column=@Column(name="ALBUM_VERSION"))
})
public class Album extends BaseObject {
@Access(AccessType.FIELD)
private String artist;
@Access(AccessType.FIELD)
private String title;
@Id @GeneratedValue //id is being generated independent of other siblings
@Column(name="ALBUM_ID")
public long getId() { return super.getId(); }
protected void setId(long id) {
super.setId(id);
}
"version" column name from parent being renamed
"id" property defined in this class given custom column name
Transient attribute in parent being reused to hold @Id for subclass using PROPERTY access
Figure 66.5. Non-Entity Example Java Mapping (Default Derived)
@Entity
@Table(name="ORMINH_TOOTHPASTE") //table holds this entity and parent class
public class ToothPaste extends BaseObject {
@Access(AccessType.FIELD)
private int size;
@Id @GeneratedValue //id is being generated independent of other siblings
public long getId() { return super.getId(); }
protected void setId(long id) {
super.setId(id);
}
Entity accepts mapping defaults
Figure 66.6. Non-Entity Example Usage
ejava.examples.orm.inheritance.annotated.Album album = new Album();
album.setArtist("Lynyrd Skynyrd");
album.setTitle("One More for the Road");
em.persist(album);
ejava.examples.orm.inheritance.annotated.ToothPaste toothpaste = new ToothPaste();
toothpaste.setSize(10);
em.persist(toothpaste);
Hibernate: insert into ORMINH_ALBUM (ALBUM_ID, ALBUM_VERSION, artist, title) values (null, ?, ?, ?) Hibernate: insert into ORMINH_TOOTHPASTE (id, version, size) values (null, ?, ?)
Rows are inserted into type-specific entity class tables
Figure 66.7. Non-Entity Example Usage (Get Entities)
List<BaseObject> objects = em.createQuery("select a from Album a").getResultList();
objects.addAll( em.createQuery("select tp from ToothPaste tp").getResultList());
assertTrue("unexpected number of objects:" + objects.size(), objects.size() == 2);
for(BaseObject o: objects) {
log.info("object found:" + o);
}
Hibernate: select album0_.ALBUM_ID as ALBUM1_1_, album0_.ALBUM_VERSION as ALBUM2_1_, album0_.artist as artist3_1_, album0_.title as title4_1_ from ORMINH_ALBUM album0_ Hibernate: select toothpaste0_.id as id1_12_, toothpaste0_.version as version2_12_, toothpaste0_.size as size3_12_ from ORMINH_TOOTHPASTE toothpaste0_ -object found:ejava.examples.orm.inheritance.annotated.Album@3822f407, id=1, name=Lynyrd Skynyrd:One More for the Road -object found:ejava.examples.orm.inheritance.annotated.ToothPaste@22a79bc, id=1, name=10oz toothpaste
Separate tables are accessed when obtaining each type
Figure 66.8. Non-Entity Example Usage (Verify DB Schema)
int rows = em.createNativeQuery(
"select ALBUM_ID, ALBUM_VERSION, ARTIST, TITLE " +
" from ORMINH_ALBUM")
.getResultList().size();
assertEquals("unexpected number of album rows:" + rows, 1, rows);
rows = em.createNativeQuery(
"select ID, VERSION, SIZE " +
" from ORMINH_TOOTHPASTE")
.getResultList().size();
assertEquals("unexpected number of toothpaste rows:" + rows, 1, rows);
select * from ORMINH_ALBUM ALBUM_ID ALBUM_VERSION ARTIST TITLE -------- ------------- -------------- --------------------- 1 0 Lynyrd Skynyrd One More for the Road select * from ORMINHTOOTHPASTE ID VERSION SIZE -- ------- ---- 1 0 10
Separate tables per concrete class (like TABLE_PER_CLASS)
No unused columns (like TABLE_PER_CLASS)