JpaRepository: Spring Sort for runtime query variabels - sql

There is my plain sql query:
SELECT id, title,
IF(o.fixed_on_top = 1 AND o.fixing_on_top_day_counter > 5, 1 ,0) actual_fixed_on_top
FROM orders o
ORDER BY actual_fixed_on_top DESC, publication_date DESC;
How can I perform sorting like this on JpaRepository?
Maybe using Criteria API?
But I dint find any examples..
Thanks!
EDIT:
ok, studied different ways, and convinced that it cant be implemented through Criteria API (but it is a pity)
One working variant: Native SQL Query, i am sure.

You can do it by using the QueryDslPredicateExecutor which provides the following two method:
Iterable<T> findAll(Predicate predicate, OrderSpecifier<?> ... orderSpecifiers);
Page<T> findAll(Predicate predicate, Pageable pageable);
The PageRequest class implements Pageable and allows you to specified the sorting you want.
You could also add a Pageable parameter in a #Query annotated method on a regular JpaRepository and Spring Data Jpa will do the rest, for example:
#Query("select e from SomeEntity e where e.param1 = :param1")
public Page<SomeEntity> findSome(#Param("param1") String param1, Pageable pageable);

(sorry cannot comment yet)
I recently had the same issue.
The only solution worked for me, is to specify the order on the relationship.
I don't see a relationship in your query
example:
#OneToMany
#OrderBy("date")
...

Spring data jpa does understand order by see doc
findByAgeOrderByLastnameDesc() will be parsed into where x.age = ?1 order by x.lastname desc clause.
I see your case a bit complicated, but you can do JQPL with #Query annotation, Sample:
#Query("SELECT o FROM Order o WHERE write your clause ORDER BY o.something desc")
public Order findByCustomPK(#Param("paramIfNeeded"));

I found the solution:
public class MyEntitySpecifications {
public static Specification<MyEntity> GetByPageSpecification() {
return new Specification<MyEntity>() {
#Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Expression fixingExpr = cb.greaterThan(root.get(MyEntity_.fixingDueDate), new DateTime(DateTimeZone.UTC));
cq.orderBy(new OrderImpl(cb.selectCase().when(fixingExpr, 1).otherwise(0), false));
return cb...;
}
};
}
}
The main idea is using case expression instead of simple logical.
plain sql equuivalent is:
select ...
from my_entity e
where ...
order by case when e.fixing_due_date > now() then 1 else 0 end desc
So we can build dynamic queries with criteria api specifications as well.
No direct access to EntityManager, no plain sql.

Related

How does sorting work with limit in kotlin exposed model?

I have following snippet of code:
UserDataModel
.find {
UserDataTable.type eq type and (
UserDataTable.userId eq userId
)
}
.limit(count)
.sortedByDescending { it.timestamp }
sortedByDescending is a part of kotlin collections API. The my main concern is: how does exposed lib return top (according to timestamp) count rows from table if select query looks like this and does not contain ORDER BY clause?
SELECT USERDATA.ID, USERDATA.USER_ID, USERDATA.TYPE,
USERDATA.PAYLOAD, USERDATA."TIMESTAMP"
FROM USERDATA
WHERE USERDATA.TYPE = 'testType'
and USERDATA.USER_ID = 'mockUser'
LIMIT 4
And is it possible that sometimes or somehow returned result would be different for the same data?
Really struggled here. Thank you in advance.
You're sorting the results after query has been executed.
You need to use orderBy method as described in docs
UserDataModel
.find {
UserDataTable.type eq type and (UserDataTable.userId eq userId)
}
.limit(count)
.orderBy(UserDataTable.timestamp to SortOrder.DESC)

SQL 'comment' that can be read in a sql profiler

I've tried several methods such as using double hyphens, i.e. --THIS IS A COMMENT but when the executed sql is read in a profiler the comment is stripped out leaving only raw SQL that is being performed.
I want to do this to enable rapid identification of queries and their origins when looking at a SQL Profilers output that has over 8000 entries per minute,
so something like
--Method signature and an application name
e.g.
--MyMethod(string username) in MyFunkyAppName.
I'm using EntityFramework 4.3 which complicates things even further with linq to entities and a smattering of linq to sql thrown in for good measure.
EDIT: I'm aware of solutions to add a dodgy where clause or use anonymous properties to identify things such as Clever tricks to find specific LINQ queries in SQL Profiler but I'm hoping for a far less hacky approach or perhaps a generic one.
Here is an extension method you can use to tag your Entity Framework queries. It uses the WHERE clause, but shouldn't impair performance.
public static class ExtensionMethods
{
public static IQueryable<T> SetQueryName<T>(this IQueryable<T> source,
[CallerMemberName] String name = null,
[CallerFilePath] String sourceFilePath = "",
[CallerLineNumber] Int32 sourceLineNumber = 0)
{
var expr = Expression.NotEqual(Expression.Constant("Query name: " + name), Expression.Constant(null));
var param = Expression.Parameter(typeof(T), "param");
var criteria1 = Expression.Lambda<Func<T, Boolean>>(expr, param);
expr = Expression.NotEqual(Expression.Constant($"Source: {sourceFilePath} ({sourceLineNumber})"), Expression.Constant(null));
var criteria2 = Expression.Lambda<Func<T, Boolean>>(expr, param);
return source.Where(criteria1).Where(criteria2);
}
}
Here is how to use it:
context.Table1.SetQueryName().Where(x => x.C1 > 4)
It will use the calling method name as the query name.
You can specify another name like this:
context.Table1.SetQueryName("Search for numbers > 4").Where(x => x.Number > 4)
Here is how the SQL will look like:
SELECT
[Extent1].[Number] AS [Number]
FROM (SELECT
[Table1].[Number] AS [Number]
FROM [dbo].[Table1] AS [Table1]) AS [Extent1]
WHERE
(N'Query name: Search for numbers > 4' IS NOT NULL)
AND
(N'Source: C:\Code\Projects\MyApp\Program.cs (49)' IS NOT NULL)
AND ([Extent1].[Number] > 4)

GORM / HQL - LISTAGG

in GORM (using grails) i need to combine in subselect multiple results into one value. (result of this subselect will be concatenated value on which i can make search / sort etc. ...)
Query is writen as HQL. Something like this in oracle
http://www.techonthenet.com/oracle/functions/listagg.php
Can be something like that achieved in HQL (e.g. GORM) ?
...
AND (
SELECT LISTAG(e.title) AS con FROM Entity e
WHERE Entity.searchKey = TRUE
AND e.parrent = par
AND LOWER(e.title) LIKE :search
) > 0
...
ORDER BY con ASC
thansk
Hibernate, and the HQL/GORM layers that sit on top of Hibernate, do not directly support database-specific functions like Oracle's LISTAGG(). There are, however, a few ways to use native SQL within Grails. If you would like to add your concatenated value to one of your domain objects, you can use GORM's derived property feature (http://grails.org/doc/latest/guide/GORM.html#derivedProperties).
Something along these lines:
class MyDomain {
Long parentId
String titleAgg
static mapping = {
titleAgg formula: '(SELECT LISTAGG(e.title) FROM Entity e WHERE e.parrent = parent_id)'
}
}
A second option would be to use the Grails-defined dataSource bean along with groovy.sql.Sql to execute native SQL statements. See here for an example.

Selecting specific columns using linq: What gets transferred?

I refer to this example: Return selected specified columns
Quote:
If BlobDetails isn't the LINQ entity, then you can do it directly:
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new BlobDetails {
Id = b.Id, Size = b.Size,
Signature = b.Signature, RowVersion = b.RowVersion};
return qry.ToList();
I see that they are selecting specific column in a query through the ORM-tool LINQ TO SQL.
Critics of ORM-tools say that, if I remember correctly, that ORM-tools select and return entire objects from the table, and limits the options of selecting only specific columns as one can do through classic SQL-programming. Of course, I have my doubts about that when I see this example, but nevertheless, I still keep asking myself the question: Does the database return only the selected columns, or does it return the entire objects, leaving the column-filtering to the ORM-tool?
From this example, they also have a class called Blobdetails:
public class BlobDetails
{
public int Id { get; set; }
public string Signature { get; set; }
public int Size { get; set; }
public System.Data.Linq.Binary RowVersion { get; set; }
}
Do I need to create my own classes everytime I only wish to select a few columns from a table through LINQ?
You don't need to create new classes to select few columns from a table. You can use anonymous types for that.
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new { b.Id, b.Size, b.Signature, b.RowVersion};
return qry.ToList();
Only selected columns are transferred. There is no difference between using plain SQL and using LINQ to SQL. When you are executing LINQ query, it is converted to plain SQL and executed. Then result is mapped to your objects.
You can use SQL Server Profiler to see what query was generated and executed on server. Also you can use LINQPad to see what SQL will be generated from your query. In your case query will be same either you use BlobDetails or anonymous object:
SELECT [t0].[Id], [t0].[Size], [t0].[Signature], [t0].[RowVersion]
FROM [Blobs] AS [t0]
ORDER BY [t0].[RowVersion] DESC
when you do projections LINQ does indeed only select those columns and there is nothing preventing you from materializing it however you want. So in your example code
select new BlobDetails
{
Id = b.Id,
Size = b.Size,
Signature = b.Signature,
RowVersion = b.RowVersion
};
Only b.id, b.size, b.signature, & b.rowversion are selected. You can verify this with sql profiler or your debugger, I seem to recall there is also a function you can call on the datacontext to get the last query that was ran.
I think that the answer to your first question is already in the POST you mentioned. However...
If your BlobDetails is not LINQ entity you can simply use it in your select statement to define (shrink) your projection attributes. For example:
var qry = from b in dc.Blobs
select new BlobDetails { Id = b.Id, Size = b.Size }
would compile to SQL query like SELECT Id, Size FROM Blob ....
But if BlobDetails is LINQ entity you will need to use that AsEnumerable() hack otherwise you will get NotSupportedException: Explicit construction of entity type in query is not allowed.
var qry = from b in dc.Blobs.AsEnumerable()
select new BlobDetails { Id = b.Id, Size = b.Size }
Edit
As #Chris Pitman stated in his comment this AsEnumerable() approach could create serious bottleneck, beacause the whole table would be loaded in memory before applying the projection. So it is not recommended!
To your second question:
You will need to create custom class for objects that you want use easily outside the scope of the method. Properties of an anonymous object are visible only in the scope, where they have been declared and anonymous objects can be cast only to type object.
So if you want to return anonymous objects from method the return type would has to be an enumerable of object or dynamic as #xeondev stated in his comment.
There's no need to create your own classes, you can return an anonymous type. You can write something like this
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new {
Id = b.Id, Size = b.Size,
Signature = b.Signature, RowVersion = b.RowVersion};
return qry.ToList();
Although the signature of the method should look to something like this
public IEnumerable<object> GetItems()
or
public dynamic GetItems()
So if you are going to use the result of linq query in outer scope like you example suggest, it is highly recommended you create your own classes.

Fetch Single Item Using DataContext

I'm doing the following:
public MyItem FetchSingleItem(int id)
{
string query = "SELECT Something FROM Somewhere WHERE MyField = {0}";
IEnumerable<MyItem> collection = this.ExecuteQuery<MyItem>(query, id);
List<MyItem> list = collection.ToList<MyItem>();
return list.Last<MyItem>();
}
It's not very elegant really and I was hoping there's something a little better to get a single item out using DataContext. I'm extending from DataContext in my repository. There's a valid reason why before you ask, but that's not the point in this question ;)
So, any better ways of doing this?
Cheers
If it is SQL Server, change your SQL to:
SELECT TOP 1 Something FROM Somewhere ...
Or alternatavely, change these lines
List<MyItem> list = collection.ToList<MyItem>();
return list.Last<MyItem>();
into this one:
return collection.First();
myDataContext.MyItem.Where(item => item.MyField == id)
.Select(item => item.Something)
.FirstOrDefault();
The record returned is undefined, since you have no ORDER BY. So it's hard to do an exact translation. In general, though, reverse the order and take the First():
var q = from s in this.Somewhere
where s.MyField == id
orderby s.Something desc
select s.Something;
return q.First();
Relational tables are unordered. So if you don't specify the record you want precisely, you must consider the returned record as randomly selected.