When using the #DiscriminatorColumn on a class and #DiscriminatorValue on subclasses, the SQL that Hibernate generates uses the discriminator value as a literal for the clause involving the discriminator column.
Example:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "PART_TYPE")
#Table(name = "NAME")
public class NamePartEntity implements Comparable<NamePartEntity> {
// Stuff
}
#Entity
#DiscriminatorValue(value = "GIV")
public class GivenNameEntity extends NamePartEntity {
// stuff
}
If I create a simple criteria query with no criteria except the class, I would get SQL generated like such:
select this_.person_id as y0_ from name this_ where this_.part_type='GIV'
This isn't so bad until you have a handful of discriminator values and the table might be selected from multiple times, such that a query like the below:
SELECT this_.person_id AS y0_
FROM name this_
WHERE this_.part_type='FAM'
AND this_.value_u =:8
AND this_.tenant_id =:9
AND this_.person_id IN
(SELECT this_.person_id AS y0_
FROM name this_
WHERE this_.part_type='GIV'
AND this_.value_u =:10
AND this_.tenant_id =:11
AND this_.person_id IN
(SELECT this_.person_id AS y0_
FROM name this_
WHERE this_.part_type='GIV'
AND this_.value_u =:12
AND this_.tenant_id =:13
AND this_.person_id IN
(SELECT this_.person_id AS y0_
FROM name this_
WHERE this_.part_type='PFX'
AND this_.value_u =:14
AND this_.tenant_id =:15
)
)
)
could have a ton of different SQL ids and execution plans based on the literals ('FAM', 'GIV', 'PFX' in this case but they could be different and in different orders). However, if bind variables were used in place of those discriminator value literals, it would be the same sql id and have the same execution plan.
So, is it possible to have Hibernate use the discriminator column/value annotations in such a way that bind variables are used instead of literals? I know it would be possible to rewrite my entities in such a way to avoid this but I wanted to see if I could get the bind variable functionality with the existing annotations in some way.
Alternatively, is there a way I can still use my extended classes without using discriminator values? If I try that and have the #Entity annotation on each extended class, it complains about missing the discriminator type even when there are no discriminator annotations.
No, it is not possible to get it out of the box.
The closest workaround that comes to my mind is to select from the base class and explicitly filter by subclass:
entityManager.createQuery("select gne from NamePartEntity gne where type(gne) = :subclass")
.setParameter("subclass", GivenNameEntity.class)
.getResultList();
Alternatively, is there a way I can still use my extended classes
without using discriminator values? If I try that and have the #Entity
annotation on each extended class, it complains about missing the
discriminator type even when there are no discriminator annotations
That is because of the InheritanceType.SINGLE_TABLE strategy you've used. You could always try to use other strategies, like InheritanceType.JOINED, depending upon what you query for most often.
On another note, Doesn't having this many types of Names per-se an indicator that you must have types as a relation and not a Sub-type?
Related
In Blaze Persistence with querydsl integration, it supports subquery in join statement. So I wonder how to combine projects with CTE entity in a join-subquery condition.
let's say I have two entities named person and pet. They are defined as this:
Person
Pet
id
id
name
personId
age
Here is my test code:
blazeJPAQueryFactory.selectFrom(QPerson.person)
.leftJoin(
JPQLNextExpressions
.select(Projections.bean(
PersonPetCte.class,
QPet.pet.personId.as(QPersonPetCte.personPetCte.personId),
QPet.pet.age.sum().as(QPersonPetCte.personPetCte.ageSum)
))
.from(QPet.pet)
.groupBy(QPet.pet.personId),
QPersonPetCte.personPetCte
)
.on(QPersonPetCte.personPetCte.personId.eq(QPerson.person.id))
.where(QPersonPetCte.personPetCte.ageSum.gt(30))
.fetch();
where PersonPetCte is declared as below (getters and stters omitted for brevity):
#CTE
#Entity
public class PersonPetCte {
#Id
Long personId;
Long ageSum;
}
run this test results in the following exception:
java.lang.UnsupportedOperationException: Select statement should be bound to any CTE attribute
Basically I want to achieve this: get all persons whose sum of their pet age is above 30.
I am trying to avoid string-hardcoded constant as much as possible, which is why I come across the idea of using CTE.
Please tell me if I am totally conceptually wrong or missing someting.
You almost got the syntax right, but Projections.bean does not provide enough metadata to deduce the mapping for the CTE.
Instead you have to do:
new BlazeJPAQuery<>()
.from(QPet.pet)
.groupBy(QPet.pet.personId)
.bind(QPersonPetCte.personPetCte.personId, QPet.pet.personId)
.bind(QPersonPetCte.personPetCte.ageSum, QPet.pet.age.sum())
I'm creating a JPA query where I want to sort on an email address. The table I'm querying is a Member table. This Member can EITHER point at an Account OR an Invite. Whether one of those associations is filled can be seen by the MemberStatus enumeration.
#Entity
public class Member {
#JoinColumn #ManyToOne private Account account;
#JoinColumn #OneToOne private Invite invite;
#Enumerated private MemberStatus status; //value can be INVITED or JOINED
}
So BOTH Account and Invite contain a String field called emailAddress. For the intents of this question, consider them to look like this:
#Entity
public class Account/Invite {
private String emailAddress;
}
I want to retrieve all members, left join on Account and Invite and sort on emailAddress. If I write a query like this:
#NamedQuery(
name="findMembers",
query="select m from Member m
left join m.invite i
left join m.account a
order by emailAddress asc"
)
Then I get an exception saying:
org.postgresql.util.PSQLException: ERROR: column "emailaddress" does
not exist Hint: Perhaps you meant to reference the column
"invite1_.email_address" or the column "account2_.email_address".
Which makes sense of course. But is there a way to add some alias to this emailAddress field depending on which left joined table is present? Is this even possible in SQL, let alone JPA? From a database perspective I'm not sure how it would work.
Btw, I do not want to go in the direction of database inheritance where both these referenced entities have the emailAddress field. That has too many downsides compared to the benefit.
You could use SQLs coalesce function which should be supported in JPA>=2.0.
e.g.
#NamedQuery(
name="findMembers",
query="select m from Member m
left join m.invite i
left join m.account a
order by coalesce(i.emailAddress, a.emailAddress) asc"
)
I have a sample code piece like this-
#Entity
#Table(name = "employee")
#Where(clause = "active IS TRUE")
public class Employee{
}
This will fetch all the record of employee table having active=true or 1. In some cases, it may require that I want to load the records having active=false or 0.
If I write my code as FROM Employee emp WHERE emp.active IS FALSE but the generated query contains bot the conditions given in HQL and Annotations.
Hence, the expected results is not coming. Is there anyway to override this predefined #Where defined over entity?
I know its too old question but I was facing same issue and thought I should share my workaround.
Totally agree with #cнŝdk answer as you cannot override but you can ignore #Where clause by defining nativeQuery as below:
#Query(value = "Select * from customer where company_id = ?1", nativeQuery = true) List<Customer> findByCompanyIdIgnoringEntityWhere(Long companyId);
The SQL in the #Query annotation must point the table's name and the fields' names (not entity's name).
AFAIK you can't override it inside your class because if you take a look at the #Where documentation you will see that this interface is annotated #Retention(value=RUNTIME) so it has RUNTIME as a RetentionPolicy and you can see in the RetentionPolicy documentation that:
RUNTIME:
Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
Which force the #Where annotation to be recorded in the class file by the compiler and retained by the VM at run time, so it will be applied all over this class.
I have 3 entities in a Hierarchy like this:
MyInterface
|
-----------------
| |
Entity1 Entity2
The MyInterface is NOT mapped in Hibernate (because I am using implicit polymorphism strategy to map this inheritance)
And, in fact, if I launch a query like this one:
"FROM MyInterface"
It works just fine (because it retrieves all the instances of Entity1 and all the instances of Entity2, puts them together, and returns a List<MyInterface>).
If we look at the SQL generated by Hibernate, it is launching 2 independent SQL queries to first retrieve the Entity1 instances an another one to retrieve Entity2 instances, but I am fine with this.
The BIG problem comes when you try to do something like this:
"FROM MyInterface ORDER BY someField"
Because it is applying the ORDER BY to the first SQL query and then the same ORDER BY to the second SQL query, instead of apply them to the WHOLE query (I know this because I can see the native SQL queries launched by Hibernate).
This is clearly a missbehaviour of Hibernate.
How can I work around this to force Hibernate to apply the ORDER BY to the whole query? (I cannot do it in memory because later I will have to add pagination also).
I'd say the problem is that Hibernate has to create those 2 SQL queries because you have to read from 2 tables.
I'm not sure if reading from 2 tables and ordering by 2 columns (one from each table) in one query is possible in plain SQL (means no vendor specific extensions), and if not, Hibernate would have to do the ordering in memory anyways.
What you could do when applying paging is: read the ids and the values you want to sort by only (not the entire entity), then sort in memory and read the entire entity for all ids contained in the page. For paging to be consistent you might have to store the results of that initial query (id + order criteria) anyways.
The way you are thinking cannot be mapped to SQL as is. Suppose you have Entity1 with fields field1A, field1B ... and Entity2 with fields field2A, field2B, ... Now you want the following query to be executed:
SELECT Entity1.* FROM Entity1
UNION
SELECT Entity2.* FROM Entity2
ORDER BY CommonField
which is not possible in SQL world, as entities have different number of fields and different field types.
So you need to think about extracting common fields into separate table CommonEntity, converting your interface into standalone entity with with one-to-one mapping to Entity1 & Entity2 (see Table per subclass). Then SQL will look like:
SELECT * from CommonEntity LEFT OUTER JOIN Entity1 ON Entity1.refId = CommonEntity.id LEFT OUTER JOIN Entity2 ON Entity2.refId = CommonEntity.id
ORDER BY CommonField
Or you can create a view over your tables and introduce an artificial discriminator (discriminator is something which will "distinguish" IDs from different tables, which caused a problem in your solution) and then map an entity to this view (so we get Table per class hierarchy):
CREATE VIEW EntityAandEntityB AS
SELECT 'A' as discriminator, Entity1.ID, CommonField1, ... CommonFieldZ, Entity1.field1A, ... Entity1.field1N, NULL, NULL, ... NULL(M)
FROM Entity1
UNION
SELECT 'B' as discriminator, Entity2.ID, CommonField1, ... CommonFieldZ, NULL, NULL, ... NULL(N), Entity2.field2A, ... Entity2.field2M
FROM Entity2
ORDER BY CommonField1, ...
Other alternatives (e.g. mentioned by #UdoFholl which is also kind of "outer join" for EntityAandEntityB) will result 2 SQLs and thus there is no way to order the "whole" query, and scrolling is not possible.
Hibernate will do this for you if you use the following.
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractEntity implements MyInterface {
private int someField;
}
Then have your subclasses that do this
#Entity
#Table(name="entity_1")
public class EntityOne extends AbstractEntity {
private int someOtherField;
}
#Entity
#Table(name="entity_2")
public class EntityTwo extends AbstractEntity {
private int anotherSomeOtherField;
}
You should then be able to write a query like this to get a single union SQL query with the DB doing the ordering.
FROM AbstractEntity ORDER BY someField
This should be a simple one I hope.
I have an invoice and that invoice has a list of payments.
Using the Criteria API I am trying to return a list of invoices and their payment total. So, in SQL I want something like this:
SELECT i.*, (SELECT SUM(PMT_AMOUNT) FROM INVOICE_PAYMENTS p WHERE p.INVOICE = i.INVOICE) FROM INVOICES i
I can't for the life of me figure out how to achieve this with the Criteria API. Doing something like:
Criteria crit = session.createCriteria(Invoice.class)
criteria.setProjection(Projections.projectionList()
.add(Projections.sum("payements.paymentAmount").as("paymentTotal"))
Simply returns 1 row with the projected payment total for all invoices, which is actually what you'd expect, but this is as close as I can get.
Any help is greatly appreciated.
There is a way with Criteria to return a list of Invoices along with the total payments for that invoice.
In theory, the answer is that you can use a grouping property on a projection query to group the result into total payment by invoice. The second part is that you could use a transient "totalPayment" value on the Invoice and select the projection into the Invoice structure using a transformer. This would be easier than dealing with an ArrayList of different properties but would depend on what you needed to use the results for.
To demonstrate this, here is the important part of a small Invoice class:
public class Invoice{
private String name;
#Transient private int totalPayments;
#OneToMany Set<Payment> payments = new HashSet<Payment>();
// getters and setters
...
}
Then this is the criteria that you could use
Criteria criteria = session.createCriteria(Invoice.class)
.createAlias("payments", "pay")
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("id"))
.add(Projections.property("id"), "id")
.add(Projections.property("name"), "name")
.add(Projections.sum("pay.total").as("totalPayments")))
.setResultTransformer(Transformers.aliasToBean(Invoice.class));
List<Invoice> projected = criteria.list();
And this is the sql that is generated
Hibernate:
select this_.id as y0_,
this_.id as y1_,
this_.name as y2_,
sum(pay1_.total) as y3_
from invoice this_
inner join invoice_payment payments3_ on this_.id=payments3_.invoice_id
inner join payment pay1_ on payments3_.payments_id=pay1_.id
group by this_.id
I'm pretty sure you can't return entities in a Projection.
There are two possibles:
Run two criteria queries, one for the actual invoices and one for there totals
Use HQL to perform the query
I haven't tested this but it should go something like:
select i, (select sum(p.amount) from InvoicePayments p where p.invoice = i.invoice) from Invoice i
Will have to wait until tomorrow, I have a very similar data structure at work I should be able to test this then.
You can also use #Formula for the totalPayments field. Disadvantage is, that the "sum" is computed every time you load the entity. So, you may use LAZY #Formula - do build time enhancement or Pawel Kepka's trick: http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html Disadvantage is, that is you have more LAZY #Fromula and you hit just one of them, all of them are loaded. Another solution may be to use #MappedSuperclass and more subclasses. Each subclass may have different #Formula fields. And one more solution beside DB view: Hibernate #Subselect.