I have following inheritance hierarchy:
Task
|
SpecificTask
|
VerySpecificTask
And I'd like to persist it usign single-table inheritance, so I annotated classes:
#Entity
#Table(name="task")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Task
#Entity
public class SpecificTask extends Task
#Entity
public class VerySpecificTask extends SpecificTask
When I try to save an object of VerySpecificTask class, I get an error:
Unable to resolve entity name from Class [com.application.task.VerySpecificTask]
expected instance/subclass of [com.application.task.Task]
What do I wrong? Is it possible to map multi-level inheritance to single table?
EDIT: Here was a lame bug, I've resolved quickly, so I deleted it to not mess this question.
OK, I've added discriminator column and now it works.
Changed code:
#Entity
#Table(name="task")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="DTYPE",
discriminatorType=DiscriminatorType.STRING
)
#Entity
public class SpecificTask extends Task
#Entity
public class VerySpecificTask extends SpecificTask
(I'm adding it just to provide an accepted answer -- I wouldn't resolve it without the helpful comments to the question.)
The accepted answer is almost perfect. To make it more clear I want to add a #DiscriminatorValue to each inheritance level.
#Entity
#Table(name="task")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="DTYPE",
discriminatorType=DiscriminatorType.STRING
)
public class Task
---
#Entity
#DiscriminatorValue(value="DS")
public class SpecificTask extends Task
---
#Entity
#DiscriminatorValue(value="DV")
public class VerySpecificTask extends SpecificTask
And the materiliazed table looks like
---------------
Table: task
---------------
|...|DTYPE|...|
---------------
|...|DS |...|
|...|DV |...|
|...|DS |...|
...
Try the #MappedSuperclass annotation :
#MappedSuperclass
public class BaseEntity {
#Basic
#Temporal(TemporalType.TIMESTAMP)
public Date getLastUpdate() { ... }
public String getLastUpdater() { ... }
...
}
#Entity
public class Order extends BaseEntity {
#Id public Integer getId() { ... }
...
}
In database, this hierarchy will be represented as an Order table having the id, lastUpdate and lastUpdater columns. The embedded superclass property mappings are copied into their entity subclasses. Remember that the embeddable superclass is not the root of the hierarchy though.
Related
I have two entities
#Entity
#Table(name = "view_a")
public class A extends BaseStringIdTableClass
#Entity
#Table(name = "view_b")
public class B extends BaseStringIdTableClass
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseStringIdTableClass implements Serializable {
#Id
private String id;
And in the database i have two views
select * from view_a
|ID|ColumnA|....
|34222|some Value|....
select * from view_b
|ID|ColumnB|...
|34222|lla lla|...
I have therefore in the database different views. But the rows in this different views have the same ID.
Now i try to read the entities with the standard CRUD Repository.
A a = aRepository.findById("34222").get();
B b = bRepository.findById("34222").get();
In this case i can not find entity b. If i swop the two lines of code i can not find entity a.
I think the persistence context can at one time containt for a specific ID only one entity? Is this right. What can i do?
Repository definitions
public interface ARepository extends JpaRepository<A, String>, QuerydslPredicateExecutor<A> {
public interface BRepository extends JpaRepository<B, String>, QuerydslPredicateExecutor<B> {
Sleeping over one night always help....First sorry for my initial incomplete question.
The problem/error was the both entities extended the same abstract class. And in this abstract class the ID was definied.
Fix after this recogniton was easy. One of the entities does not extend my abstract class, but definies his own id. And now it works......
Haven't been able to find any example on how to use InheritanceType.JOINED and two levels of inheritance, so I'm not sure how to do it. Been trying for a few days (not very eagerly, as you may imagine).
I need to create classes to do something like this:
I was thinking about having a "kind" #DiscriminatorColumn in person and an "Origin" #DiscriminatorColumn in Supplier and in Client or any other kind. The problem is I couldn't find a way to have two values for #DiscriminatorValue in one table.
So my question is: What is the supposed way to do something like this?
Thank you all.
Ely.
P.S. In some classes of the "kind" level (Supplier, client, etc) could need to use something different than National" or "Foreign" for a child.
If your criteria is to minimize an unused space then JOINED strategy is the way you should go. An example approach may look like this:
#Entity // or #MappedSuperclass
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "ORIGIN", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Person { ... }
Optionally you may remove discriminatorType thus defaulting it to string.
Both Supplier and Client entities acts as mapped superclasses (so they are not persistable, cannot be instantiated and queried, cannot be the target of a relationship). Their state and behavior is inherited by the concrete entities which are persistable.
#MappedSuperclass // or #Entity
public abstract class Supplier extends Person { ... }
#Entity
#Table(name = "NATIONAL_SUPPLIER")
#DiscriminatorValue("1")
public class National extends Supplier { ... }
#Entity
#Table(name = "FOREIGN_SUPPLIER")
#DiscriminatorValue("2")
public class Foreign extends Supplier { ... }
#MappedSuperclass // or #Entity
public abstract class Client extends Person { ... }
#Entity
#Table(name = "NATIONAL_CLIENT")
#DiscriminatorValue("3")
public class National extends Client { ... }
#Entity
#Table(name = "FOREIGN_CLIENT")
#DiscriminatorValue("4")
public class Foreign extends Client { ... }
Obviously such an hierarchy can be extended both horizontally and vertically but it's worth to mention that the deeper or wider hierarchy is the more expensive querying/inserting may become (in other words: querying/inserting across hierarchy would require more joins on each new level).
I have the following
#MappedSuperclass
public abstract class A {
#Id #GeneratedValue
public Long id;
}
#Entity
public class B extends A {
}
#Entity
public class C extends A {
}
#Entity
public class D {
#ManyToOne
public A a;
}
The problem is class D and the field a (could be either of type B or C). What should be the mapping?
If you reference an A from another entity, then A should not be a MappedSuperclass, but an entity. You should annotate it with #Entity, and choose an inheritance strategy.
Other than that, the mapping will stay as is.
This is not valid in JPA, as relationships cannot be defined to #MappedSuperclass.
You could map A and #Entity (TABLE_PER_CLASS inheritance would give you the same data model, but also consider JOINED or SINGLE_TABLE, as they are normally more efficient).
In EclipseLink you can also use a #VariableOneToOne for this type of relationship.
See,
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_variableonetoone.htm#CHDDFDGF
What you are doing is correct. It will be the mapping A. So, in Table D ; we will have an additional column which will tell us if A is B or C. In Toplink we have the column name as Type. Even in hibernate we have similar concept. And this column is automatically populated by the ORM.
In my application I have a mapping like this
#MappedSuperclass
public abstract class Base implements Serializable {
#Entity
#Table(name = "LevelOne")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class LevelOne extends Base {
#Entity
#Table(name = "LevelTwo")
public class LevelTwo extends LevelOne {
#Entity
#Table(name = "LevelThree")
public class LevelThree extends LevelTwo {
The tables are created in the DB as expected. The problem I have comes when I try to create a query like this:
session.getCurrentSession().createCriteria(LevelOne.class, "levelOne"). [..] .list();
I get results from all the other LevelX tables not only from the LevelOne Table.
I'm not sure if this behavior is expected or not or if my mapping has an error by not using the an abstract class with the "#Inheritance" annotation, however I would need to get only the "LevelOne" results.
How I could get them?
Yes, it's an expected behaviour. If you need LevelOne only, add a restriction on the implicit class property:
session.getCurrentSession()
.createCriteria(LevelOne.class, "levelOne")
.add(Restrictions.eq("class", LevelOne.class))
. [..] .list();
Suppose a Table per subclass inheritance relationship which can be described bellow (From wikibooks.org - see here)
Notice Parent class is not abstract
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class Project {
#Id
private long id;
// Other properties
}
#Entity
#Table(name="LARGEPROJECT")
public class LargeProject extends Project {
private BigDecimal budget;
}
#Entity
#Table(name="SMALLPROJECT")
public class SmallProject extends Project {
}
I have a scenario where i just need to retrieve the Parent class. Because of performance issues, What should i do to run a HQL query in order to retrieve the Parent class and just the Parent class without loading any subclass ???
A workaround is described below:
Define your Parent class as MappedSuperClass. Let's suppose the parent class is mapped To PARENT_TABLE
#MappedSuperClass
public abstract class AbstractParent implements Serializable {
#Id
#GeneratedValue
private long id;
#Column(table="PARENT_TABLE")
private String someProperty;
// getter's and setter's
}
For each subclass, extend the AbstractParent class and define its SecondaryTable. Any persistent field defined in the parent class will be mapped to the table defined by SecondaryTable. And finally, use AttributeOverrides if needed
#Entity
#SecondaryTable("PARENT_TABLE")
public class Child extends AbstractParent {
private String childField;
public String getChildProperty() {
return childField;
}
}
And define another Entity with the purpose of retrieving just the parent class
#Entity
#Table(name="PARENT_TABLE")
#AttributeOverrides({
#AttributeOverride(name="someProperty", column=#Column(name="someProperty"))
})
public class Parent extends AbstractParent {}
Nothing else. See as shown above i have used just JPA specific annotations
Update: It appears the first option doesn't work as I thought.
First option:
Specify the class in the where clause:
select p from Project p where p.class = Project
Second option:
Use explicit polymorphism that you can set using Hibernate's #Entity annotation:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(polymorphism = PolymorphismType.EXPLICIT)
#Inheritance(strategy = InheritanceType.JOINED)
public class Project {
#Id
private long id;
...
}
This is what Hibernate Core documentation writes about explicit polymorphism:
Implicit polymorphism means that
instances of the class will be
returned by a query that names any
superclass or implemented interface or
class, and that instances of any
subclass of the class will be returned
by a query that names the class
itself. Explicit polymorphism means
that class instances will be returned
only by queries that explicitly name
that class.
See also
How to get only super class in table-per-subclass strategy?
Actually, there is a way to get just the superclass, you just need to use the native query from JPA, in my case I'm using JPA Repositories it would be something like that:
#Query(value = "SELECT * FROM project", nativeQuery = true)
List<Resource> findAllProject();
The flag nativeQuery as true allow running the native SQL on database.
If you are using Entity Manager check this out: https://www.thoughts-on-java.org/jpa-native-queries/