Inheriting mapping annotations - java

I'm working on a JPA project in which I have some different Entities extending a super-class annotated as Entity:
#Entity
#Table(name = "export_profiles")
#NamedQueries({
#NamedQuery(name = "ExportProfile.getAll", query = "select ep from PersistentExportProfile ep"),
#NamedQuery(name = "ExportProfile.getByName", query = "select ep from PersistentExportProfile ep where ep.profileName = :name") })
public abstract class PersistentExportProfile extends AbstractExportProfile {
// other mappings...
}
I'd like to inherit mappings defined in my PersistentExportProfile into each sub-class.
Is it possible? What do I have to change in my super-class, and what do I have to add in my sub-entities?
NOTE all the sub-classes will be mapped on the same table.

If the sole purpose of your superclass is to define common mappings for the sub classes, but is not persistent itself, you would be better off using the #MappedSuperclass annotation or the <mapped-superclass> for xml mappings. There is an example here.

This case may be a good start in Postgres.
By example
CREATE TABLE "public"."abstract_export_profile" (
"label" TEXT
) WITHOUT OIDS;
CREATE TABLE "public"."persistent_export_profile" (
"id" BIGSERIAL,
"value" TEXT,
CONSTRAINT "mandant_pkey" PRIMARY KEY("id")
) INHERITS ("public"."abstract_export_profile")
WITHOUT OIDS;
The Name Class:
#MappedSuperclass
public class AbstractExportProfile { ... }
And the
#Entity
#Table(name= "mandant")
public class PersistentExportProfile extends AbstractExportProfile { ... }

The best solution for me was to add #Inheritance(strategy=InheritanceType.SINGLE_TABLE) and #DiscriminatorColumn(name="export_type", discriminatorType=DiscriminatorType.STRING) to my abstract class, then in my concrete classes I added #DiscriminatorValue to define the value of the DiscriminatorColumn.

Related

Spring Jpa inheritance type joined select only baseclass

I got problem with select only base class and cannot find solution for this.
I always get subclasses too and not only base class.
Consider I have base class
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class BaseClass {
#Id
#GeneratedValue
private Long id;
private Date date;
// some fields
}
now I inherite
#Entity
public class SubClassA extends BaseClass {
// some fields
}
interface BaseClassRep extends JpaRepository<BaseClass,Long>{
#Query(nativeQuery = true, value = "select id,date from baseclass where date = ?1")
public BaseClass getByDate(Date date)
}
Problem is that BaseClassRep is not only returns BaseClass but SubClassA as well and all other subclasses that inherits from BaseClass.
How to tell Hibernate that I really only want baseclass and not subclasses too.
you could use type operator.
please look at the following link
How do I query for only superclass entities in a jpql query?
or(Java /JPA | Query with specified inherited type)
select id,date from baseclass b where date = ?1 and b.class = ?2
I think you have to use the TABLE_PER_CLASS inheritance type:
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
, then your create a repository for the superclass and you fetch your data.

JPA OneToMany Association from superClass

I’m trying to map the inheritance from the superclass LendingLine and the subclasses Line and BlockLine. LendingLine has an ManyToOne association with Lending.
When I try to get the LendingLines from the database without the inheritance it works fine. The association works also. But when i add the inheritance, lendingLines in Lending is empty. I also can't get any LendingLines from the DB with the inheritance.
Can anybody help me?
(Sorry for the bad explanation)
Thanks in advance!
LendingLine:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
#DiscriminatorValue(value="Line")
#Table(name = "LendingLine")
public class LendingLine {
...
public LendingLine(){}
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER, targetEntity=Lending.class)
#JoinColumn(name = "LendingId")
private Lending lending;
...
Lending:
#Entity
#Table(name = "Lending")
public class Lending {
...
public Lending(){}
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER, mappedBy = "lending")
private List<LendingLine> lendingLines;
...
BlockDate:
#Entity
#DiscriminatorValue(value = "BlockLine")
public class BlockLine extends LendingLine {
public BlockLine(){
}
}
LendingLineRepository:
This class only reads from the db because the db was created by another application ( C#) where the objects are added to the db.
public class LendingLineRepository extends JpaUtil implement LendingLineRepositoryInterface {
#Override
protected Class getEntity() {
return LendingLine.class;
}
#Override
public Collection<LendingLine> findAll() {
Query query = getEm().createQuery("SELECT l FROM LendingLine l");
System.out.println(query.getResultList().size());
return (Collection<LendingLine>) query.getResultList();
}
Table LendingLine:
Choose your type of superclass according to your needs:
Concrete Class
public class SomeClass {}
Define your superclass as a concrete class, when you want to query it and when you use a new operator for further logic. You will always be able to persist it directly. In the discriminator column this entity has it's own name. When querying it, it returns just instances of itself and no subclasses.
Abstract Class
public abstract class SomeClass {}
Define your superclass as an abstract class when you want to query it, but don't actually use a new operator, because all logic handled is done by it's subclasses. Those classes are usually persisted by its subclasses but can still be persisted directly. U can predefine abstract methods which any subclass will have to implement (almost like an interface). In the discriminator column this entity won't have a name. When querying it, it returns itself with all subclasses, but without the additional defined information of those.
MappedSuperclass
#MappedSuperclass
public abstract class SomeClass {}
A superclass with the interface #MappedSuperclass cannot be queried. It provides predefined logic to all it's subclasses. This acts just like an interface. You won't be able to persist a mapped superclass.
For further information: JavaEE 7 - Entity Inheritance Tutorial
Original message
Your SuperClass LendingLine needs to define a #DiscriminatorValue as well, since it can be instantiated and u use an existing db-sheme, where this should be defined.

Using Hibernate in case of a common table which holds metadata for several tables

I have the below scenario :
There is one table which maintains the metadata related to several entities. Below is the schema and how the table would look like :
Both TABLE_A and TABLE_B are mapped as Hibernate Entities and are backed by ClassA and ClassB.
Now, how can I map this table in Hibernate such that I can achieve the below scenario :
ClassA classA = aService.getById("1234");
ClassB classB = bService.getById("1234");
classA.getAttributeValue(); -- Should return 12ab
classB.getAttributeValue(); -- Should return cs12
Is there a way where Hibernate can intelligently get the AttributeValue based on the type of the object from which the method is called?
I think you're looking for Single table inheritance. So your classes could look like this:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="entityType",
discriminatorType=DiscriminatorType.STRING
)
#DiscriminatorValue("TABLE_A")
public class ClassA {
#Id
private String id;
#Column(name = "ATTRIBUTE_NAME")
private String name;
#Column(name = "ATTRIBUTE_VALUE")
private String value;
}
#Entity
#DiscriminatorValue("TABLE_B")
public class ClassB extends ClassA { ... }
An alternative would be creating a common ancestor class and have Table_A mapped to it's own subclass entity.

JPA 2.0 / Hibernate InheritanceType TABLE_PER_CLASS and OneToMany/ManyToOne bidirectional relation

We have 2 tables (an active table and an archive table) which have the same structure (ex. Employee and EmployeeArchive). To be able to leverage common code to use results for both tables we have an abstract parent class that defines all the methods and annotations.
We like to be able to perform queries that will use the same query for both tables and union the results together.
We have another entity/table (ex. Organization) with a onetomany/manytoone bidirectional relationship with Employee; Organization has a List of Employees and every employee has an organization.
When getting the employees of an organization via the association we only want the employees from the active table not the archive.
Is there a way to achieve what we are attempting or a viable workaround?
We have tried various implementations of #MappedSuperclass, #Entity/#InheritanceType.TABLE_PER_CLASS to try to achieve what we want. Each implementation would nearly achieve what we want but not completely. For example to be able to query both tables we could have an abstract parent Entity with InheritanceType.TABLE_PER_CLASS but then we could not have the mappedBy relationship to Employee in the Organization. We can use a MappedSuperclass as the parent to be able to have the correct relationship but then we cannot query both the Archive and Active tables via the union.
Here is basically what we are trying to layout:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractEmployee {
#ManyToOne
#JoinColumn(name="employeeId", nullable=false)
Organization org;
...
}
#Entity
public class Employee extends AbstractEmployee {
}
#Entity
public class EmployeeArchive extends AbstractEmployee {
}
#Entity
public class Organization {
#OneToMany(cascade=ALL, mappedBy="org")
List<Employee> employees;
...
}
Code
public List<AbstractEmployee> getAllEmployees()
{
Query query = em.createQuery("SELECT e FROM AbstractEmployee e where e.name = ‘John’", AbstractEmployee.class);
return query.getResultList();
}
public List<Organization> getOrganizations()
{
Query query = em.createQuery("SELECT e FROM Organization o ", Organization.class);
List<Organization> orgs = query.getResultList();
// fetch or eager fetch the Employees but only get the ones from the active employee table
return orgs;
}
We also tried to have the parent class extend the MappedSuperclass and put the implementation and annotations in the MappedSuperclass but we get an AnnotationException for the relationship of the Organization
#MappedSuperclass
public abstract class AbstractMapped {
#ManyToOne
#JoinColumn(name="employeeId", nullable=false)
Organization org;
}
#Entity
#Inheritance(#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS))
public abstract class AbstractEmployee extends AbstractMapped {
... `Constructors` ...
}
On deployment we get the following exception:
Caused by org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: Employee.org in Organizaztion.employees
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:685)
You can do this by changing the mapping of Organization to Employee so that it uses a relationship table, rather than having the org field in the Employee table. See the example in the Hibernate documentation, which for you would look something like:
#Entity
public class Organization {
#OneToMany(cascade=ALL)
#JoinTable(
name="ACTIVE_EMPLOYEES",
joinColumns = #JoinColumn( name="ORGANIZATION_ID"),
inverseJoinColumns = #JoinColumn( name="EMPLOYEE_ID")
)
List<Employee> employees;
...
}
However, I have to say that I think having two tables to represent current vs archived Employees is a bad idea. This sounds to me like a 'soft delete' kind of situation, which is better handled with an in-table flag (IS_ACTIVE, or something). Then you don't have these odd abstract classes to do your queries, multiple tables with the same kind of data, etc etc. A bit of a description of this strategy is here.
Then you can use the non-join table mapping that you've already got, and use the #Where annotation to limit the employees in an organization to ones that have IS_ACTIVE set to true. An example of this approach is here.
This is one of the annoying things about hibernate. The way to do this is to have another abstract class, AbstractMapped, which simply looks like this:
#MappedSuperclass
public abstract class AbstractMapped {
}
and then have AbstractEmployee extend AbstractMapped. Then you have AbstractEmployee as both an Entity and a Mapped Superclass, even though the two tags are mutually exclusive.
AbstractEmployee should be the #MappedSuperClass, and should not be an #Entity, which creates a table for the class.
Organization should contain a List<AbstractEmployee> not of Employee.

How to map an abstract class or interface in Hibernate HQL?

Imagine that I have a Debtor class. With Hibernate, I will define the class like that:
#Entity
#Table(name = "T_DEBTOR")
public class Debtor {
#Id
#Column(name = "ID_DEBTOR")
private String idDebtor;
...
My DAO will then looks like:
public class DebtorDaoImpl implements DebtorDao {
#PersistenceContext
private EntityManager em;
#SuppressWarnings("unchecked")
public List<Debtor> findAllDebtors() {
Query q = em.createQuery("select d from Debtor d");
return (List<Debtor>) q.getResultList();
}
This works well. However, I am in a configuration where I need to access differents schemas (as pointed here). Of course, in each schema the table that hosts the debtor list has not the same name. In addition to that, they may not have the exact same structure. That why I have x differents Debtor class (where x is the number of schemas I manipulate).
In the case where I have two differents schemas, I will have two different Debtor class: DebtorOne and DebtorTwo.
As I want to ease my developments, I created an Interface (or an Abstract class, it does not change my problem here) that is implemented by both DebtorOne and DebtorTwo:
public interface Debtor {
String getIdDebtor();
}
and:
#Entity
#Table(name = "T_DEBTOR_ONE")
public class DebtorOne implements Debtor {
#Id
#Column(name = "ID_DEBTOR")
private String idDebtor;
...
If I let my DAO as it is, I get the following error from Hibernate:
Caused by: org.hibernate.hql.ast.QuerySyntaxException: Debtor is not mapped [select d from Debtor d]
If I change my DAO to have this:
public List<Debtor> findAllDebtors() {
Query q = em.createQuery("select d from DebtorOne d");
return (List<Debtor>) q.getResultList();
}
then it works, but it is specific to DebtorOne schema...
One solution I see is to define a named query on the DebtorOne and DebtorTwo classes and call this named query from my DAO.
In others words:
#Entity
#Table(name = "T_DEBTOR_ONE")
#NamedNativeQueries( { #NamedNativeQuery(name = "findAllDebtors", query = "select d from DebtorOne d") })
public class DebtorOne implements Debtor {
and in the DAO:
#SuppressWarnings("unchecked")
public List<Debtor> findAllDebtors() {
Query q = em.createNamedQuery("findAllDebtors");
return (List<Debtor>) q.getResultList();
}
I didn't try it yet, but I think it will work...
EDIT I just tried, this will work... except that the NamedQuery must be named differently for DebtorOne and DebtorTwo...
However, I am wondering if there is a way to solve my problem without using the latter solution?
Edit regarding the first answer, which suggests to use the #MappedSuperclass. This annotation seems to be the perfect solution for me, but I think I forgot something, as I still get the same error.
The main Debtor:
#MappedSuperclass
public class Debtor {
#Id
#Column(name = "IDDEBTOR")
protected String idDebtor; // With getter and setter
}
one of the extended Debtor class:
#Entity
#Table(name = "DEBTOR_ONE")
public class DebtorOne extends Debtor {
...
and in my DAO:
public List<Debtor> findAllDebtors() {
return (List<Debtor>) em.createQuery("select d from Debtor d").getResultList();
}
is still returning me the error Caused by: org.hibernate.hql.ast.QuerySyntaxException: Debtor is not mapped [select d from Debtor d]
What did I miss this time?
I think this is not possible with an interface, but only with a common abstract base class, that will be annotated with #MappedSuperclass (see Hibernate documentation for further details)
I think for this to work you'd either have to actually map the Debtor table to a table or use the table per class strategy (union-subclass). #MappedSuperClass only seems to implement a very rudimentary mechanism to copy properties and wouldn't work because you can't query for instances of the superclass.
I take it from the link that you already have something in place to avoid mapping DebtorTwo in the hibernate session for the schema of DebtorOne (otherwise querying Debtor would pull in all records, including ones from the DebtorTwo table that wouldn't exist). In that case, follow the example from the documentation to map the subclass for either case.

Categories