AppFuse: JPA Entity Inheritance

Posted by Martin Homik | Posted in Java, WebApp | Posted on 27-07-2007

0

Motivation. Originnaly, I picked up an approach from Semantic Web and RDF(s) where everything is a Thing and everthing else derives from that base class. In my current application, I wanted to have a base pojo class and other classes were supposed to extend that class. Since I am still new to this AppFuse and application frameworks, I had to get familiar with persistency and AppFuse in the context of entity inheritance following the try-and-error style. Here are my results.

Reading. Using google and folling discussions in AppFuse forums I collected a bunch of links. The most valuable are listed below:

Strategies. There are three strategies for entity inheritance: Single Table per Class Hierarchy, Table per Concrete Class, and Joined Subclass.

Single Table per Class Hirarchy. This is the default strategy. All classes in the hirerarchy are mapped to a single table in the database. If you have a deep hierarchy, you might end up having a table with many columns. With respect to memory resources this might be not the best choice, but it is runtime efficient and quite easy to program. To use this strategy, you have to apply the inheritance annotation in the root class. You might also want to stick to the default discriminator name (“DTYPE”) and discriminator type (String). Otherwise change it. Every class in the hierarchy should define a discriminator value which identifies the class in the table row. Here is how you code it.

  1.  
  2.    @Entity
  3.    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
  4.    @DiscriminatorValue("base")
  5.    @Table(name = "single_table")
  6.    public class BaseInheritEntitySingleTable extends BaseObject {
  7.  
  8.     private Long id;
  9.     private String baseText;
  10.  
  11.    …
  1.  
  2.    @Entity
  3.    @DiscriminatorValue("extend")
  4.    public class ExtendInheritEntitySingleTable extends BaseInheritEntitySingleTable {
  5.  
  6.     private String extendedText;
  7.  
  8.    …

To test whether the database schema has been set up properly type ‘mvn hibernate3:hbm2ddl’. If you want to insert values using dbunit, modify your sample-data.xml, like follows:

  1.  
  2. <!–BaseInheritEntitySingleTable-START–>
  3. table name="single_table"><column>id</column>
  4.       <column>base_text</column>
  5.       <column>DTYPE</column>
  6.    <row>
  7.       <value description="id">1</value>
  8.       <value description="base_text">Base text 1</value>
  9.       <value description="DTYPE">base</value>
  10.    </row>
  11.    <row>
  12.       <value description="id">2</value>
  13.       <value description="base_text">Base text 2</value>
  14.       <value description="DTYPE">base</value>
  15.    </row>
  16.    <row>
  17.       <value description="id">3</value>
  18.       <value description="base_text">Base text 3</value>
  19.       <value description="DTYPE">base</value>
  20.    </row>
  21.  
  22. <!–BaseInheritEntitySingleTable-END–>
  23.  
  24. <!–ExtendInheritEntitySingleTable-START–>
  25. <table name="single_table"><column>id</column>
  26.       <column>base_text</column>
  27.       <column>extended_text</column>
  28.       <column>DTYPE</column>
  29.    <row>
  30.       <value description="id">4</value>
  31.       <value description="base_text">E-Base text 1</value>
  32.       <value description="extended_text">Extended text 1</value>
  33.       <value description="DTYPE">extended</value>
  34.    </row>
  35.    <row>
  36.       <value description="id">5</value>
  37.       <value description="base_text">E-Base text 2</value>
  38.       <value description="extended_text">Extended text 2</value>
  39.       <value description="DTYPE">extended</value>
  40.    </row>
  41.    <row>
  42.       <value description="id">6</value>
  43.       <value description="base_text">E-Base text 3</value>
  44.       <value description="extended_text">Extended text 3</value>
  45.       <value description="DTYPE">extended</value>
  46.    </row></table>
  47. <!–BaseInheritEntitySingleTable-START–>

Run ‘mvn test-compile’ to fill the tables with values from your sample-data.xml.

 

Table per Class. Using this strategy means that each class in the hierarchy is mapped to its own table in the database. It provides poor support for polymorphic relationships and usually requires SQL UNION queries. Hence, it is not recommended. However, here are the JPA annotations:

  1.  
  2.    @Entity
  3.    @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
  4.    @Table(name = "base_table_per_class")
  5.    public class BaseInheritEntityTablePerClass extends BaseObject {
  6.  
  7.     private Long id;
  8.     private String baseText;
  9.  
  10.    …
  1.  
  2.    @Entity
  3.    @Table(name = "extend_table_per_class")
  4.    public class ExtendInheritEntityTablePerClass extends BaseInheritEntityTablePerClass {
  5.  
  6.     private String extendedText;
  7.  
  8.    …

Run ‘mvn hibernate3:hbm2ddl’ to create the database schema.

  1.  
  2. <!–BaseInheritEntityTablePerClass-START–>
  3. <table name="base_table_per_class"><column>id</column>
  4.         <column>base_text</column>>
  5.     <row>
  6.         <value description="id">1</value>
  7.         <value description="base_text">Base Text 1</value>
  8.     </row>
  9.     <row>
  10.         <value description="id">2</value>
  11.         <value description="base_text">Base Text 2</value>
  12.     </row>
  13.     <row>
  14.         <value description="id">3</value>
  15.         <value description="base_text">Base Text 3</value>
  16.     </row></table>
  17. <!–BaseInheritEntityTablePerClass-END–>
  18. <!–ExtendedInheritEntityTablePerClass-START–>
  19. <table name="extend_table_per_class"><column>id</column>
  20.         <column>base_text</column>
  21.         <column>extended_text</column>
  22.     <row>
  23.         <value description="id">1</value>
  24.         <value description="base_text">E-Base Text 1</value>
  25.         <value description="extended_text">Extended Text 1</value>
  26.     </row>
  27.     <row>
  28.         <value description="id">2</value>
  29.         <value description="base_text">E-Base Text 2</value>
  30.         <value description="extended_text">Extended Text 2</value>
  31.     </row>
  32.     <row>
  33.         <value description="id">3</value>
  34.         <value description="base_text">E-Base Text 3</value>
  35.         <value description="extended_text">Extended Text 3</value>
  36.     </row></table>
  37. <!–ExtendedInheritEntityTablePerClass-END–>

Run ‘mvn test-compile’ to populate your database schema with data from your sample-data.xml.

 

Joined Subclass Strategy. The root of the class hierarchy is represented by a single table, and each subclass has a separate table that only contains those fields specific to that subclass. The tables are joined using the same primary key names but the primary key of the extended class is also a foreign key to its superclass. In our case, it is ‘id’. This strategy provides good support for polymorphic relationships , but requires join operations to be performed when instantiating entity subclasses. This may result in poor performance for extensive class hierarchies.

  1.  
  2.    @Entity
  3.    @Inheritance(strategy=InheritanceType.JOINED)
  4.    @Table(name = "base_joined_table")
  5.    public class BaseInheritEntityJoined extends BaseObject {
  6.  
  7.        private Long id;
  8.        private String baseText;
  9.  
  10.        …
  1.  
  2.    @Entity
  3.    @Table(name = "extended_joined_table")
  4.    public class ExtendInheritEntityJoined extends BaseInheritEntityJoined {
  5.  
  6.        private String extendedText;
  7.  
  8.    …

Create the database schema using ‘mvn hibernate3:hbm2ddl’. To populate the database with data add to your sample-data.xml:

  1.  
  2. <!–BaseInheritEntityJoined-START–>
  3. <table name="base_joined_table"><column>id</column>
  4.         <column>base_text</column>
  5.     <row>
  6.         <value description="id">1</value>
  7.         <value description="base_text">Base text 1</value>
  8.     </row>
  9.     <row>
  10.         <value description="id">2</value>
  11.         <value description="base_text">Base text 2</value>
  12.     </row>
  13.     <row>
  14.         <value description="id">3</value>
  15.         <value description="base_text">Base text 3</value>
  16.     </row>
  17.     <row>
  18.         <value description="id">4</value>
  19.         <value description="base_text">E-Base text 1</value>
  20.     </row>
  21.     <row>
  22.         <value description="id">5</value>
  23.         <value description="base_text">E-Base text 2</value>
  24.     </row>
  25.     <row>
  26.         <value description="id">6</value>
  27.         <value description="base_text">E-Base text 3</value>
  28.     </row></table>
  29. <!–BaseInheritEntityJoined-START–>
  30. <!–ExtendInheritJoined-START–>
  31. <table name="extended_joined_table"><column>id</column>
  32.         <column>extended_text</column>
  33.     <row>
  34.         <value description="id">4</value>
  35.         <value description="extended_text">Extended text 1</value>
  36.     </row>
  37.     <row>
  38.         <value description="id">5</value>
  39.         <value description="extended_text">Extended text 2</value>
  40.     </row>
  41.     <row>
  42.         <value description="id">6</value>
  43.         <value description="extended_text">Extended text 3</value>
  44.     </row></table>
  45. <!–BaseInheritEntityJoined-START–>

Type ‘mvn test-compile’.

 

Tips.

  • Delete the database schema whenever you are in a prototype try-and-error programming process. You’ll avoid a bunch of errors you might not be able to explain, because your changed code has not be propagated to the database schema or whatever. Starting with a clean database filters unnecessary discractions.
  • Turn off foreign key validation as dbunit causes errors. It’s not the best step, but you will be able to compile and run your application. See here.
  • Be aware that fields in sub classes are by default nullable. Otherwise, you have to define a default value. I am not sure whether this is the best solution, but I usually assign a default value right after declaration of the field.

Open questions.

  • AppFuse seems to complain about a missing primary key when you derive an abstract class from BaseObject and and extend the abstract class by a concrete class. So far I have no explanation for this.
  • Is it possible to overwrite the primary key entity?
  • What about compund primary keys?
  • So far, I have been working only at the maven-dbunit level. That is, I have not tested anything with DAOs and Managers. This could be the next step.
  • I have not tested hierarchies deeper than one level.

 

Conclusion. In the process of going through this brief tutorial I relaised that Entity Inheritance might not be the best approach. I guess, I will stick to relationships as they provide me with more flexibility. There will be still a base class ‘Thing’, but other classes won’t derive from it. Saying this, the alternative would be to establish a OneToOne relationship between a class and its assumed “super” ‘Thing’ class. I wonder if this is different from the Joined Subclass Strategy?

AppFuse: removing auto-generated classes

Posted by Martin Homik | Posted in WebApp | Posted on 12-07-2007

0

AppFuse is an application-framework based on the latest software developments. One extremely useful feature is automatic generation of code (snippets) from POJO classes. Unfortunately, there is no auto-deinstall function. So once, you have generated and installed code automatically, you have to remove the code snippets by hand.Here is a short description how to proceed. Let’s assume, you did not go through the full “Person” tutorial. Instead you implemented a POJO class including annotations and ran ‘mvn appfuse:gen|install -Dentity=Person’. To remove “Person” from project you have to delete:

  • src/main/java/**/model/Person.java
  • src/main/java/**/model/webapp/action/PersonAction.java
  • src/test/java/**/model/webapp/action/PersonActionTest.java
  • src/main/resources/**/model/Person-validation.xml
  • src/main/resources/**/webapp/action/PersonAction-validation.xml
  • src/main/webapp/WEB-INF/pages/personForm.jsp
  • src/main/webapp/WEB-INF/pages/personList.jsp

Remove “Person” related entries from the following files:

  • src/main/resources/hibernate.cfg.xml Delete mapping to Person modell class.
  • src/main/resources/struts.xml Delete all Person actions.
  • src/main/resources/ApplicationResources.properties Delete properties related to Person. Also delete properties inside other languages.
  • src/main/webapp/common/menu.jsp Delete entry for Person
  • src/main/webapp/WEB-INF/applicationContext.xml Delete Person related beans.
  • src/main/webapp/WEB-INF/menu-config.xml Delete Person section.
  • src/test/resources/sample-data.xml Delete Person mockup data.
  • src/test/resources/web-tests.xml Delete Person Canoo web test data. Don’t forget to delete PersonTests in the top target.

RDF Triple Stores

Posted by Martin Homik | Posted in Semantic Web | Posted on 04-07-2007

3

For my competency matching task, I have been looking for different RDF Triple Stores. I am a newbie in this field, so I won’t understand the reasons, advantages, and details of those different stores. So, I will base my design decision on an impression. My requirements are:

  • Easy to use.
  • OWL DL support.
  • Support for SPARQL.
  • Should run in server mode and provide web services.
  • Existing documentation.
  • Vivid community.
  • Roadmap.

I started to develop an ontology for competencies with Protegé. I was able to test the ontology using RacerPro and even to retrieve simple information with some first SPARQL queries. However, I did not know how to formulate my exact matching query in SPARQL: “Find all job competency profiles whose competencies are a subset of a person’s competencies.” The quest for a suitable RDF store began. Here are the candidates.

Jena. I think, Jena is the most known RDF triple store in the field. The documentation is very good and it also comes with some extra tool developed by third parties. It provides a programmatic environment for RDF, RDFS and OWL, SPARQL and includes a rule-based inference engine. It also has the ability to be used as an RDF database via its Joseki layer.  But after a while, I somehow got the impression that Semantic Web people recently were in favour of Sesame. As far as I remember, they criticised Jena’s missing web framework and SPARQL interface which is a bit annoying, because it has. Maybe not at the time. Also they pointed me to Kowari and Mulgara. The Mulgara Semantic Store is an Open Source, massively scalable, transaction-safe, purpose-built database for the storage and retrieval of RDF, written in Java. It is an active fork of Kowari. Another reason why I dropped Jena from my list, is that  Sesame comes with a more modern architecture. And even Boca turned from a hardwired Jena RDF Model to Sesame’s open  RDF Model.

Sesame. Sesame seem to be the most modern approach. It comes with a plugin architecture which makes it very modular. Also, in Version 2 it offers interfaces to the Spring Framework which makes it even more modular. Finally, it can be deployed to tomcat such that one can upload and ontology and test queries in the browser. That’s not bad.

But what convinced me me most, is its documentation, vivid community, and roadmap. I have the impression that they guys will continue developing the system and really care aboout their community. When I posted my request on competency matching in the forum, I received an answer the next day. The suggested query was a bit complicated, but it gave me a very good insight in how to formulate queries. The query was in SeRQL a query language/engine devloped by the Sesame people. I am not quite sure, if I can reformulate that query in SPARQL, because it uses nested WHEREs and existential quantifiers (in a negated form).

Boca. Boca is another RDF Triple Store. I came across it via IBM Semantic Layered Research Platform. Their goal is to come up with a full application framework based on Semantic RDF Store. The framework is quite impressive and supports many sophisticated features such as client support, offline persistence with replication, notification, access control, versioning, and much more. It was easy to install the server application and to run some client examples.

Unfortunately, on the flipside, it seems that they do not support any reasoning. That is, they just provide the RDF store and a SPARQL query interface, but there is no OWL Reasoner. Also, though it is a very interesting project, the community is rather small and I do not know whether this project will die one day. Also, I have the impression that Sesame might catch up in Version 2 with Boca’s features.

Conclusion. I chose the following setting. In the beginning, I use Protegé to develop the antology and to include  some individuals for testing. Then I load the ontology in Sesame and test my queries. This phase is just for testing what can be done and where are the limitations. My requirements fitted top Sesame best.

In a next phase, a user-friendly interface to instantiate competencies and profiles is needed. I am not quite sure whether I want to save this information straight into the RDF store. A much more reliable and stable solution might be to base this phase on a classic application framework including persistency and a relational database, and to write a converter wich converts data from the relational database to rdf statements that can be then uploaded to the RDF store. Considering this alternative enables to use much of state-of-the-art best practices. Or maybe, I should also check Jastor. Jastor is a open source Java code generator that emits Java Beans from Web Ontologies (OWL). Let’s see.