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

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.

Related

Select only some columns from a table

Is there a way to select only some columns from a table using jpa?
My tables are huge and I am not allowed to map all the columns in my entities. I tried to create an entity (as a side note, I don't have PKs in my tables):
#Entity
#Table(name = "SuperCat")
#Getter
#Setter
public class Cat{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name="nameCat")
private String name;
}
and then in my repository to
public interface CatRepository extends
CrudRepository<Cat, Long> {
#Query(
"SELECT name FROM Cat")
Page<Cat> getAlCats(Pageable pageable);
This is only a simple example, but the idea is the same. I have searched a lot and I found projections, but there you need to map the whole table, then I found native queries, but still doesn't apply. I know I can return an Object and the other solution is to use query with NEW and create my own object (no #entity, like a pojo). But is there a way that I can do this using jpa, to be able to use repository and services, if I am creating my own pojo then i will create a #transactional class put the queries (with NEW) there and this is it. I don't like this approach and I don't think that the jpa does't allow you to select only some columns, but I didn't find a proper way.
Maybe you will ask what is the result if I am doing like this:
I get this error: "Cannot create TypedQuery for query with more than one return using requested result type [java.lang.Long]"
(For new queries, I am talking about : http://www.java2s.com/Tutorials/Java/JPA/4800__JPA_Query_new_Object.htm maybe I was not clear)
You can do the same by using below approach.
Just create a constructor in entity class with all the required parameters and then in jpa query use new operator in query like below.
String query = "SELECT NEW com.dt.es.CustomObject(p.uniquePID) FROM PatientRegistration AS p";
TypedQuery<CustomObject> typedQuery = entityManager().createQuery(query , CustomObject.class);
List<CustomObject> results = typedQuery.getResultList();
return results;
And CustomObject class should look like below with the constructor.
public class CustomObject {
private String uniquePID;
public CustomObject(String uniquePID) {
super();
this.uniquePID = uniquePID;
}
public String getUniquePID() {
return uniquePID;
}
public void setUniquePID(String uniquePID) {
this.uniquePID = uniquePID;
}
}
spring-data-jpa projection not need to map the whole table, just select the necessary fileds :
// define the dto interface
public interface CatDto {
String getName();
// other necessary fields
...
}
#Query(value = "select c.name as name, ... from Cat as c ...)
Page<CatDto> getAllCats(Pageable pageable);
By this way, CatDto is an interface and it only includes some fileds part of the whole table. Its fields name need to match the select field's alias name.

Retrieving #NamedQuery from interface of JPA Entity

I have a JPA 2.1 project, database has 2 tables. I have created an interface "UsedClasses.java" and have both Entities implement the interface., i.e.
#Entity
#Table(name = "runRanges")
#NamedQuery(name = "RunRange.findAll", query = "SELECT r FROM RunRange r")
public class RunRange extends AbstractTimestampEntity implements UsedClasses, Serializable ....
and
#Entity
#Table(name = "users")
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User extends AbstractTimestampEntity implements UsedClasses, Serializable ...
I am curious to know if I can use the interface to grab the NamedQuery for either of the Entities.
What I am trying to accomplish if getting the results list according to the class.
public List<UsedClasses> getAllItems() {
open();
Query query = EntityManagerHandler.INSTANCE.getEntityManager().createQuery(UsedClasses.class.getResource(NamedQueries));
List<UsedClasses> aList = query.getResultList();
return aList;
}
You can do it but you should use reflection to retrieve the NamedQuery annotation and you would need to have the UsedClasses instance and not the interface like in your example :
Query query = EntityManagerHandler.INSTANCE.getEntityManager().createQuery(UsedClasses.class.getResource(NamedQueries));
Besides, if you add multiple annotations, it will not work any longer.
I think that a more simple and clean solution would be to add a method in your interface to get the namedQuery String name.
For example
public interface UsedClasses(){
String getNamedQueryForGetAll();
}
And you could implement it in this way :
#Entity
#Table(name = "users")
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User implements UsedClasses(){
public String getNamedQueryForGetAll(){
return "User.findAll";
}
}
Then you can use it in this way :
Query query = EntityManagerHandler.INSTANCE.getEntityManager().createQuery(usedClassInstance.getNamedQueryForGetAll());
Now, I find that it creates a little of duplication and it is not straight readable.
NamedQuery brings a extremely tiny performance improvement.
I am not sure that the complexity/indirection level introduced in the code to gain so few in terms of execution time makes you winning.

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.

Inheriting mapping annotations

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.

Categories