I am trying to implement Search filter functionality and load grid in spring boot data JPA application. For creating dynamic query I am using Querydsl.
I am searching data according to sFloor and nBuildId.
If I am passing sFloor, nBuildId only that matching record should display in grid
If I am not passing any values then grid should load with all values.
I tried like below In that when I am passing data I am able to filter data. But when I am not passing any records I am getting null pointer exception.
RoomController
#GetMapping("/getUnclaimedRoomDetails")
public List<Tuple> populateUnclaimedRoomGridView(#RequestParam(value="nBuildId", required=false) Integer nBuildId,
#RequestParam(value="sFloor", required=false) String sFloor) {
return roomService.loadUnclamiedRoomGrid(nBuildId,sFloor);
}
RoomService
public List<Tuple> loadUnclamiedRoomGrid(Integer nBuildId, String sFloor) {
QRoom room = QRoom.room;
QRoomDepartmentMapping roomDepartmentMapping = QRoomDepartmentMapping.roomDepartmentMapping;
JPAQuery<Tuple> query = new JPAQuery<Tuple>(em);
query.from(room)
.where(room.nRoomId.notIn
(JPAExpressions.select(roomDepartmentMapping.nRoomId)
.from(roomDepartmentMapping)
)
);
if (nBuildId != 0) {
query.where(room.nBuildId.eq(nBuildId));
}
if(sFloor != null) {
query.where(room.sFloor.eq(sFloor));
}
return query.fetch();
}
Can any one please tell me why I am getting null pointer exception instead of all data?
I think the issue is if (nBuildId != 0). nBuildId is a big Integer so when the check is being performed it is being unboxed to a primitive int. If it's null, this will cause a NullPointerException. A null check on nBuildId should fix things, E.G. if (nBuildId != null && nBuildId != 0).
Related
I'm learning reactive programming with webflux, and for that I'm migrating some code.
For example I'm trying to migrate this method:
public Set<Vaccine> getAll(Set<Long> vaccinesIds) throws EntityNotFoundException {
if (null == vaccinesIds) {
return null;
}
Set<Long> vaccinesToFind = new HashSet<>(vaccinesIds);
vaccinesToFind.remove(null);
Set<Vaccine> vaccines = new HashSet<>();
vaccineRepository.findByIdIn(vaccinesToFind).forEach(vaccines::add);
if (vaccines.size() != vaccinesToFind.size()) {
LOG.warn("Could not find vaccines with ids: " + vaccinesToFind.removeAll(vaccines.stream().map(Vaccine::getId).collect(Collectors.toSet())));
throw new EntityNotFoundException(VACCINE_ERROR_NOT_FOUND);
}
return vaccines;
}
To summarize the code, if the respository returns all the vaccines that are requested should return the result, if not should return an error.
For that, I thought in something like this, but is not working:
public Flux<Vaccine> getAll(Set<Long> vaccinesIds) {
if (null == vaccinesIds) {
return Flux.empty();
}
Set<Long> vaccinesToFind = new HashSet<>(vaccinesIds);
Flux<Vaccine> byIdIn = vaccineRepository.findByIdIn(vaccinesToFind);
Mono<Long> filter = vaccineRepository.findByIdIn(vaccinesToFind).count().filter(x -> x.equals(Long.valueOf(vaccinesToFind.size())));
return filter.flatMapMany(asd -> vaccineRepository.findByIdIn(vaccinesToFind)
).switchIfEmpty(Flux.error((new EntityNotFoundException(VACCINE_ERROR_NOT_FOUND))));
}
What am I doing wrong?
My first doubt is why the filter is a Mono of Long if it has a equals method in the end. My problem is about evaluating the filter in order to return the list or the error.
First of all, you are querying the same result vaccineRepository.findByIdIn(vaccinesToFind) multiple times. The same data is queried, transferred and deserialized multiple times. This is a sign that something is wrong here.
Let's assume the result set fits into the memory. Then the idea would be to transform flux into a usual collection and to decide whether to emit an error or not:
return vaccineRepository.findByIdIn(vaccinesIds)
.collectList()
.flatMapMany(result -> {
if(result.size() == vaccinesIds.size()) return Flux.fromIterable(result);
else return Flux.error(new EntityNotFoundException(VACCINE_ERROR_NOT_FOUND));
});
In the case the result is to huge for the main memory, you could do count in the db by the first query and in the positive case query the results. The solution is similar to your code:
return vaccineRepository.countByIdIn(vaccinesIds)
.filter(count -> count == vaccinesIds.size())
.flatMapMany($ -> vaccineRepository.findByIdIn(vaccinesIds))
.switchIfEmpty(Mono.error(new EntityNotFoundException(VACCINE_ERROR_NOT_FOUND)));
The result of filter is Mono<Long> because filter just takes the elements from the upstream and tests against the given predicate. If the predicate returns false, the item is filtered out and the Mono is empty. To keep all results of a the test you could use map and the type would be Mono<Boolean>.
I have a function doing a complicated Where query on my db context and then applies another transformation passed to it:
static IQueryable<T> Query<T>(Func<IQueryable<ServicesData>, IQueryable<T>> f, string path1 = null, string path2 = null, string path3 = null, string path4 = null, string path5 = null) {
try {
using (var dbc = new MyDbContext() ) {
var res = dbc.ServicesData
.Where(sd =>
(path1 == null || (path1.Contains("%") || path1.Contains("_") ? EF.Functions.Like(sd.Path1, path1) : sd.Path1 == path1))
&& (path2 == null || (path2.Contains("%") || path2.Contains("_") ? EF.Functions.Like(sd.Path2, path2) : sd.Path2 == path2))
&& (path3 == null || (path3.Contains("%") || path3.Contains("_") ? EF.Functions.Like(sd.Path3, path3) : sd.Path3 == path3))
&& (path4 == null || (path4.Contains("%") || path4.Contains("_") ? EF.Functions.Like(sd.Path4, path4) : sd.Path4 == path4))
&& (path5 == null || (path5.Contains("%") || path5.Contains("_") ? EF.Functions.Like(sd.Path5, path5) : sd.Path5 == path5)));
return f(res.ToList().AsQueryable());
//return f(res).ToList().AsQueryable();
}
} catch (Exception ex_) {
return VList<T>.Empty.AsQueryable();
}
}
This is used ie like this:
IQueryable<int> Int1InLastHour(IQueryable<ServicesData> input) {
var lastHour = DateTimeOffset.Now.AddHours(-1).ToUnixTimeMilliseconds();
return input
.Where(v => (v.Time <= lastHour) && (v.Int1 is object))
.Select(v => v.Int1.Value);
}
var lastHourProcessTime = Query(Int1InLastHour, "Publisher", "%", "ItemProcessTime").Sum();
This works, however since I call res.ToList() before calling f the linq in f is done in memory and not on the DB SQL
If I try to replace f(res.ToList().AsQueryable()) with f(res).ToList().AsQueryable() I get an exception:
{"Processing of the LINQ expression '[EntityShaperExpression][ServicesData]' by 'RelationalProjectionBindingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information."}
Is there any way for me to solve this ? can I somehow pass the query (Func<IQueryable<ServicesData>, IQueryable<T>>) and then combine it to the query in Query before excecuting it on the dbc ?
A few issues. You can split the querying to break down your results, but the scope of your DbContext needs to be at the outermost point of the chain, not inside the inner-most:
This here:
static IQueryable<T> Query<T>(Func<IQueryable<ServicesData>, IQueryable<T>> f, string path1 = null, string path2 = null, string path3 = null, string path4 = null, string path5 = null) {
try {
using (var dbc = new MyDbContext() ) { // DbContext should not be scoped here...
var res = dbc.ServicesData
As the simplest re-factor:
static IQueryable<T> Query<T>(MyDbContext dbc, Func<IQueryable<ServicesData>, IQueryable<T>> f, string path1 = null, string path2 = null, string path3 = null, string path4 = null, string path5 = null) {
try
{
var res = dbc.ServicesData.AsQueryable();
if(path1 != null)
if(path1.Contains("%") || path1.Contains("_"))
res = res.Where(EF.Functions.Like(sd.Path1, path1));
else
res = res.Where(sd.Path1 == path1);
// Repeat for Path 2 - 5 ....
return f(res);
}
catch (Exception ex_)
{
return VList<T>.Empty.AsQueryable();
}
}
Firstly, we pass in the DbContext. If the context is scoped here, the list must be materialized before being returned. The goal is to allow callers to further reduce the expression before executing the list. This means the DbContext needs to be scoped outside of this initial generation and passed in. With IoC containers managing lifetime scope you can bypass this if the DbContext is injected and scoped to a Request or common lifetime scope.
The next improvement suggestion is to move the conditional checks for the parameters out of the Linq and into regular conditions so that the Like / Equals check will only be added if the condition was provided. This will result in simpler, faster SQL being run on the server.
So the end result would look something like:
using (var dbContext = new MyDbContext())
{
var lastHourProcessTime = Query(dbContext, Int1InLastHour, "Publisher", "%", "ItemProcessTime").Sum();
}
I sort of get where you're trying to go here, but abstracting expressions from EF is bound to lead to confusing code and still prone to limitations and bugs. IMO keeping it simpler generally leads to less issues, but give this a go and see if it gets you closer.
I am new to RxJava2.
I am trying to get a list of Transaction object both from cache and from server.
I want to compare the server value to cache value and if the server value is the same, then ignore it.
I was able to do it easily using .scan() because we can return null and when null is returned from the .scan() the value got ignored(filtered).
RxJava 1
private Observable<List<Transaction>> getTransactionsFromCacheAndServer() {
return Observable.concat(
getTransactionsFromCache(),
getTransactionsFromServer()
)
.scan((p1, p2) -> {
if (p1 == null && p2 != null) {
return p2;
} else if (p1 != null && !isListSame(p1, p2)) {
return p2;
} else {
return null;
}
});
}
With RxJava 2, since I cannot return null anymore, things are not easy.
RxJava 2
private Observable<List<Transaction>> getTransactionsFromCacheAndServer() {
return Observable.concat(
getTransactionsFromCache(),
getTransactionsFromServer()
)
.map(FilterObject::new)
.scan((filterObject1, filterObject2) -> {
List<Transaction> p1 = (List<Transaction>)filterObject1.value;
List<Transaction> p2 = (List<Transaction>)filterObject2.value;
if (p1.size() == 0 && p2.size() > 0) {
return filterObject2;
} else if (!isListSame(p1, p2)) {
return filterObject2;
} else {
filterObject2.filter = true;
return filterObject2;
}
})
.filter(filterObject -> !filterObject.filter)
.map(filterObject -> (List<Transaction>)filterObject.value);
}
Where FilterObject is:
public class FilterObject {
public Object value;
public boolean filter;
public FilterObject(Object value) {
this.value = value;
}
}
Even though I can achieve the same thing using above method, it seems very ugly. Also I had to include two maps which might not be so performance friendly.
Is there a simple/clean way to achieve what I want?
I don't think there is a generic solution to this problem, since an empty list and a list that needs to be filtered (which happens to be empty in all cases) are two different things (the output of the scan) and needs to be handled differently.
However, in your particular case you never emit an empty list, except maybe for the first output.
(I am using String instead Transaction, shouldn't matter)
private Observable<List<String>> getTransactionsFromCacheAndServer() {
return Observable.concat(
getTransactionsFromCache(),
getTransactionsFromServer()
)
.filter(list -> !list.isEmpty())
// If you prefer a consistent empty list over the first
// empty list emission getting filtered
.startWith((List<String>) Collections.EMPTY_LIST)
// Newly emitted value cannot be empty, it only depends only on the comparison
.distinctUntilChanged(this::isListSame);
}
That's the closest I could get with as few operators as possible. Hope it solves your problem.
Based on andras' answer, I modified little bit to achieve what I want.
private Observable<List<String>> getTransactionsFromCacheAndServer() {
return Observable.concat(
getTransactionsFromCache(),
getTransactionsFromServer()
)
.filter(list -> !list.isEmpty())
.distinctUntilChanged(this::isListSame)
.switchIfEmpty(Observable.just(new ArrayList<>()));
}
Andreas' answer will always receive an empty list and then a real data.
My solution above will receive:
1. Data from cache (and then data from server if different)
2. Empty list if both cache and server returns Empty list.
I have the following matcher:
Example.of(
user,
ExampleMatcher.matching()
.withMatcher("name", contains().ignoreCase())
.withMatcher("phoneNumber", contains())
)
It works fine except null values. For example, it doesn't return the users whose phone number is NULL.
I have tried to following to include the NULL values but it didn't work:
Example.of(
user,
ExampleMatcher.matching()
.withMatcher("name", contains().ignoreCase())
.withIncludeNullValues()
.withMatcher("phoneNumber", contains())
.withIncludeNullValues()
)
The generated SQL is the following:
select
user0_.id as id1_9_,
user0_.document_expiry_date as document2_9_,
user0_.document_type as document3_9_,
user0_.document_url as document4_9_,
user0_.email as email5_9_,
user0_.name as name6_9_,
user0_.phone_number as phone_nu7_9_
from
user user0_
where
(lower(user0_.name) like ?)
and (user0_.id is null)
and (user0_.document_type is null)
and (user0_.document_url is null)
and (user0_.email is null)
and (user0_.phone_number like ?)
and (user0_.document_expiry_date is null)
How can I configure it to include rows with NULL columns as well?
Confronted the same problem, but I think there is no way to accomplish this goal. Here is the source code of method withMatcher:
public ExampleMatcher withMatcher(String propertyPath, ExampleMatcher.GenericPropertyMatcher genericPropertyMatcher) {
Assert.hasText(propertyPath, "PropertyPath must not be empty!");
Assert.notNull(genericPropertyMatcher, "GenericPropertyMatcher must not be empty!");
ExampleMatcher.PropertySpecifiers propertySpecifiers = new ExampleMatcher.PropertySpecifiers(this.propertySpecifiers);
ExampleMatcher.PropertySpecifier propertySpecifier = new ExampleMatcher.PropertySpecifier(propertyPath);
if (genericPropertyMatcher.ignoreCase != null) {
propertySpecifier = propertySpecifier.withIgnoreCase(genericPropertyMatcher.ignoreCase);
}
if (genericPropertyMatcher.stringMatcher != null) {
propertySpecifier = propertySpecifier.withStringMatcher(genericPropertyMatcher.stringMatcher);
}
if (genericPropertyMatcher.valueTransformer != null) {
propertySpecifier = propertySpecifier.withValueTransformer(genericPropertyMatcher.valueTransformer);
}
propertySpecifiers.add(propertySpecifier);
return new ExampleMatcher(this.nullHandler, this.defaultStringMatcher, propertySpecifiers, this.ignoredPaths, this.defaultIgnoreCase, this.mode);
}
this method returns a ExampleMatcher with the 'global' nullHandler which applied to all the fields. That means, you can't specify a different nullHander for some special fields.
You can implements JpaSpecificationExecutor<T> in your repository and use findAll(Specification specification), and set like param:
//My solution, create a specification with predicate and add example.
public Specification<MaestroPlasticoTebca> specificationAttributeNull(boolean isNull, Example<MaestroPlasticoTebca> example, String attributeName) {
return (root, query, builder) -> {
final List<Predicate> predicates = new ArrayList<>();
if (isNull) {
predicates.add(builder.isNull(root.get(attributeName)));
} else {
predicates.add(builder.isNotNull(root.get(attributeName)));
}
predicates.add(QueryByExamplePredicateBuilder.getPredicate(root, builder, example));
return builder.and(predicates.toArray(new Predicate[0]));
};
}
And use it:
Example example = Example.of(
user,
ExampleMatcher.matching()
.withMatcher("name", contains().ignoreCase())
);
repository.findAll(specificationAttributeNull(true, example, "phoneNumber"));
I'm using WCF RIA in a Lightswitch project to create some query results. This query brings back all results regardless. I cannot make it filter the records based on the parameter passed (string Town).
public IQueryable<Enquiries> TestQuery(string Town)
{
List<Enquiries> riaenqs = new List<Enquiries>();
var enqs = this.Context.ClientEnquiries
.Include("Client")
.Include("Client.Town")
.OrderBy(enq => enq.Id);
if (Town != null)
{
enqs.Where(enq => enq.Client.Town.TownName == Town);
}
foreach (ClientEnquiry item in enqs.ToList())
{
Enquiries enq = new Enquiries();
enq.Id = item.Id;
enq.ClientName = item.Client.FirstName + " " + item.Client.Surname;
enq.Town = item.Client.Town != null ? item.Client.Town.TownName : null;
riaenqs.Add(enq);
}
return riaenqs.AsQueryable();
}
During debugging I can see that the Town is correctly populated and I can see that the query is built accordingly if Town is not null. However, when I hit the foreach statement where the linq to ef query is executed I always get all the results. I just cannot figure out where I'm slipping up.
The LINQ methods like the Where do not modify the collection/expression but always returning a new one.
So you need to reassign the result of the Where to your original variable enqs:
if (Town != null)
{
enqs = enqs.Where(enq => enq.Client.Town.TownName == Town);
}