I want to ignore default Join Restriction in createAlias. I have a OnetoOne relationship.
My Problem is Hibernate generates default restriction for the join relationship.
Pojo
Note : No column for diagnosticTemplate in charge table.
Charge.java
#OneToOne(mappedBy = "charge")
private DiagnosticTemplate diagnosticTemplate;
DiagnosticTemplate.java
#OneToOne
#JoinColumn(name = "charge")
#Exclude
private Charge charge;
Query
select
*
from
charges c
inner join diagnostic_template dt
on (dt.charge = c.id and dt.status=1) or (dt.status=0)
Criteria
Criteria criteria = getSession().createCriteria(Charge.class, "charge")
.createAlias("charge.diagnosticTemplate", "diagnosticTemplate",
JoinType.INNER_JOIN,
Restrictions.or(
Restriction.and(Restrictions.eqProperty("charge.id",
"diagnosticTemplate.charge"),
Restrictions.eq("diagnosticTemplate.status",true)),
Restrictions.eq("diagnosticTemplate.status",false) ))
Hibernate Query
select
*
from
charges c
inner join diagnostic_template dt
on dt.charge = c.id and (dt.charge = c.id and dt.status=1) or (dt.status=0)
How to avoid this condition? or anything wrong with my relationship?
Please help..!
When you join a charge with charge.diagnosticTemplate, it means that Hibernate will try to lookup a DiagnosticTemplate that is linked with this charge. Thus, the generated condition dt.charge = c.id does make sense.
Remember that you have define the relationship using foreign key. So, soft relation like dt.status=0 cannot be understood by Hibernate.
If you still wish to achieve you query, you can consider joining the two instance indirectly (not using association path). Refer to How to join Multiple table using hibernate criteria where entity relationship is not direct?.
Related
I'm fighting this whole day and I can't figure it out. I'm JPA beginner, so for now Criteria API is a nightmare for me. The problem:
I have 3 entities: Policy, Customer, Insurer. Policy has references to a Customer and an Insurer (eager fetch here). Customer has list of policies, so do Insurer (lazy fetch here).
I'm trying to find all policies in a way described below (SQL):
SELECT * FROM POLICY as p WHERE
p.CUSTOMER_ID IN (SELECT ID FROM CUSTOMER as c WHERE [customerPredicates])
AND
p.INSURER_ID IN (SELECT ID FROM INSURER as i WHERE [insurerPredicates])
AND
[policyPredicates]
Where CUSTOMER_ID / INSURER_ID are JoinColumns generated from #ManyToOne relationships in Policy.
customerPredicates / insurerPredicates / policyPredicates are lists of predicates ('where' conditions prepared from given search criteria).
How can I achieve that in Criteria API? What are rules / good practices for creating this kind of queries?
Try to rewrite the SQL first to use JOINS
SELECT *
FROM POLICY as p
INNER JOIN CUSTOMER as c ON p.CUSTOMER_ID = c.ID
INNER JOIN INSURER as i ON p.INSURER_ID = i.ID
WHERE [customerPredicates])
AND
[insurerPredicates])
AND
[policyPredicates]
The all you need is criteria api is to get main criteria (for the Policy entity) and create aliases for Customer entity and Insurer entity.
Criteria criteria = session.createCriteria(Policy.class, "p");
criteria.setFetchMode("p.Customer", FetchMode.JOIN);
criteria.createAlias("p.Customer", "c");
and add your restriction to the "c" alias for Customer.
And the same for Insurer
Let's say I have the following tables:
Locations
================
LocationKey, LocationName
Employees
================
EmployeeKey, FirstName, LastName
EmployeeLocationXRef
================
EmployeeLocationXRefKey, LocationKey, EmployeeKey
TimeSheets
=================
TimeSheetKey, EmployeeLocationXRefKey, Minutes
OK, so as you can see, in order for me to get all of the TimeSheets for a Location and Employee, I can do the following in SQL
select
*
from TimeSheets ts
join EmployeeLocationXRef ex on (ex.EmployeeLocationXRefKey = ts.EmployeeLocationXRefKey)
join Locations l on (l.LocationKey = ex.LocationKey)
join Employees e on (e.EmployeeKey = ex.EmployeeKey)
where
l.LocationKey = 'xxxx'
and e.EmployeeKey = 'yyyy'
But I have tried every way I can think of to map with (with annotations) in JPA using #JoinTable, etc.
Any ideas how this could be done? Unfortunately, this is a legacy system and the schema cannot be changed.
EDIT
Forgot to mention the EmployeeLocationXRef entity has not been directly mapped. Which could be the real problem. I will see about creating that entity mapping and see if it makes it easier.
I don't see anything particularly difficult or strange about this schema. There should simply be one entity per table and (unless there are unique constraints):
a ManyToOne between EmployeeLocationXRef and Location, using a JoinColumn
a ManyToOne between EmployeeLocationXRef and Employee, using a JoinColumn
a ManyToOne between Timesheet and EmployeeLocationXRef, using a JoinColumn
(these associations can of course be made bidirectional, or be in the other direction if you prefer so).
The JPQL query would simply be
select ts from Timesheet ts
join ts.employeeXRef ex
join ex.location l
join ex.employee e
where
l.locationKey = 'xxxx'
and e.employeeKey = 'yyyy'
which is a straightforward translation of the SQL query.
I am learning EJB,JPA and have very basic doubt
I have 3 Tables A,B,C
A - ID, Name
B - ID, Name
C - ID, A_ID, B_ID
When i create a Entity Class from Database, i get 3 EJB classes with JPA stuff.
Now in my Managed bean i get either A.Name or B.Name and i need to find the matching entries using C.
Normal SQL Query will look like (may not be the best query)
SELECT a.name FROM schema.A a, schema.B b, schema.C c where b.Name='ABC' and c.B_ID=b.ID and a.ID = c.A_ID;
Now where do i do the above query in my classes.
I came across #SecondaryTable but could not understand how exactly its used.
I also saw em.createQuery( SQL query).getResultList().
Now is the above the best way or is there something available in EJB/JPA which is better.
UPDATE 1:
I was trying to execute the query in em.CreateQuery
em.CreateQuery(SELECT a.name FROM A a, B b, C c where b.Name='ABC' and c.B_ID=b.ID and a.ID = c.A_ID).getResultList();
but i get following error in my GlassFish Server (i am using EclipeLink JPA)
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
Position:
Error Code: 0
Call: SELECT t0.given_name FROM test.a t3, test.a_b t2, test.b t1, test.a t0 WHERE ((((t1.Name = ?) AND (t2.b_id = t1.id.t1.id)) AND (t0.id = )) AND (t3.id = t2.a_id))
bind => [1 parameter bound]
Query: ReportQuery(referenceClass=Consultant sql="SELECT t0.given_name FROM test.a t3, test.a_b t2, test.b t1, test.a t0 WHERE ((((t1.Name = ?) AND (t2.b_id = t1.id.t1.id)) AND (t0.id = )) AND (t3.id = t2.a_id))")
Now why is the SQl statement is messed up in the error log
1.There is an extra entry test.a t0
2.t2.b_id = t1.id.t1.id
3.t0.id=
How does the error log SQL statement gets generated.
As C is a many to many relationship between A and B it shouldn't be an entity. However I don't think JPA likes it that your join table (C) has it's own ID column. If possible remove the ID column from C and make the combination of A_ID, B_ID your primary key.
Then the entity class A could have:
#JoinTable(name = "C",
joinColumns = { #JoinColumn(name = "A_ID", referencedColumnName = "ID") },
inverseJoinColumns = { #JoinColumn(name = "B_ID", referencedColumnName = "ID") })
#ManyToMany
private Collection<B> bCollection;
I think it is clear what all those annotations mean.
Class B would have:
#ManyToMany(mappedBy = "bCollection")
private Collection<A> aCollection;
The mappedBy attribute tells JPA to use the JoinTable defenition of A::bCollection (A is deduced from the type Collection<A> of the field).
Now if you have an instance of A you can easily get all the B's for that A by getting the property. No need for any SQL/JPQL.
Now as for executing queries you should know that you have JPQL and SQL. JPQL is Java Persistence Query Language and is the language of JPA. SQL is the native language of your database. To execute SQL you need to use the createNativeQuery family of functions. The createQuery functions are for JPQL.
You should prefer JPQL over SQL. The most important difference between the two is that JPQL works on your entities and expect all identifiers to correspond to the names used for your classes and properties. For example if the Name column (first letter uppercase) is mapped to a property called name (lower case) then in a JPQL query you should use name (lower case). Same for entity class names and corresponding table names. JPQL also has build in support for the join table of the many to many relation ship the JPQL query for what you want would be
SELECT a
FROM B b JOIN b.aCollection a
WHERE b.name='ABC'
No need to specify all the join conditions JPA knows them from the annotations on your classes.
#SecondaryTable is not relevant here it is used when a single entity is split into more then one table.
I have multiple entities that are queried via JPA2 Criteria Query.
I am able to join two of these entities and get the result at once:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<LadungRgvorschlag> criteriaQuery = criteriaBuilder.createQuery(LadungRgvorschlag.class);
Root<LadungRgvorschlag> from = criteriaQuery.from(LadungRgvorschlag.class);
Join<Object, Object> ladung = from.join("ladung");
from.fetch("ladung", JoinType.INNER);
Then i try to join an additional table like that:
ladung.join("ladBerechnet");
ladung.fetch("ladBerechnet", JoinType.LEFT);
i get the following error:
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias3,role=null,tableName=ladberechnet,tableAlias=ladberechn3_,origin=ladungen ladung1_,columns={ladung1_.id ,className=de.schuechen.beans.tms.master.LadBerechnet}}] [select generatedAlias0 from de.schuechen.beans.tms.master.LadungRgvorschlag as generatedAlias0 inner join generatedAlias0.ladung as generatedAlias1 inner join generatedAlias1.ladBerechnet as generatedAlias2 left join fetch generatedAlias1.ladBerechnet as generatedAlias3 inner join fetch generatedAlias0.ladung as generatedAlias4 where ( generatedAlias0.erledigt is null ) and ( generatedAlias0.belegart in (:param0, :param1) ) and ( generatedAlias1.fzadresse in (:param2, :param3) ) and ( generatedAlias1.zudatum<=:param4 ) and ( 1=1 ) order by generatedAlias0.belegart asc, generatedAlias1.fzadresse asc, generatedAlias1.zudatum asc, generatedAlias1.zulkw asc]
How can i tell JPA/Hibernate, that it should select all the entities at once?
With JPA 'some dialects of JPA' you can chain join fetches, but I don't think you can/should do both a join and a join fetch.
For instance, if we have a Program that has a one-to-many relation to a Reward that has a relation to a Duration, the following JPQL would get a specific instance with the rewards and duration pre-fetched:
SELECT DISTINCT
program
FROM
Program _program
LEFT JOIN FETCH
_program.rewards _reward
LEFT JOIN FETCH
_reward.duration _duration
WHERE
_program.id = :programId
}
With the equivalent Criteria code:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Program> criteriaQuery = criteriaBuilder.createQuery(Program.class);
Root<Program> root = criteriaQuery.from(Program.class);
Fetch<Program, Reward> reward = root.fetch("rewards", JoinType.LEFT);
Fetch<Reward, Duration> duration = reward.fetch("duration", JoinType.LEFT);
criteriaQuery.where(criteriaBuilder.equal(root.get("id"), programId));
TypedQuery<program> query = entityManager.createQuery(criteriaQuery);
return query.getSingleResult();
Note that the intermediate variables reward and duration are not needed here, but they're just for informational purposes. root.fetch("rewards", JoinType.LEFT).fetch("duration", JoinType.LEFT) would have the same effect.
What it comes to JPA, you cannot chain join fetches in Criteria API queries (citation from specification):
An association or attribute referenced by the fetch method must be
referenced from an entity or embeddable that is returned as the result
of the query. A fetch join has the same join semantics as the
corresponding inner or outer join, except that the related objects are
not top-level objects in the query result and cannot be referenced
elsewhere by the query.
And it is also not supported in JPQL queries:
The association referenced by the right side of the FETCH JOIN clause
must be an association or element collection that is referenced from
an entity or embeddable that is returned as a result of the query.
It is not permitted to specify an identification variable for the
objects referenced by the right side of the FETCH JOIN clause, and
hence references to the implicitly fetched entities or elements cannot
appear elsewhere in the query.
With HQL it seems to be possible: Hibernate documentation EclipseLink does not provide such a extension, so syntax of following query is accepted by Hibernate, but not by EclipseLink:
SELECT a FROM A a LEFT JOIN FETCH a.bb b LEFT JOIN FETCH b.cc
In EclipseLink same can be done via query hints.
I have a question using Hibernate Criteria, I need to convert this query using criteria.
SELECT * FROM A a_ INNER JOIN B b_ ON a_.column1=b_.column1 AND b_.column2 IN (X,Y) AND active='Y';
I need the result as table A.
I just solved this issue, here is my code
Criteria criteria = session.createCriteria(ProductOffer.class);
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
Date effDate = TypeConvertUtil.toDate(param.get("effDate"));
criteria.add(Restrictions.le("effDate", effDate));
criteria.add(Restrictions.gt("expDate", effDate));
criteria.createAlias("productOfferPropDefs", "def",JoinType.LEFT_OUTER_JOIN);
criteria.setFetchMode("productOfferPropDefs", FetchMode.JOIN);
criteria.add(Restrictions.le("def.effDate", effDate));
criteria.add(Restrictions.gt("def.expDate", effDate));
criteria.createAlias("def.productOfferProps", "prop",JoinType.LEFT_OUTER_JOIN);
criteria.setFetchMode("def.productOfferProps", FetchMode.JOIN);
criteria.add(Restrictions.le("prop.effDate", effDate));
criteria.add(Restrictions.gt("prop.expDate", effDate));
productOfferList = criteria.list();
Please beware that
criteria.createAlias("productOfferPropDefs", "def",JoinType.LEFT_OUTER_JOIN);
this parameter is important:
JoinType.LEFT_OUTER_JOIN
if you did not use it, and your relation is one-to-many, it will hit the 1:N classic issue for hibernate
If the associations are defined, see http://docs.jboss.org/hibernate/core/3.3/reference/en/html/querycriteria.html#querycriteria-associations
In case associations are not specified in the entities definition, you can't use criteria.
You can use HQL to do inner joins (need to write in implicit join notation), for doing left joins you have to use native SQL.