0

I'm having lots of trouble with a little prototype I'm trying to build.

The problem involves two entities. But to understand it, I will need to describe a few others.

In terms of the database structure, it can be described as follows:

Person
----------------
id   Integer PK
surname VARCHAR 
givenNames VARCHAR

TypedIdentifier
----------------
idCode  VARCHAR PK FK (IdType.code)
idVal   VARCHAR PK
person  Integer FK  (Person.id)

IdType
------
code   VARCHAR PK

IdDescription
----------
code   VARCHAR PK FK   (IdType.code)
category VARCHAR 
name    VARCHAR

My problem appears in the relationship between tables IdDescription and IdType.

Basically, the IdType table should consist of a single column, that being the primary key (unique) and the secondary table (IdDescription), which describes the id-type, should have a foreign key to that column.

Two important points which guided my design of the database:

  1. IdType.code is in its own separate table because I wanted to isolate Person from IdDescription (from a Java structure perspective)
  2. Because both tables TypedIdentifier and IdDescription need to reference IdType, I can only have references/relationship annotations to IdType in THOSE tables (and none in IdType itself).

My Java code looks like this:

Person

@MappedSuperclass
public class Person implements Serializable
{
    /**
     * internal unique identifier
     */
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    public Integer id;

    /**
     * 
     * Assigned patient identifiers
     */
    @OneToMany(targetEntity=TypedIdentifier.class, mappedBy="ownerPerson", fetch=FetchType.EAGER)
    public List<TypedIdentifier> patientIdentifiers;

    /**
     * natural identifiers
     */
    public String surname;

    public String givenNames;
}

TypedIdentifier

@Table(uniqueConstraints = @UniqueConstraint(columnNames={"idType", "idValue"}))
@Entity @IdClass(TypedIdentifier.class)
public class TypedIdentifier implements Serializable
{
    /**
     * internal unique identifier
     */
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    public Integer id;

    @Id
    @ManyToOne(fetch=FetchType.LAZY)    
    public IdType idType;   // should be a foreign key

    @Id
    public String idValue;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn
    public Person ownerPerson;
}

IdType

@Entity
public class IdType implements Serializable
{
    @Id
    public String code;
}

IdTypeDescription

@Entity
public class IdTypeDescription implements Serializable
{
    @MapsId
    @JoinColumn(name = "code")
    @OneToOne(cascade = CascadeType.ALL, mappedBy="code")  
    public IdType itype;

    public IdCategory category;

    public String name;
}

If I needed to write this up in SQL I would have no problems. But with JPA/Hibernate a simple thing is turning into a nightmare. I'm bouncing from one error to another and back again.

My problem seems to come from my annotation of the itype member of IdTypeDescription. I've tried various ways (based on example code and API documentation).

These annotation combinations result in my metamodel generator not generating attributes for my static class IdTypeDescription_

@MapsId
@JoinColumn(name = "code")
@OneToOne(cascade = CascadeType.ALL, mappedBy="code")   
public IdType itype;

These annotation combinations result in a null pointer exception in hibernate as it attempts to create the tables:

@Id
@OneToOne(cascade = CascadeType.ALL, mappedBy="code")
public IdType itype;    

or

@Id
@OneToOne(cascade = CascadeType.ALL, mappedBy="code")
@PrimaryKeyJoinColumn
public IdType itype;    

Errors:

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[ProvisionException: Unable to provision, see the following errors:

1) Error injecting constructor, javax.persistence.PersistenceException: Unable to build entity manager factory
  at play.db.jpa.DefaultJPAApi$JPAApiProvider.<init>(DefaultJPAApi.java:35)
  at play.db.jpa.DefaultJPAApi$JPAApiProvider.class(DefaultJPAApi.java:30)
  while locating play.db.jpa.DefaultJPAApi$JPAApiProvider
  while locating play.db.jpa.JPAApi

1 error]]
    at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:265) ~[play_2.11-2.4.4.jar:2.4.4]
    at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:191) ~[play_2.11-2.4.4.jar:2.4.4]

    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.6.jar:na]
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.6.jar:na]
Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors:

1) Error injecting constructor, javax.persistence.PersistenceException: Unable to build entity manager factory
  at play.db.jpa.DefaultJPAApi$JPAApiProvider.<init>(DefaultJPAApi.java:35)
  at play.db.jpa.DefaultJPAApi$JPAApiProvider.class(DefaultJPAApi.java:30)
  while locating play.db.jpa.DefaultJPAApi$JPAApiProvider
  while locating play.db.jpa.JPAApi

1 error
    at com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1025) ~[guice-4.0.jar:na]
    at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051) ~[guice-4.0.jar:na]


    at com.google.inject.internal.InjectorImpl$2$1.call(InjectorImpl.java:1016) ~[guice-4.0.jar:na]
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1092) ~[guice-4.0.jar:na]
    at com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1012) ~[guice-4.0.jar:na]
    ... 38 common frames omitted
Caused by: java.lang.NullPointerException: null
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1708) ~[hibernate-core-5.0.5.Final.jar:5.0.5.Final]
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1617) ~[hibernate-core-5.0.5.Final.jar:5.0.5.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:278) ~[hibernate-core-5.0.5.Final.jar:5.0.5.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:770) ~[hibernate-entitymanager-5.0.5.Final.jar:5.0.5.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:797) ~[hibernate-entitymanager-5.0.5.Final.jar:5.0.5.Final]
    at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:58) ~[hibernate-entitymanager-5.0.5.Final.jar:5.0.5.Final]
    ... 57 common frames omitted
halfer
  • 19,824
  • 17
  • 99
  • 186
svaens
  • 649
  • 7
  • 23
  • Firstly, you should note that mapped superclasses cannot be the target of entity relationships e.g. TypedIdentifier > Person https://docs.oracle.com/javaee/6/tutorial/doc/bnbqn.html#bnbqp Secondly, you have a synthetic key to TypedIdentifier so why bother with the other ID fields - add a unique constraint as you have done. Thirdly, if the DB structure 'too flat' vis a viz your Domain model you can use an Embeddable - IDType and IDTypeDescription then go in the same table. https://en.wikibooks.org/wiki/Java_Persistence/Embeddables#Embeddables – Alan Hay Feb 10 '16 at 14:22
  • Hi Alan. Thanks for your comments. Yes you are right about the TypedIdentifier. I have already those two fields listed as unique. They don't need to be Ids. However, with this class AND especially with class IdTypeDescription I wish the idType not just to be unique, but have a foreign key constraint, so as to prevent values being in those tables that don't already exist in the IdType table. For IdTypeDescription I didn't want to have to create an artificial key. In fact, I didn't want it for TypedIdentifier EITHER, but this was one of my attempts to get Hibernate to accept my model! – svaens Feb 10 '16 at 17:05
  • @AlanHay So it seems you were right to point out my incorrect usage of mapped superclass. In the end I had to use InheritedType.JOINED. Once I did that, errors that I thought were related to the mapping of other classes (but were not ) disappeared, and allowed me to concentrate on the real problem. – svaens Feb 12 '16 at 13:15

0 Answers0