How do you specify multi-column OrderSpecifier for use in SpringData and QueryDsl? Is this possible - spring-data-jpa

So I have the following query below:
public Iterable<Dealer> findAll(Dealer dealer) {
QDealer qdealer = QDealer.dealer;
BooleanExpression where = null;
if(dealer.getId() != null && dealer.getId() != 0) {
buildPredicate(qdealer.id.goe(dealer.getId()));
}
OrderSpecifier<String> sortOrder = QDealer.dealer.dealerCode.desc();
Iterable<Dealer> results = dlrRpstry.findAll(where, sortOrder);
return results;
}
The query above works fine. However, I would like to sort the results by dealerType first, then by dealerCode somewhat like "order by dealerType asc, dealerCode desc". How do I instantiate the OrderSpecifier so that the results will be sorted by dealerType then by dealer code.
The DealerRepository dlrRpstry extends JpaRepository, QueryDslPredicateExecutor
I am using spring-data-jpa-1.1.0, spring-data-commons-dist-1.3.2 and querydsl-jpa-2.9.0.
If OrderSpecifier can not be configured to a multi-column sort order what would be the alternative solution that will satisfy my requirement of sorting the results "by dealerType asc, dealerCode desc".
Any help would be greatly appreciated. Thanks in advance.
Nick

I do not have JPA install setup, but I believe reading the QueryDslJpaRepository documentation, you can simply do this:
OrderSpecifier<String> sortOrder1 = QDealer.dealer.dealerType.asc();
OrderSpecifier<String> sortOrder2 = QDealer.dealer.dealerCode.desc();
Iterable<Dealer> results = dlrRpstry.findAll(where, sortOrder1, sortOrder2);
Let us know if it works. Here is a link to a stackoverflow question/answer that explains the ... parameter syntax on the findAll() method:
https://stackoverflow.com/a/12994104/2879838
This means you can have as many OrderSpecifiers as you want as optional parameters to the function.

If you don't want to create variable for each specifier, it can be done this way:
OrderSpecifier<?>[] sortOrder = new OrderSpecifier[] {
QDealer.dealer.dealerType.asc(),
QDealer.dealer.dealerCode.desc()
};
Iterable<Dealer> results = dlrRpstry.findAll(where, sortOrder);

Related

Exclude null columns in an update statement - JOOQ

I have a POJO that has the fields that can be updated. But sometimes only a few fields will need to be updated and the rest are null. How do I write an update statement that ignores the fields that are null? Would it be better to loop through the non missing ones and dynamically add to a set statement, or using coalesce?
I have the following query:
jooqService.using(txn)
.update(USER_DETAILS)
.set(USER_DETAILS.NAME, input.name)
.set(USER_DETAILS.LAST_NAME, input.lastName)
.set(USER_DETAILS.COURSES, input.courses)
.set(USER_DETAILS.SCHOOL, input.school)
.where(USER_DETAILS.ID.eq(input.id))
.execute()
If there is a better practice?
I don't know Jooq but it looks like you could simply do this:
val jooq = jooqService.using(txn).update(USER_DETAILS)
input.name.let {jooq.set(USER_DETAILS.NAME, it)}
input.lastName.let {jooq.set(USER_DETAILS.LAST_NAME, it)}
etc...
EDIT: Mapping these fields explicitly as above is clearest in my opinion, but you could do something like this:
val fields = new Object[] {USER_DETAILS.NAME, USER_DETAILS.LAST_NAME}
val values = new Object[] {input.name, input.lastName}
val jooq = jooqService.using(txn).update(USER_DETAILS)
values.forEachIndexed { i, value ->
value.let {jooq.set(fields[i], value)}
}
You'd still need to enumerate all the fields and values explicitly and consistently in the arrays for this to work. It seems less readable and more error prone to me.
In Java, it would be somthing like this
var jooqQuery = jooqService.using(txn)
.update(USER_DETAILS);
if (input.name != null) {
jooqQuery.set(USER_DETAILS.NAME, input.name);
}
if (input.lastName != null) {
jooqQuery.set(USER_DETAILS.LAST_NAME, input.lastName);
}
// ...
jooqQuery.where(USER_DETAILS.ID.eq(input.id))
.execute();
Another option rather than writing this UPDATE statement is to use UpdatableRecord:
// Load a POJO into a record using a RecordUnmapper
UserDetailsRecord r =
jooqService.using(txn)
.newRecord(USER_DETAILS, input)
(0 .. r.size() - 1).forEach { if (r[it] == null) r.changed(it, false) }
r.update();
You can probably write an extension function to make this available for all jOOQ records, globally, e.g. as r.updateNonNulls().

Is there an ExampleMatcher with not equals condition

I am working with spring data jpa, I would like to know in QueryByExample(QBE) can i get all the records (where colum value not equals 'XXX')
I have seen ExampleMatcher , but couldnt find anything like not equals
Employee filterBy = new Employee();
filterBy.setLastName("ar");
//Filter - ignore case search and contains
ExampleMatcher matcher = ExampleMatcher.matching()
.withStringMatcher(StringMatcher.CONTAINING) // Match string containing pattern
.withIgnoreCase(); // ignore case sensitivity
example = Example.of(filterBy, matcher);
The above code gets all the records where lastname is ar, but i am looking for lastname should not be "ar".
Is there any other ExampleMatcher ?
BizExceptionConfig condition = configRequestPair.getCondition();
ExampleMatcher exampleMatcher = ExampleMatcher.matching()
.withMatcher("appCode", startsWith())
.withMatcher("name", startsWith())
.withMatcher("code", startsWith());
if(Objects.isNull(condition.getLifecycle())){
condition.setLifecycle(LifeCycle.DELETE.getCode());
HashMap<String, Integer> params = new HashMap<>();
params.put("$ne", LifeCycle.DELETE.getCode());
exampleMatcher = exampleMatcher.withTransformer("lifecycle", (obj) -> Optional.of(params));
}
Example<BizExceptionConfig> example = Example.of(condition, exampleMatcher);
Page<BizExceptionConfig> pageRecord = bizExcConfigRepository.findAll(example, PageUtil.toPageRequest(configRequestPair.getPage()));`enter code here`
This problem can be solved by "withTransformer". JPA is rather limited,
so I suggest using Mongotmpl. I hope it can help you
Your problem could be solved with QBE by using REGEX as StringMatcher and the solution would look like the following:
Employee filterBy = new Employee();
filterBy.setLastName("ar");
filterBy.setLastName(String.format("^(?!.*$1$).*$", Pattern.quote(filterBy.getLastName())));
//filterBy.setLastName(String.format(".*(?<!$1)$", Pattern.quote(filterBy.getLastName()))); // this would be another alternative
ExampleMatcher matcher = ExampleMatcher.matching()
.withStringMatcher(StringMatcher.REGEX) // Match string containing pattern
.withIgnoreCase(); // ignore case sensitivity
Example example = Example.of(filterBy, matcher);
Unfortunately, even though the developer would at first think that Regular Expressions are supported (as there exists aforementioned enum constant), Spring actually currently doesn't support them - and according to the discussion of the related Jira issue, it never won't: https://jira.spring.io/browse/DATAJPA-944

LINQ to SQL compare nullable in subquery

I fail to translate a sql query to a linq query that could calculate some stock.
This is my test query that I'm trying to convert to a linq query.
SELECT
i.*,
(SELECT COUNT(t.*) FROM tickets t
WHERE t.starttime::time = i.sessionstarttime::time
AND t.starttime::date = '2018-04-06'::date)
as stock
FROM items I
-- note that the hardcoded date ('2018-04-06') is a function parameter
( tl;dr; how would you convert this PostgreSQL query to LINQ? )
My attempts so far are the variations of the following query:
var items = await _context.Items.Select(x => new Item
{
Id = x.Id,
IsTicket = x.IsTicket,
Name = x.Name,
Price = x.Price,
SaleItems = x.SaleItems,
SessionStartTime = x.SessionStartTime,
DateCreated = x.DateCreated,
DateEdit = x.DateEdit,
UserIdCreated = x.UserIdCreated,
UserIdEdited = x.UserIdEdited,
// calculate stock in subquery
Stock = _context.Tickets.Count(
t => t.StartTime.Date == ticketDate
&& x.SessionStartTime.HasValue
&& t.StartTime.Hour == x.SessionStartTime.Value.Hours // this is the part that is failing
&& t.State != TicketState.Canceled)
}).ToListAsync();
t.StartTime is Datetime and x.SessionStartTime is Nullable Timespan
So when I comment the line && t.StartTime.Hour == x.SessionStartTime.Value.Hours everything is fine, but with it I get warnings that it could not be translated and will be evaluated locally. But I don't want to download the whole ticket table just to count them.
The t.StartTime.Hour part is fine, I tried to perform static comparisons with both parameters. t.StartTime.Hour == 5 was translated without any problems, but x.SessionStartTime.Value.Hours == 5 failed to translate.
Also the problematic part in the application output:
([t].StartTime.Hour == Convert([x].SessionStartTime, TimeSpan).Hours))
So I guess that convert part is failing.
So what I'm missing and how I could work around this problem. Any help will be appreciated.
Update:
After experimenting a bit I have found two workarounds, that I wouldn't call the answers.
First I noticed that EF is trying to convert Nullable<TimeSpan> to a regular TimeSpan from the mentioned output: ([t].StartTime.Hour == Convert([x].SessionStartTime, TimeSpan).Hours))
I thought I could prevent that conversion by converting to a string and comparing the strings (I have a feeling this will bite me in the future):
t.StartTime.ToString().Contains(x.SessionStartTime.ToString())
The second workaround is only viable for my scenario since I know the items query is final and I can materialise it without calculated Stock, and then loop through the results and calculate it on a separate query. But this seems to add additional calls to the database and sacrifice some performance.
foreach(var x in items.Where(x=>x.SessionStartTime.HasValue))
{
// accessing the t.StartTime.TimeOfDay property seems to fail the LINQ to SQL as well
var hours = x.SessionStartTime.Value.Hours;
var minutes = x.SessionStartTime.Value.Minutes;
x.Stock = _context.Tickets.Count(t => t.StartTime.Date == ticketDate
&& t.StartTime.Hour == hours
&& t.StartTime.Minute == minutes);
}

Linq to Entities does not recognize the method System.DateTime.. and cannot translate this into a store expression

I have a problem that has taken me weeks to resolve and I have not been able to.
I have a class where I have two methods. The following is supposed to take the latest date from database. That date represents the latest payment that a customer has done to "something":
public DateTime getLatestPaymentDate(int? idCustomer)
{
DateTime lastDate;
lastDate = (from fp in ge.Payments
from cst in ge.Customers
from brs in ge.Records.AsEnumerable()
where (cst.idCustomer == brs.idCustomer && brs.idHardBox == fp.idHardbox
&& cst.idCustomer == idCustomer)
select fp.datePayment).AsEnumerable().Max();
return lastDate;
}//getLatestPaymentDate
And here I have the other method, which is supposed to call the previous one to complete a Linq query and pass it to a Crystal Report:
//Linq query to retrieve all those customers'data who have not paid their safebox(es) annuity in the last year.
public List<ReportObject> GetPendingPayers()
{
List<ReportObject> defaulterCustomers;
defaulterCustomers = (from c in ge.Customer
from br in ge.Records
from p in ge.Payments
where (c.idCustomer == br.idCustomer
&& br.idHardBox == p.idHardBox)
select new ReportObject
{
CustomerId = c.idCustomer,
CustomerName = c.nameCustomer,
HardBoxDateRecord = br.idHardRecord,
PaymentDate = getLatestPaymentDate(c.idCustomer),
}).Distinct().ToList();
}//GetPendingPayers
No compile error is thrown here, but when I run the application and the second method tries to call the first one in the field PaymentDate the error mentioned in the header occurs:
Linq to Entities does not recognize the method System.DateTime.. and cannot translate this into a store expression
Please anybody with an useful input that put me off from this messy error? Any help will be appreciated !
Thanks a lot !
Have a look at these other questions :
LINQ to Entities does not recognize the method
LINQ to Entities does not recognize the method 'System.DateTime Parse(System.String)' method
Basically, you cannot use a value on the C# side and translate it into SQL. The first question offers a more thorough explanation ; the second offers a simple solution to your problem.
EDIT :
Simply put : the EF is asking the SQL server to perform the getLatestPaymentDate method, which it has no clue about. You need to execute it on the program side.
Simply perform your query first, put the results into a list and then do your Select on the in-memory list :
List<ReportObject> defaulterCustomers;
var queryResult = (from c in ge.Customer
from br in ge.Records
from p in ge.Payments
where (c.idCustomer == br.idCustomer
&& br.idHardBox == p.idHardBox)).Distinct().ToList();
defaulterCustomers = from r in queryResult
select new ReportObject
{
CustomerId = r.idCustomer,
CustomerName = r.nameCustomer,
HardBoxDateRecord = r.idHardRecord,
PaymentDate = getLatestPaymentDate(r.idCustomer),
}).Distinct().ToList();
I don't have access to your code, obviously, so try it out and tell me if it works for you!
You'll end up with an in-memory list

How do I disable some entities based on a few properties in NHibernate Search?

Im still pretty new to NHibernate.Search so please bear with me if this is stupid question :)
Say, I have indexed some entities of type BlogPost, which has a property called IsDeleted. If IsDeleted is set to true, I don't want my queries to show this particular blogpost.
Is this possible? And if it is - How? :P
Thanks in advance
- cwap
// Using NHibernate.Linq:
var result = Session.Linq<BlogPost>().Where(post => !post.IsDeleted).ToList();
// Using HQL:
var hql = "from BlogPost bp where bp.IsDeleted == false";
var result = Session.CreateQuery(hql).List<BlogPost>();
// Using Criteria API:
var result = s.CreateCriteria(typeof(BlogPost))
.Add(Restrictions.Eq("IsDeleted", false));
.List<BlogPost>();
NHibernate.Linq
HQL: Hibernate Query Language
Found the solution myself. I added the [Field(Index.Tokenized, Store = Store.Yes)]-attribute to the IsDeleted property, and added this clause to any query inbound:
string q = "(" + userQuery + ") AND IsDeleted:False";
I knew it was something simple :)