JPA Mixed Inheritance Strategy - java

I have 3 entities:
#Entity
public abstract class A {
#Id
public Long id;
public String a1;
public String a2;
public String a3;
//much more fields
//getters and setters
}
#Entity
public class B extends A {
public String b1;
public String b2;
public String b3;
//much more fields
//getters and setters
}
#Entity
public class C extends A {
public String c;
//that's it. no more data
//getters and setters
}
I want to map these classes to 2 tables. First one will contain all of A and C data (i.e. SINGLE_TABLE inheritance). And the second one will contain B's data and a foreign key to A (i.e. JOINED inheritance).
I tried the solution proposed here, but it doesn't work for me. The attributes of BB1 and BB2 are also included into A.
How to implement such a strategy? Classes A and C are as different as Dog and Cat are, so I can't merge them into one class.
Also I don't want to use table-per-hierarchy which would result in duplicating lots of A's data.

JPA spec (paragraph 2.12) is saying that Support for the combination of inheritance strategies within a single entity inheritance hierarchy is not required by this specification.
Keeping that in mind, in similar cases I usually use JOINED strategy for all of my entities.

After spending so much time on this, and not getting any answer, I've come to this solution (which could be not the best):
#Entity
public abstract class A implements Serializable {
#Id
public Long id;
public String a1;
public String a2;
public String a3;
//much more fields
//getters and setters
}
#Entity
public class B implements Serializable {
#Id
#Column(name="id", nullable=false)
public Long id;
#MapsId
#OneToOne(optional=false)
#JoinColumn(name="id")
public A a;
public String b1;
public String b2;
public String b3;
//much more fields
//getters and setters
}
#Entity
public class C extends A {
public String c;
//that's it. no more data
//getters and setters
}
Conclusion
I was amazed at how such well-supported and popular technology as JPA does not offer a solution for such a trivial case. As pointed out by Pascal Thivent in his answer to this question, Hibernate is cheating us, using a secondary table, which is a very tedious and error-prone approach (you should manually specify for each field, to which table it belongs). So it looks like, there is still a room for improvement in JPA spec.

Related

JPA Inheritance Two Or More Superclass

I made a research about Inheritance in JPA and resources that I found uses just one superclass for each entity. But there is not an example that uses 2 or more superclass.
What about this:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = “Abstract_One”)
public abstract class AbstractOne {
#Id
protected Long id;
…
}
#Entity(name = “A”)
#DiscriminatorValue(“A”)
public class A extends AbstractOne {
#Column
private int a;
…
}
#Entity(name = “B”)
#DiscriminatorValue(“B”)
public class B extends A {
#Column
private int b;
…
}
Is it possible to do that?
If it is possible, which Inheritance Strategy allows that and gives the best data consistency?
I can imagine only the following example
#MappedSuperclass
public class A
{
...
#Id
#Column(name = "RECID")
public Long getId()
...
}
#MappedSuperclass
public class B extends A
{
...
#Column(name = "COL1")
public String getColumn1()
...
}
#Entity(name="INH_TAB1")
public class C extends B
{
...
#Column(name = "COL2")
public String getColumn2()
...
}
Also at the excellent book "Java Persistence with Hibernate" by Bauer, King, Gregory I found the following plase what can be useful in the context of this question:
6.5 Mixing inheritance strategies
You can map an entire inheritance hierarchy with the TABLE_PER_CLASS,
SINGLE_TABLE, or JOINED strategy. You can’t mix them — for example, to switch from a
table-per-class hierarchy with a discriminator to a normalized table-per-subclass
strategy. Once you’ve made a decision for an inheritance strategy, you have to stick with it. This isn’t completely true, however. By using some tricks, you can switch
the mapping strategy for a particular subclass. For example, you can map a class
hierarchy to a single table, but, for a particular subclass, switch to a separate
table with a foreign key–mapping strategy, just as with table-per-subclass.
However, I can not imagine any real case when such complex inheritance hierarchy will be required/useful and also it can affect performance.

What is the best practice for modeling Java classes with Hibernate?

I have a question about what would be the best practice for modeling my Java classes and how they should be related in the database. I'm using Spring-Boot and Spring-Data in the project
I have the situation containing professional Administrative, Professional Financial and Professional Developer. Each of them have specific characteristics.
So, I created a Professional class with the attibutos that are common to all employees:
Professional Class
#Entity
#Table(name = "Professional")
#Inheritance(strategy = InheritanceType.JOINED)
public class Professional {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String age;
... getters e setters
}
The other classes, extend from the professional class:
Administrative Class
#Entity
public class Administrative extends Professional {
private String attribute1;
private String attribute2;
... getters e setters
}
Financial Class
#Entity
public class Financial extends Professional {
private String attribute1;
private String attribute2;
... getters e setters
}
Developer Class
#Entity
public class Developer extends Professional {
private String attribute1;
private String attribute2;
... getters e setters
}
Is this the most appropriate way to handle this scenario? Should I use Class Composition instead of Inheritance? Could you please share examples?
Thank you
Inheritance makes more sense here. As an example, composition makes sense where an entity has another entity, e.g.
class Adult extends Person {
private List<Animal> pets; // person HAS pets (with are another class)
private ID identityCard; // person HAS an identity card
}
It wouldn't make sense here for a Person to extend either of Animal or ID because they aren't a subset of either. However, they are a child of the Person class so it makes sense to inherit.
In your case inheritance makes sense because your child classes are a subset of the Professional. You wouldn't say that a Developer has a Professional, but they are a Professional.

What is the point of #MappedSuperclass if it can't be used with queries and the entity manager?

I have some tables that have some common columns and I thought that when creating entities I would have the entities extend a class where all of the mapping is defined:
#MappedSuperclass
public class AbstractLogMaster implements Serializable
{
#Id
#Basic(optional = false)
#Column("id")
protected long id;
#Column("field1")
protected String field1;
#Column("field2")
protected String field2;
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
etc...
}
#Entity
#Table(name = "logwork")
public class LogWork extends AbstractLogMaster implements Serializable
{
#Column("field3")
protected int field3;
public int getField3()
{
return field3;
}
}
Using this in a query Predicate p1 = builder.equal(logWork.get(LogWork_.field1), f1); results in a NPE (specifically because of the logWork.get(LogWork_.field1) part - the #StaticMetaModel doesn't seem to work with mapped superclass)
Looking at the JPA documentation I discovered that:
Mapped superclasses cannot be queried and can’t be used in EntityManager or Query operations. You must use entity subclasses of the mapped superclass in EntityManager or Query operations. Mapped superclasses can’t be targets of entity relationships. Mapped superclasses can be abstract or concrete.
Does this mean that none of the fields I map in the mapped superclass are available to be used by the extending entities in criteria queries? If so, what is the point of putting mapping into the superclass?? If not, what am I doing wrong??
My bad. Turns out the answer to this question is the "proper" use of the #StaticMetamodel annotated companion classes. The mapped superclasses must all have individual static metamodels that inherit in the same way (I had bundled everything for the entity into one metamodel, LogWork_)
#StaticMetamodel(AbstractLogMaster.class)
public final class AbstractLogMaster_
{
public static volatile SingularAttribute<AbstractLogMaster, Long> id;
public static volatile SingularAttribute<AbstractLogMaster, String> field1;
public static volatile SingularAttribute<AbstractLogMaster, String> field2;
}
#StaticMetamodel(LogWork.class)
public final class LogWork_ extends AbstractLogMaster_
{
public static volatile SingularAttribute<LogWork, Integer> field3;
}

Mapping a class that consists only of a composite PK without #IdClass or #EmbeddedId

I've got two tables A and B with simple PK's.
#Entity
public class A {
#Id
public int idA;
}
#Entity
public class B {
#Id
public int idB;
}
I want to map a new association class AB that simply stores the relations between A and B, with composite PK idA+idB. AB doesn't have any extra columns, just the relation between idA and idB.
Is it possible to map AB using a single class? I want to avoid having to create a new ABId class just to use it as #IdClass or #EmbeddedId in AB, and I don't want to map this with a #ManyToMany association on A or B.
Why do you want to map such a join table? Just use a ManyToMany association between A and B. This join table will then be handled automatically when you'll add/remove a B to/from the list of Bs contained in A.
See http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#d0e11402
If you really want to do that, then just map the two IDs with the #Id notation. The primary class will be the entity class itself (which must be serializable), as explained in the hibernate reference documentation. Note that this is Hibernate-specific.
It would be nice if #JBNizet 's suggestion worked. Unfortunately, there's an old bug which makes it impossible to adopt it in the version I'm using (3.3.1-GA)
I've finally sorted this out by defining an inner static ID class and using it as #IdClass:
#Entity
#Table(name="TABLE_AB")
#IdClass(value=ClassAB.ClassABId.class)
public class ClassAB implements Serializable {
private String idA;
private String idB;
#Id
public String getIdA(){ return idA; }
public void setIdA(String idA){ this.idA = idA; }
#Id
public String getIdB(){ return idB; }
public void setIdB(String idB){ this.idB = idB; }
static class ClassABId implements Serializable {
private String idA;
private String idB;
#Column(name="ID_A")
public String getIdA(){ return idA; }
public void setIdA(String idA){ this.idA = idA; }
#Column(name="ID_B")
public String getIdB(){ return idB; }
public void setIdB(String idB){ this.idB = idB; }
// HashCode(), equals()
}
}
This way I don't have to define a new public class, and I don't have to modify the mappings file to include the ID class.

Mapping Multiple Classes to a Table in Hibernate, Without a DTYPE Column

I have two hibernate classes: a base class, and an extended class that has additional fields. (These fields are mapped by other tables.)
For example, I have:
#Entity
#Table(name="Book")
public class A {
private String ID;
private String Name;
// ...
}
#Entity
#Table(name="Book")
public class B extends A {
public String node_ID;
// ...
}
public class Node {
public String ID; // maps to B.node_ID
// ...
}
How do I map this in Hibernate? The hibernate documentation states three types of inheritence configurations: one table per class, one table with a type column, and a join table -- none of which apply here.
The reason I need to do this is because class A is from generic framework that's reused over multiple projects, and class B (and Node) are extensions specific to one project -- they won't be used again. In the future, I may have perhaps a class C with a house_ID or some other field.
Edit: If I try the above pseudo-code configuration (two entities mapped to the same table) I get an error that the DTYPE column doesn't exist. The HQL has a "where DTYPE="A" appended.
This is possible by mapping the #DiscriminatorColumn and #DiscriminatorValue to the same values for both classes; this can be from any column you use that has the same data regardless of which type (not sure if it works with null values).
The classes should look like so:
#Entity
#Table(name="Book")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="published")
#DiscriminatorValue(value="true")
public class A {
private String ID;
private String Name;
// ...
}
#Entity
#Table(name="Book")
#DiscriminatorValue(value="true")
public class B extends A {
public String node_ID;
// ...
}
For anyone who got here like me and does not want to have the dtype column but instead want to use the same table for more than one entity as is I would recommend using this
Basically you can create a Base like this
#MappedSuperclass
public abstract class BaseBook<T extends BaseBook> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
... any other variables, getters + setters
}
#Entity
#Table(name= "book")
public class BookA extends BaseBook<BookA>{
//Default class no need to specify any variables or getters/setters
}
#Entity
#Table(name= "book")
public class BookB extends BaseBook<BookB>{
#Column(name = "other_field")
private String otherFieldInTableButNotMapedInBase
... Any other fields, getter/setter
}
From the above we have created base super class which does not have any entity or table mapping. We then create BookA to be default with the Entity + Table mapping. From there we can create other Entities all extending from BaseBook but pointing to one table

Categories