Beispiel: Entity Inheritance


Inhalt:

Strategie SINGLE_TABLE
Strategie TABLE_PER_CLASS
Strategie JOINED
Application Client
Ohne Annotations

Beispiel für eine Entity Bean die drei Subklassen mit unterschiedlichen Feldern hat. Diese Vererbung wird mittels der drei im JavaEE5-Standard definierten Vererbungsstrategien abgebildet.

Das Beispiel erweitert das Kuchen-Zutat-Beispiel: die Entity "Kuchen" bleibt unverändert, aber es gibt eine abstrakte Basisklasse "Zutat" mit drei Subklassen, die jeweils eigene Felder haben.

Als Client wird ein Swing-Application Client verwendet (GUI-Design mit Jigloo erstellt). Da es sehr viel doppelten Code erfordert hätte, alle drei Strategien in einem einzigen Beispiel abzubilden, wurde nur eine Vererbungs-Variante umgesetzt. Die anderen beiden Varianten unterscheiden sich nur in Annotations und liegen in auskommentierter Form im Code vor.
Hier gibt es das Projekt zum Download (dies ist ein EAR-Export, die Importanleitung findet man im Stateless-Beispiel): KuchenZutatInheritance.ear

Im Beispiel-EAR ist die Strategie "JOINED" aktiviert. Die anderen beiden Strategien sind auskommentiert.

Aufbau des Beispieles

a) Entity Bean "KuchenInheritance" mit Feldern "id" und "name" sowie einer @OneToMany-Relation zu den Zutaten (wie in den bisherigen Beispielen auch).
b) Abstrakte Entity Bean "ZutatBaseInheritanceBean" mit dem Feld "id" und einer @ManyToOne-Relation zum Kuchen.
c) Entity Bean "ZutatFluessigkeitInheritanceBean" als Subklasse von "ZutatBaseInheritanceBean", die eine flüssige Zutat abbildet. Ihre Felder sind eine Menge und eine Maßeinheit (Enumeration mit den Werten "Liter" und "Milliliter"), also z.B. "200 ml Milch".
d) Entity Bean "ZutatGewichtInheritanceBean" als Subklasse von "ZutatBaseInheritanceBean", die eine wiegbare Zutat abbildet. Ihre Felder sind eine Menge und eine Gewichtseinheit (Enumeration mit den Werten "Gramm" und "Kilogramm"), also z.B. "100 g Zucker".
e) Entity Bean "ZutatStueckzahlInheritanceBean" als Subklasse von "ZutatBaseInheritanceBean", die eine nur abzählbare Zutat abbildet. Ihre Felder sind nur eine Menge, also z.B. "5 Eier".
f) Worker Bean "KuchenZutatInheritanceWorkerBean" mit Methoden zum Speichern, Laden und verwalten von Kuchen und Zutaten.
g) Application Client für das Erfassen von Kuchen und den drei Zutaten-Typen pro Kuchen.


Strategie SINGLE_TABLE

Dies bedeutet: es wird nur eine einzige Tabelle in der Datenbank erzeugt, die so heißt wie die Basisklasse und alle Felder aller Subklassen enthält. Zur Unterscheidung der einzelnen Beans wird eine "Discriminator Column" eingeführt, die pro Datensatz eine Kennung des Typs enthält.

Basisklasse ZutatBaseInheritanceBean:
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@NamedQuery (name="findAllZutaten", query="select o from ZutatBaseInheritanceBean o")
public abstract class ZutatBaseInheritanceBean implements Serializable 
{
  private Integer intId;
  private String strZutatName;
  private KuchenInheritanceBean kuchen = null;
  
  public ZutatBaseInheritanceBean()
  { 
  }
  
  @Column()
  @Id ()
  @GeneratedValue ()
  public Integer getId()
  {
    return this.intId;
  }

  public void setId(Integer int_Id)
  {
    this.intId = int_Id;
  }
  
  @Column()
  public String getZutatName()
  {
    return this.strZutatName;
  }

  public void setZutatName(String str_ZutatName)
  {
    this.strZutatName = str_ZutatName;
  }
  
  @ManyToOne ()
  public KuchenInheritanceBean getKuchen()
  {
    return this.kuchen;
  }

  public void setKuchen (KuchenInheritanceBean kuchen)
  {
    this.kuchen = kuchen;
  }
} 
Die Strategie wird über die Annotation @Inheritance definiert, als Typ haben wir hier InheritanceType.SINGLE_TABLE gewählt. Hier könnten Name und Typ der Discriminator Column über eine Annotation @DiscriminatorColumnangegeben werden, ich belasse das hier beim Default (resultiert in einer Spalte "DTYPE" in der Datenbank).
Die Named Query dient dazu alle Zutaten inklusive aller Subtypen zu laden, also den gesamten Tabelleninhalt.

Subklasse ZutatFluessigkeitInheritanceBean:
@Entity
@DiscriminatorValue(value="fluessigkeit")
@Entity
@NamedQuery (name="findAllZutatenFluessigkeit", query="select o from ZutatFluessigkeitInheritanceBean o")
public class ZutatFluessigkeitInheritanceBean extends ZutatBaseInheritanceBean
{
  private Integer intMasseinheitMenge;
  private Masseinheit masseinheit;

  @Column()
  public Integer getMasseinheitMenge()
  {
    return this.intMasseinheitMenge;
  }

  public void setMasseinheitMenge(Integer int_MasseinheitMenge)
  {
    this.intMasseinheitMenge = int_MasseinheitMenge;
  }
  
  @Column()
  @Enumerated(value=EnumType.STRING)
  public Masseinheit getMasseinheit()
  {
    return this.masseinheit;
  }

  public void setMasseinheit(Masseinheit _masseinheit)
  {
    this.masseinheit = _masseinheit;
  }
  
  @Override
  public String toString()
  {
    return this.getZutatName() + " (" + this.intMasseinheitMenge + " " + this.masseinheit + ")";
  }
}
Hier wird zur Erkennung von Instanzen dieser Klasse in der Datenbank ein Wert der Discriminator Column mittels der Annotation @DiscriminatorValue eingetragen: alle Käsekuchen sollen hier den Wert "fluessigkeit" stehen haben.
Die Named Query liefert NUR flüssige Zutaten zurück, alle anderen Typen werden nicht berücksichtigt.

Behandlung von Enums:
Die Property "Masseinheit" ist eine Enum, die so aussieht:
public enum Masseinheit
{
  Liter,
  Milliliter
}
Per Default werden Enum-Spalten mit ihren Integer-Werten in der Datenbank gespeichert. Da ich die Anzeigenamen der einzelnen Werte schöner finde, habe ich die Enum-Speicherungsstrategie an der Property deklariert:
  @Enumerated(value=EnumType.STRING)
  public Masseinheit getMasseinheit()
  {
    ...
Default ist EnumType.ORDINAL.


Ähnlich wie die "ZutatFluessigkeitInheritanceBean" sieht die Subklasse ZutatGewichtInheritanceBean aus:
@Entity
@DiscriminatorValue(value="gewicht")
@NamedQuery (name="findAllZutatenGewicht", query="select o from ZutatGewichtInheritanceBean o")
public class ZutatGewichtInheritanceBean extends ZutatBaseInheritanceBean
{
  private Integer intGewicht;
  private Gewicht gewichtEinheit;

  @Column()
  public Integer getGewicht()
  {
    return this.intGewicht;
  }

  public void setGewicht(Integer int_Gewicht)
  {
    this.intGewicht = int_Gewicht;
  }
  
  public void setGewichtEinheit(Gewicht _gewichtEinheit)
  {
    this.gewichtEinheit = _gewichtEinheit;
  }
  
  @Column()
  @Enumerated(value=EnumType.STRING)
  public Gewicht getGewichtEinheit()
  {
    return this.gewichtEinheit;
  }

  @Override
  public String toString()
  {
    return this.getZutatName() + " (" + this.intGewicht + " " + this.gewichtEinheit + ")";
  }
} 
Auch hier wird ein Discriminator Value gesetzt, und es wird eine spezielle Named Query deklariert die nur abzuwiegende Zutaten zurückliefert.


Zum Schluss noch die ZutatStueckzahlInheritanceBean:
@Entity
@DiscriminatorValue(value="stueckzahl")
@NamedQuery (name="findAllZutatenStueckzahl", query="select o from ZutatStueckzahlInheritanceBean o")
public class ZutatStueckzahlInheritanceBean extends ZutatBaseInheritanceBean
{
  private Integer intAnzahl;

  @Column()
  public Integer getAnzahl()
  {
    return this.intAnzahl;
  }

  public void setAnzahl(Integer int_Anzahl)
  {
    this.intAnzahl = int_Anzahl;
  }
  
  @Override
  public String toString()
  {
    return this.getZutatName() + " (" + this.intAnzahl + " Stück)";
  }
}
Der Discriminator Value wird auf "stueckzahl" gesetzt, und es wird eine spezielle Named Query deklariert die nur zählbare Zutaten zurückliefert.


Das Ergebnis in der Datenbank sieht so aus:
SINGLE_TABLE
Der EntityManager nutzt folgende Query, um alle Zutaten (also alle drei Subtypen) zu laden:
select zutatbasei0_.id as id96_, zutatbasei0_.kuchen_id as kuchen9_96_, zutatbasei0_.zutatName as zutatName96_, zutatbasei0_.gewicht as gewicht96_, zutatbasei0_.gewichtEinheit as gewichtE5_96_, zutatbasei0_.masseinheit as masseinh6_96_, zutatbasei0_.masseinheitMenge as masseinh7_96_, zutatbasei0_.anzahl as anzahl96_, zutatbasei0_.DTYPE as DTYPE96_ from ZutatBaseInheritanceBean zutatbasei0_

Strategie TABLE_PER_CLASS

Dies bedeutet: es wird pro echter Bean-Klasse (abstrakte Klassen ausgenommen!) eine Tabelle in der Datenbank erzeugt, die alle Felder der jeweiligen Subklasse sowie aller Parentklassen enthält.

Basisklasse ZutatBaseInheritanceBean:
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@NamedQuery (name="findAllZutaten", query="select o from ZutatBaseInheritanceBean o")
public abstract class ZutatBaseInheritanceBean implements Serializable
{
  ...
  
  @Column()
  @Id ()
  @GeneratedValue (strategy=GenerationType.TABLE)
  public Integer getId()
  {
    return this.intId;
  }

  ...
} 
Der Rest des Codes ist identisch mit dem vorherigen Beispiel (abgesehen von den DiscriminatorValue-Annotations, die hier in den Subklassen natürlich unsinnig wären.

Besonderheit dieses Beispiels: Das automatische Generieren des Primary Keys muss hier die "strategy" explizit setzen. Der Default GenerationType.AUTO führt zu folgender Meldung:
javax.persistence.PersistenceException: [PersistenceUnit: kuchenZutatInheritancePersistenceUnit] Unable to build EntityManagerFactory
	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:677)
	at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:132)
	at org.jboss.jpa.deployment.PersistenceUnitDeployment.start(PersistenceUnitDeployment.java:281)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	...
Caused by: org.hibernate.MappingException: Cannot use identity column key generation with <union-subclass> mapping for: de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatBaseInheritanceBean
	at org.hibernate.persister.entity.UnionSubclassEntityPersister.<init>(UnionSubclassEntityPersister.java:89)
	at org.hibernate.persister.PersisterFactory.createClassPersister(PersisterFactory.java:90)
	at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:261)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1327)
	at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:669)
	... 100 more

Die drei Subklassen ZutatFluessigkeitInheritanceBean, ZutatGewichtInheritanceBean und ZutatStueckzahlInheritanceBean unterscheiden sich nicht von den Beans aus dem vorherigen Beispiel (abgesehen davon dass hier die Annotation @DiscriminatorValue natürlich nicht verwendet werden darf) und werden hier nicht nochmal wiederholt.

Das Ergebnis in der Datenbank sieht so aus:
TABLE_PER_CLASS
Für die abstrakte Basisklasse KuchenTablePerClassBaseBean wurde keine Tabelle erzeugt weil diese abstrakt ist.

Eine Besonderheit ist, dass jetzt eine neue Tabelle namens "HIBERNATE_SEQUENCES" entsteht, über die die IDs erzeugt werden:
HIBERNATE_SEQUENCES
Der Wert in der Spalte "SEQUENCE_NEXT_HI_VALUE" ist allerdings nicht die nächste ID, der hat wohl eine andere Bedeutung

Der EntityManager nutzt folgende Query, um alle Zutaten (also alle drei Subtypen) zu laden:
select zutatbasei0_.id as id113_, zutatbasei0_.kuchen_id as kuchen3_113_, zutatbasei0_.zutatName as zutatName113_,
zutatbasei0_.gewicht as gewicht114_, zutatbasei0_.gewichtEinheit as gewichtE2_114_,
zutatbasei0_.masseinheit as masseinh1_115_, zutatbasei0_.masseinheitMenge as masseinh2_115_,
zutatbasei0_.anzahl as anzahl116_, zutatbasei0_.clazz_ as clazz_ from
( select id, gewicht, zutatName, null as anzahl, kuchen_id, null as masseinheit, gewichtEinheit, null as masseinheitMenge, 1 as clazz_ from ZutatGewichtInheritanceBean union select id, null as gewicht, zutatName, null as anzahl, kuchen_id, masseinheit, null as gewichtEinheit, masseinheitMenge, 2 as clazz_ from ZutatFluessigkeitInheritanceBean union select id, null as gewicht, zutatName, anzahl, kuchen_id, null as masseinheit, null as gewichtEinheit, null as masseinheitMenge, 3 as clazz_ from ZutatStueckzahlInheritanceBean ) zutatbasei0_


BUGALARM!!!
Im ApplicationClient wurden Subklassen nicht richtig eingeladen, wenn z.B. auf die Zutatenliste des Kuchens zugegriffen wurde. Es klappte nur für die Instanzen von ZutatGewichtInheritanceBean (wie man in obiger Query sieht: die erste im Union-Statement), bei den anderen beiden Subklassen waren alle Felder, die zur Subklasse gehörten, beim Auslesen und als Ergebnis der Query NULL. Ein explizites Laden nur der abzuwiegenden Zutaten klappte allerdings.
So sieht es im Client aus:
TABLE_PER_CLASS im Client
Mal schauen, was das JBoss-Forum sagt...

Strategie JOINED

Dies bedeutet: es wird pro Bean-Klasse (einschließlich abstrakter Klassen) eine Tabelle in der Datenbank erzeugt, die alle Felder der jeweiligen Klasse enthält.

Basisklasse KuchenJoinedBaseBean:
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@NamedQuery (name="findAllZutaten", query="select o from ZutatBaseInheritanceBean o")
public abstract class ZutatBaseInheritanceBean implements Serializable
{
  ...
} 
Der Rest des Codes ist identisch mit dem Beispiel "SINGLE_TABLE" (einschließlich automatisch generierter ID).

Die drei Subklassen ZutatFluessigkeitInheritanceBean, ZutatGewichtInheritanceBean und ZutatStueckzahlInheritanceBean unterscheiden sich nicht von den Beans aus dem vorherigen Beispiel (abgesehen davon dass hier die Annotation @DiscriminatorValue natürlich nicht verwendet werden darf) und werden hier nicht nochmal wiederholt.

Das Ergebnis in der Datenbank sieht so aus:
JOINED

Der EntityManager nutzt folgende Query, um alle Zutaten (also alle drei Subtypen) zu laden:
select zutatbasei0_.id as id86_, zutatbasei0_.kuchen_id as kuchen3_86_, zutatbasei0_.zutatName as zutatName86_,
zutatbasei0_1_.gewicht as gewicht87_, zutatbasei0_1_.gewichtEinheit as gewichtE2_87_,
zutatbasei0_2_.masseinheit as masseinh1_88_, zutatbasei0_2_.masseinheitMenge as masseinh2_88_,
zutatbasei0_3_.anzahl as anzahl89_,
case when zutatbasei0_1_.id is not null then 1 when zutatbasei0_2_.id is not null then 2 when zutatbasei0_3_.id is not null then 3 when zutatbasei0_.id is not null then 0 end as clazz_
from ZutatBaseInheritanceBean zutatbasei0_ left outer join ZutatGewichtInheritanceBean zutatbasei0_1_ on zutatbasei0_.id=zutatbasei0_1_.id left outer join ZutatFluessigkeitInheritanceBean zutatbasei0_2_ on zutatbasei0_.id=zutatbasei0_2_.id left outer join ZutatStueckzahlInheritanceBean zutatbasei0_3_ on zutatbasei0_.id=zutatbasei0_3_.id


Application Client

Aufbau des Clients:
Der Client ist eine schicke Swing-Anwendung mit diesen Features:


Start des Clients:
Da Injection verwendet wird, sind folgende Startparameter in der "Run Configuration" nötig:
Main Class:
org.jboss.client.AppClientMain

Program Arguments:
-jbossclient de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.FrameKuchenZutatInheritance -launchers org.jboss.ejb3.client.ClientLauncher -j2ee.clientName KuchenZutatInheritanceClient

VM Arguments:
-Djava.naming.factory.initial=org.jnp.interfaces.NamingContextFactory -Djava.naming.provider.url=jnp://localhost:1099 -Djava.naming.factory.url.pkgs=org.jboss.naming.client


Die Oberfläche sieht so aus:
Client

Ohne Annotations

"ejb-jar.xml" sieht so aus:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar id="ejb-jar_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                           http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
	<display-name>KuchenZutatInheritanceEJB</display-name>
	
	<enterprise-beans>
		<session>
			<description>
				<![CDATA[Stateless Session Bean für das Arbeiten mit Kuchen und Zutaten.]]>
			</description>
			<display-name>KuchenZutatInheritanceWorkerBean</display-name>
			<ejb-name>KuchenZutatInheritanceWorkerBean</ejb-name>
			<business-remote>de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.KuchenZutatInheritanceWorkerRemote</business-remote>
			<ejb-class>de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.KuchenZutatInheritanceWorkerBean</ejb-class>
			<session-type>Stateless</session-type>
			<persistence-context-ref>
				<persistence-context-ref-name>KuchenZutatInheritancePersistenceUnitRef</persistence-context-ref-name>
				<persistence-unit-name>kuchenZutatInheritancePersistenceUnit</persistence-unit-name>
				<injection-target>
					<injection-target-class>
						de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.KuchenZutatInheritanceWorkerBean
					</injection-target-class>
					<injection-target-name>entityManager</injection-target-name>
				</injection-target>
			</persistence-context-ref>
		</session>
	</enterprise-beans>
</ejb-jar>  
Es gibt keine Neuerungen im Vergleich zum KuchenZutatNM-Beispiel.

"orm.xml" mit den Deklarationen der Entity Beans sieht so aus (im Beispiel für die Strategie "Joind"):
Fett markiert sind hier neuen Elemente, die die Inheritance Strategy steuern.
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
	version="1.0">
	<named-query name="findAllKuchen">
		<query>select o from KuchenInheritanceBean o</query>
	</named-query>
	
	<named-query name="findAllZutaten">
		<query>select o from ZutatBaseInheritanceBean o</query>
	</named-query>
	<named-query name="findAllZutatenFluessigkeit">
		<query>select o from ZutatFluessigkeitInheritanceBean o</query>
	</named-query>
	<named-query name="findAllZutatenGewicht">
		<query>select o from ZutatGewichtInheritanceBean o</query>
	</named-query>
	<named-query name="findAllZutatenStueckzahl">
		<query>select o from ZutatStueckzahlInheritanceBean o</query>
	</named-query>
	
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatBaseInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<inheritance strategy="JOINED"/>
		<attributes>
			<id name="id">
				<generated-value />
			</id>
			<basic name="zutatName"/>
			<many-to-one name="kuchen"
				target-entity="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.KuchenInheritanceBean">
			</many-to-one>
		</attributes>
	</entity>
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatFluessigkeitInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<attributes>
			<basic name="masseinheitMenge"/>
			<basic name="masseinheit">
				<enumerated>STRING</enumerated>
			</basic>
		</attributes>
	</entity>
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatGewichtInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<attributes>
			<basic name="gewichtEinheit">
				<enumerated>STRING</enumerated>
			</basic>
			<basic name="gewicht"/>
		</attributes>
	</entity>
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatStueckzahlInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<attributes>
			<basic name="anzahl"/>
		</attributes>
	</entity>
	
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.KuchenInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<attributes>
			<id name="id">
				<generated-value />
			</id>
			<basic name="name">
			</basic>
			<one-to-many name="zutaten" mapped-by="kuchen" fetch="EAGER"
				target-entity="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatBaseInheritanceBean">
				<cascade>
					<cascade-all />
				</cascade>
			</one-to-many>
		</attributes>
	</entity>
</entity-mappings>

Bei Verwendung der Strategie "SINGLE_TABLE" würde es so aussehen (nur geänderte Stücke der Entity Beans dargestellt):
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatBaseInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<inheritance strategy="SINGLE_TABLE"/>
		<attributes>
			...
		</attributes>
	</entity>
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatFluessigkeitInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<discriminator-value>fluessigkeit</discriminator-value>
		<attributes>
			...
		</attributes>
	</entity>
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatGewichtInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<discriminator-value>gewicht</discriminator-value>
		<attributes>
			...
		</attributes>
	</entity>
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatStueckzahlInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<discriminator-value>stueckzahl</discriminator-value>
		<attributes>
			...
		</attributes>
	</entity>
Auch hier ist bei Verwendung von "TABLE_PER_CLASS" die explizite Angabe der strategy nötig.

Und schließlich die Strategie "TABLE_PER_CLASS" würde es so aussehen (nur geänderte Stücke der Entity Beans dargestellt):
	<entity class="de.fhw.komponentenarchitekturen.knauf.kuchenzutatinheritance.ZutatBaseInheritanceBean" access="PROPERTY"
		metadata-complete="true">
		<inheritance strategy="TABLE_PER_CLASS"/>
		<attributes>
			<id name="id">
				<generated-value strategy="TABLE"/>
			</id>
			,,,
		</attributes>
	</entity>
	
Wichtig ist hier, dass die ID-Generierungsstrategie angegeben wird.


Die modifizierte Version des Projekts gibt es hier:
KuchenZutatInheritanceNoAnnotation.ear.
Man beachte, dass die drei Strategien in orm.xml durch einkommentieren des entsprechenden Abschnitts umgeschaltet werden können, ohne Neucompilieren. Ein eindeutiger Vorteil der Deployment-Deskriptoren.

ACHTUNG: Dieses Projekt kann nicht neben dem obigen KuchenInheritance-Beispiel existieren !



Stand 08.03.2009
Historie:
18.11.2008: Erstellt aus Vorjahresbeispiel, aber komplett neu gebaut.
19.11.2008: Beispiel "Ohne Annotations" zugefügt.
08.03.2009: Client-Start: InitialContext-Property "Context.URL_PKG_PREFIXES" auf "org.jboss.naming.client" geändert