I have a query with a self join that looks like this,
select t1., t2. from table t1
left outer join table t2 on t2.LFT < t1.LFT
and t2.RGT > t1.RGT
AND t2.REG_CODE_PAR = 'ALL'
AND t1.STATUS_CODE = 'A'
AND t2.STATUS_CODE = 'A'
I'm using #NamedNativeQuery with a result set mapping to get the result.
#NamedNativeQuery(
name="findTree",
query="..... the query above",
resultSetMapping = "regionT")
With the following result set mapping
#SqlResultSetMapping(name = "regionT" , entities ={
#EntityResult(
entityClass = Tree.class
fields = {
#FieldResult(name = "regCode", column = "REG_CODE")
#FieldResult(name = "rgt", column = "RGT"),
#FieldResult(name = "lft", column = "LFT"),
#FieldResult(name = "name", column = "NAME"),
#FieldResult(name = "regCodePar", column = "REG_CODE_PAR"),
#FieldResult(name = "statusCode", column = "STATUS_CODE")
}
),
#EntityResult(
entityClass = TreeSelf.class
fields = {
#FieldResult(name = "regCode1", column = "REG_CODE")
#FieldResult(name = "rgt1", column = "RGT"),
#FieldResult(name = "lft1", column = "LFT"),
#FieldResult(name = "name1", column = "NAME"),
#FieldResult(name = "regCodePar1", column = "REG_CODE_PAR"),
#FieldResult(name = "statusCode1", column = "STATUS_CODE")
}
)
})
The entity class contains looks like this.
#NamedNativeQuery(...)
#SqlResultSetMapping(...)
#Entity
#Table(name = "table")
public class Tree implements Serializable {
#Id
#Column(name = "REG_CODE")
private String regCode; ... ..getters and setters...}
When I run the query using em.createQuery("findTree"), I get the exact same object in both
the 1st and 2nd elements of the returned object array.
Even if I create a class called TreeSelf that is identical to Tree and use it as the 2nd
EntityResult instead of having 2 EntityResults using the same entityClass, I get the same
result.
Can someone point out what's wrong with the configuration?
Let's see if I understand your question. You're expecting to capture two Tree entities from each native query result row. The first entity should be formed from t1's columns. The second entity should be formed from t2's columns. Contrary to expectation, you actually receive two instances formed from t1. No instances from t2 appear. You made a doppelganger Entity for Tree called TreeSelf while debugging, but TreeSelf is ultimately unnecessary and you want to get rid of it. Stop me if any of that was wrong.
I think the problem is due to ambiguous column names. Each column name in the native query appears twice, once from t1 and once from t2. The result mapper seems to be arbitrarily picking the first occurrence of each ambiguous column name for both Tree entities. I'm surprised that works at all. I would have expected an SQLException complaining about column reference ambiguity.
Also, are you sure you want a left outer join? What if no match is found for a t1 row? It will be paired with all NULL in t2's columns. Then you have a null-valued Tree entity. I think. I don't even know what the result mapper would do in that case. Perhaps you want an inner join?
Consider translating this native query into a JPQL query. (JPA Criteria API is just as well, but I find it more cumbersome for examples.) Here's a JPQL version of the native query:
SELECT t1, t2
FROM Tree t1, Tree t2
WHERE t2.lft < t1.lft AND t2.rgt > t1.rgt AND t2.regCodePar = 'ALL' AND
t1.statusCode = 'A' AND t2.statusCode = 'A'
N.B.: This changes the join semantics to inner instead of left outer.
Here's a sketch of code that could run this query:
EntityManager em = ... // EntityManager by injection, EntityManagerFactory, etc.
String jpql = ... // Like example above
TypedQuery<Object[]> q = em.createQuery(jpql, Object[].class);
for (Object[] oa : q.getResultList()) {
Tree t1 = (Tree)oa[0];
Tree t2 = (Tree)oa[1];
}
In case you are stuck with the native query for whatever reason, here's how you can work around the column name ambiguity. Instead of starting the native query like select t1.*, t2.*, alias each column with AS. The SELECT clause would resemble this:
SELECT t1.REG_CODE AS t1_REG_CODE, t1.RGT AS t1_RGT, (... rest of t1 cols ...),
t2.REG_CODE AS t2_REG_CODE, t2.RGT AS t2_RGT, (... rest of t2 cols ...)
The column attribute in each FieldResult must change accordingly. So the column attributes under the first EntityResult should all start with t1_ and the second's should all start with t2_.
I'd humbly recommend deleting the native query and sql result mapper and using JPA Query Language or Criteria API, if you can find a way.
Update: As confirmed in your comments, a useful answer to your question must preserve left (outer) join semantics. Unfortunately, JPQL and the Criteria API don't support complex left join conditions. There is no way to qualify a JPQL left join with an explicit ON condition.
To my knowledege, the only way to do a left outer join under the spec is by traversing an entity relationship. The JPA implementation then generates an ON condition that tests identity equality. The relevant spec bits are 4.4.5 "Joins" and 4.4.5.2 "Left Outer Joins".
To satisfy this constraint, each Tree you want to left-join to its ultimate parent must have an additional column storing the ultimate parent's id. You might be able to cheat around this constraint in a variety of ways (views?). But the path of least resistance seems to be modifying the native query to use aliased arguments, deleting TreeSelf, and updating the result mapper accordingly. Cleverer solutions welcome, though...
Related
I need to convert a certain query to JPA using CriteriaBuilder. The query looks like this:
SELECT *
FROM ENTITY1 a
INNER JOIN (
SELECT *
FROM ENTITY2 b
where b.STARTDATE = '2019-10-31T00:00:00.000+0100'
) ON ENTITY2.ID = ENTITY1.ENTITY2_ID
Before you ask: for the sake of performance, I wish to avoid converting the query to:
SELECT *
FROM ENTITY1 a
INNER JOIN ENTITY2 b ON ENTITY2.ID = ENTITY1.ENTITY2_ID
where b.STARTDATE = '2019-10-31T00:00:00.000+0100'
Indeed, both ENTITY1 and ENTITY2 contain such a huge lot of rows, even with the right indexes, executing the latter version of the query takes an unacceptable amount of time.
Now, I'm at a loss how to implement the former version with JPA. Any hint would be appreciated!
I use spring data jpa with native query
I have already some query like this
How to use native query none parameter.
String q="SELECT t1.blockNumber-1 FROM someTAble t1 LEFT JOIN someTAble t2 ON t2.blockNumber = t1.blockNumber-1 WHERE t2.blockNumber IS NULL AND t1.blockNumber> 0 ORDER BY t1.blockNumber";
#Query(value = q,nativeQuery = true)
List<Entity> findByBlockNumberIs();
they are occur errors Column 'sequence' not found.
That query means are when i insert some Contiguous data int value then i find missing data.
But
this query working
SELECT *,t1.blockNumber-1 FROM someTAble t1 LEFT JOIN someTAble t2 ON t2.blockNumber = t1.blockNumber-1 WHERE t2.blockNumber IS NULL AND t1.blockNumber> 0 ORDER BY t1.blockNumber
The difference between the two queries is whether there is a '*' or not
how to change simple to my query.
How to i changed error
OR How to use spring data jpa predicate
QEntity qBe1= QEntity .blockEntity;
QEntity qBe2= QEntity .blockEntity;
build.and(qBe2.blockNumber.eq(be.getBlockNumber()-1))
.and(qBe2.blockNumber.isNull().and(qBe1.blockNumber.gt(0)));
is predicate can use left join?
well...
use this.
List<Integer> findByBlockNumber()
I have this problem and reproduced it with AdventureWorks2008R2 to make it more easy. Basically, I want to filter a parent table for a list of IN values and I thought it would generate this type of query
but it doesn't.
SELECT * FROM SalesOrderDetail where EXISTS( select * from SalesOrderHeader where d.id=h.id and rowguid IN ('asdf', 'fff', 'weee' )
Any ideas how to change the LINQ statement to query Header only once?
(ignore the fact I'm matching on Guids - it will actually be integers; I was just quickly looking for a 1-1 table in EF because that's when the problem occurs and I happened to find these)
var guidsToFind = new Guid[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()};
AdventureWorks2008R2Entities context = new AdventureWorks2008R2Entities();
var g = context.People.Where(p => guidsToFind.Contains(p.BusinessEntity.rowguid)).ToList();
That produces the following more expensive query:
SELECT [Extent1].[BusinessEntityID] AS [BusinessEntityID],
[Extent1].[PersonType] AS [PersonType],
[Extent1].[NameStyle] AS [NameStyle],
[Extent1].[Title] AS [Title],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[MiddleName] AS [MiddleName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Suffix] AS [Suffix],
[Extent1].[EmailPromotion] AS [EmailPromotion],
[Extent1].[AdditionalContactInfo] AS [AdditionalContactInfo],
[Extent1].[Demographics] AS [Demographics],
[Extent1].[rowguid] AS [rowguid],
[Extent1].[ModifiedDate] AS [ModifiedDate]
FROM [Person].[Person] AS [Extent1]
INNER JOIN [Person].[BusinessEntity] AS [Extent2] ON [Extent1].[BusinessEntityID] = [Extent2].[BusinessEntityID]
LEFT OUTER JOIN [Person].[BusinessEntity] AS [Extent3] ON [Extent1].[BusinessEntityID] = [Extent3].[BusinessEntityID]
WHERE [Extent2].[rowguid] = cast('b95b63f9-6304-4626-8e70-0bd2b73b6b0f' as uniqueidentifier) OR [Extent3].[rowguid] IN (cast('f917a037-b86b-4911-95f4-4afc17433086' as uniqueidentifier),cast('3188557d-5df9-40b3-90ae-f83deee2be05' as uniqueidentifier))
Really odd. Looks like a LINQ limitation.
I don't have a system to try this on right now but if you first get a list of BusinessEntityId values based on the provided guids and then get the persons like this
var g = context.People.Where(p => businessEntityIdList.Contains(p.BusinessEntityId)).ToList();
there should not be a reason for additional unnecessary joins anymore.
If that works, you can try to combine the to steps into one LINQ expression to see if the separation stays intact.
A colleague of mine has the following (apparently invalid) JPQL query:
SELECT NEW com.foobar.jpa.DonationAllocationDTOEntity(a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund)
FROM DonationAllocation a JOIN a.donation d JOIN a.allocationType t
JOIN FETCH a.campaign
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')
It is worth noting (for later in this message) that DonationAllocation's relationship with a Campaign entity is many-to-one, and is marked as FetchType.LAZY. My colleague's intent with this query is to (among other things) ensure that a.campaign is "inflated" (eagerly fetched).
Hibernate (obviously just one JPA implementation of several), when faced with this query, says:
query specified join fetching, but the owner of the fetched association was not present in the select list
This makes sense, as the select list contains only NEW DonationAllocationDTOEntity(), and section 4.4.5.3 of the JPA 2.0 specification says:
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.
So since there is no "entity or embeddable that is returned as a result of the query" (it's a DTO constructed using the NEW operator), it follows that there is no possible association for a FETCH JOIN to reference, and hence this query is invalid.
How, given this limitation, should one construct a JPQL query in this case such that a.campaign--passed into the constructor expression--is fetched eagerly?
I would simply select the entity and its association, and llopover the results to invoke the DTO constructor explicitely. You would have the additional advantage of compile-time checks and refactorable code:
select a from DonationAllocation a
JOIN a.donation d
JOIN a.allocationType t
JOIN FETCH a.campaign
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')
...
for (DonationAllocation a : list) {
result.add(new DonationAllocationDTOEntity(a.id,
a.campaign,
a.campAppeal,
a.campDivision,
a.divisionFund));
}
EDIT:
This query should also select what's needed, and avoid selecting the whole DonationAllocation entity:
select a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund
from DonationAllocation a
JOIN a.donation d
JOIN a.allocationType t
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')
and you might just add the DTO constructor in the query if you want:
select new com.foobar.jpa.DonationAllocationDTOEntity(a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund)
from DonationAllocation a
JOIN a.donation d
JOIN a.allocationType t
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')
The fact the a.campaign is in the select clause should be sufficient to tell Hibernate to load the entity. At least that's how it behaves in my tests.
I am looking at some EF examples and trying to decipher what 'Query Projection' exactly equates to when doing LINQ to Entities or EntitySQL. I believe it is when the query results are filtered and projected into an anonymous type but not 100% sure.
Can someone please define this and maybe provide a small L2E query that uses an example of it?
Projection is when the result of a query is output to a different type than the one queried. Another article defined it as : the process of transforming the results of a query
Projection can be to an anonymous type, but could also be to a concrete type. If you come from a SQL world, it is akin to the columns listed in your SELECT clause.
Example selecting a sub-set of an object into an concrete type:
ParentObj.Select(x=> new ParentSlim { ParentID = x.ParentID, Name = x.Name } );
.
Example merging to object into a 3rd anonymous type:
Note: the select new portion is the projection.
from P in ParentObj.AsQueryable()
join C in ChildObj.AsQueryable() on P.ParentID == C.ParentID
select new { // <-- look ma, i'm projecting!
ParentID = P.ParentID,
Name = P.Name,
SubName = C.Name
RandomDate = DateTime.UtcNow()
}