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()
}
Related
I am experiencing various things while studying JPA, but I am too unfamiliar with it, so I would like to get some advice.
The parts I got stuck in during my study were grouped into three main categories. Could you please take a look at the code below?
#Repository
public interface TestRepository extends JpaRepository<TestEntity,Long> {
#Query(" SELECT
, A.test1
, A.test2
, B.test1
, B.test2
FROM TEST_TABLE1 A
LEFT JOIN TEST_TABLE2 B
ON A.test_no = B.test_no
WHERE A.test3 = ?1 # Here's the first question
if(VO.test4 is not null) AND B.test4 = ?2") # Here's the second question
List<Object[] # Here's the third question> getTestList(VO);
}
First, is it possible to extract test3 from the VO received when using native sql?
Usually, String test1 is used like this, but I wonder if there is any other way other than this.
Second, if extracting is possible in VO, can you add a query in #QUERY depending on whether Test4 is valued or not?
Thirdly, if I use List<Object[]>, can the result of executing a query that is not in the already created entity (eg, test1 in TEST_TABLE2, which is not in the entity of TEST_TABLE1) can be included?,
First, is it possible to extract test3 from the VO received when using native sql? Usually, String test1 is used like this, but I wonder if there is any other way other than this.
Yes, it is possible.
You must use, eg where :#{[0].test3} is equals vo.test3
[0] is position the first param, past for method annotated with #Query
#Query(value = "SELECT a.test1, a.test2, b.test1, b.test2
FROM test_table1 a
LEFT JOIN test_table2 b ON a.test_no = b.test_no
WHERE a.test3 = :#{[0].test3}", nativeQuery = true)
List<Object[]> getList(VO);
Second, if extracting is possible in VO, can you add a query in #QUERY depending on whether Test4 is valued or not?
You can use a trick eg:
SELECT ... FROM table a
LEFT JOIN table b ON a.id = b.id
WHERE a.test3 = :#{[0].test3}
AND (:#{[0].test4} IS NOT NULL AND b.test4 = :#{[0].test4})
Thirdly, if I use List<Object[]>, can the result of executing a query that is not in the already created entity (eg, test1 in TEST_TABLE2, which is not in the entity of TEST_TABLE1) can be included?
Sorry, but I not understand the third question.
Maybe this tutorial will help you: https://www.baeldung.com/jpa-queries-custom-result-with-aggregation-functions
I am trying to use the Criteria API instead of constructing queries as JPQL Strings as the Criteria API seems much better suited for this. However, I am having a few problems understanding how to construct the two following statements.
SELECT e
FROM Subject e
WHERE e.company = :c1
OR e.company = :c2
OR e.company = :c3
In this case I need to iterate over an unknown number of values (c1, c2, c3 etc.) to match to the same attribute.
SELECT e
FROM Subject e
WHERE e.company
LIKE :c1
OR e.account
LIKE :c1
OR e.email
LIKE :c1
In this case I need to pass in a single value (c1) and have a 'LIKE' comparison done on a specific range of attributes.
My current pattern looks something like this:
// Criteria
CriteriaBuilder builder = subjectDAO.criteriaBuilder();
CriteriaQuery<Subject> query = builder.createQuery(Subject.class);
// Root
Root<Subject> subject = query.from(Subject.class);
List<Predicate> predicates = new ArrayList();
for (String property : map.keySet()) {
String value = (String) coreFilter.get(map);
predicates.add(????? This is where I come unstuck ?????);
}
// pass all the predicates into the query
query.where(predicates.toArray(new Predicate[predicates.size()]));
NB. I don't have any problems constructing the Query object or specifying the Root or Joins. I am just having problems with the specificity of the above queries. For the sake of clarity just assume that all the attributes are String and don't require any joins.
The expression CriteriaQuery<T> where(Predicate... restrictions), as you can see in the javadoc,
Modify the query to restrict the query result according to the conjunction of the specified restriction predicates.
So, it makes the conjunction of the predicates in the list, i.e. it concatenates them with AND expressions. In order to concatenate them with OR expression, simply use CriteriaBuilder#or(Predicate... restrictions)
query.where(builder.or(predicates.toArray(new Predicate[] {})));
I'm trying to replicate the following query usine Squeryl.
SELECT c.order_number,p.customer,p.base,(
SELECT sum(quantity) FROM "Stock" s where s.base = p.base
) as stock
FROM "Card" c, "Part" p WHERE c."partId" = p."idField";
I have the following code for selecting the Cards and Parts but I cannot see a way to add a sumation into the select clause.
from(cards, parts)((c,p) =>
where(c.partId === p.id)
select(c,p)
Any help is much appreciated!
In Squeryl, you can use any Queryable object in the from clause of your query. So, to create a subquery, something like the following should work for you:
def subQuery = from(stock)(s => groupBy(s.base) compute(sum(s.quantity)))
from(cards, parts, subquery)((c, p, sq) =>
where(c.partId === p.idField and sq.key === p.base)
select(c.orderNumber, p.customer, sq.measures))
Of course the field names may vary slightly, just guessing at the class definitions. If you want the whole object for cards and parts instead of the single fields from the original query - just change the select clause to: select(c, p, sq.measures)
I am trying to create an Entity SQL that is a union of two sub-queries.
(SELECT VALUE DISTINCT ROW(e.ColumnA, e.ColumnB, 1 AS Rank) FROM Context.Entity AS E WHERE ...)
UNION ALL
(SELECT VALUE DISTINCT ROW(e.ColumnA, e.ColumnB, 2 AS Rank) FROM Context.Entity AS E WHERE ...)
ORDER BY *??* LIMIT 50
I have tried:
ORDER BY Rank
and
ORDER BY e.Rank
but I keep getting:
System.Data.EntitySqlException: The query syntax is not valid. Near keyword 'ORDER'
Edit:
This is Entity Framework. In C#, the query is executed using:
var esql = "...";
ObjectParameter parameter0 = new ObjectParameter("p0", value1);
ObjectParameter parameter1 = new ObjectParameter("p1", value2);
ObjectQuery<DbDataRecord> query = context.CreateQuery<DbDataRecord>(esql, parameter0, parameter1);
var queryResults = query.Execute(MergeOption.NoTracking);
There is only a small portion of my application where I have to resort to using Entity SQL. Generally, the main use case is when I need to do: "WHERE Column LIKE 'A % value % with % multiple % wildcards'".
I do not think it is a problem with the Rank column. I do think it is how I am trying to apply an order by to two different esql statements joined by union all. Could someone suggest:
How to apply a ORDER BY to this kind of UNION/UNION ALL statment
How to order by the non-entity column expression.
Thanks.
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...