I'm new to JPA with the CriteriaBuilder and want to reduce some lines of code.
cb.and is usefull to add predicate's, but what if they are null?
How can I reduce these lines of code? Thanks!
I'm worried about the readability of the code...
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(OrganizationEntity.class);
Root<OrganizationEntity> root = cq.from(OrganizationEntity.class);
Predicate predicateFrom = null;
Predicate predicateTo = null;
if (from != null) {
predicateFrom = cb.ge(root.get(OrganizationEntity_.id), from);
}
if (to != null) {
predicateTo = cb.le(root.get(OrganizationEntity_.id), to);
}
if(predicateFrom != null && predicateTo != null) {
cq.where(cb.and(predicateFrom,predicateTo));
} else if (predicateFrom != null && predicateTo == null) {
cq.where(predicateFrom);
} else if (predicateFrom == null && predicateTo != null) {
cq.where(predicateTo);
}
TypedQuery<OrganizationEntity> query = em.createQuery(cq);
list = query.getResultList();
return list;
what you could do is create your predicate list
List<Predicate> predicates = new ArrayList<>();
if (from != null) {
predicates .add(cb.ge(root.get(OrganizationEntity_.id), from));
}
if (to != null) {
predicates .add(cb.le(root.get(OrganizationEntity_.id), to));
}
if (predicates.size()>0{
cq.where(cb.and(predicates.toArray(new Predicate[predicates
.size()])));
}
Related
This Is my coding for mapping data and filter it by subs class:
Hi i have a problem where is my list is null after it mapping the data so can you help me for this solution
void onUserJobListData(Event e) {
Map data = Map.from(e.snapshot.value);
data?.forEach((a, b) {
print("key " + a);
// int i = _jobs.indexWhere((j) => j.jobId == a);
int i = _jobs == null ? 0 : _jobs.indexWhere((j) => j.jobId == a);
if (i >= 0) {
if (b['notification_date'] != null) {
_jobs[i].matchDetail.notificationDate = b['notification_date'];
}
if (b['status_date'] != null) {
_jobs[i].matchDetail.statusDate = b['status_date'];
}
if (b['event_status'] != null) {
_jobs[i].matchDetail.eventStatus = b['event_status'];
}
if (b['notification_status'] != null) {
_jobs[i].matchDetail.notificationStatus = b['notification_status'];
}
if (b['status'] != null) {
_jobs[i].matchDetail.status = b['status'];
}
}
});
print("Apa apa jerr");
jobState.setJobList(_jobs, JobState.USER_JOB);
}`
I need to generate a query dynamically based on parameter passed and need to join three tables, getting below exception while building query with EntityManager CriteriaBuilder, same code structure is working if I convert it to Criteria but I want use CriteriaBuilder only.
#Override
#Transactional
public List<DatapointReply> getAllByFilter(Map<String, List<Object>> filter, int maxResults,
boolean matchAllFilters) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<DatapointReply> criteriaQuery = criteriaBuilder.createQuery(DatapointReply.class);
Root<PartDatapointReply> datapointReplyRoot = criteriaQuery.from(PartDatapointReply.class);
Join<DatapointReply, Datapoint> datapointJoin = null;
Join<PartDatapointReply, Part> partJoin = null;
criteriaQuery.select(datapointReplyRoot);
if (filter.containsKey("datapointkey")) {
datapointJoin = datapointReplyRoot.join("datapoint");
}
if (filter.containsKey("partstatus") || filter.containsKey("partmodelnumber")) {
partJoin = datapointReplyRoot.join("part");
}
List<Predicate> predicateList = new ArrayList<>();
for (String searchKey : filter.keySet()) {
List<Object> searchTerms = filter.get(searchKey);
Predicate predicate = buildSearchCriterion(new String[searchTerms.size()], true, JunctionType.OR, datapointReplyRoot, criteriaBuilder,
"value");
if (predicate != null) {
predicateList.add(predicate);
}
}
Predicate[] predicateArray = predicateList.toArray(new Predicate[predicateList.size()]);
if (!predicateList.isEmpty() && matchAllFilters) {
criteriaBuilder.and(predicateArray);
} else if (!predicateList.isEmpty() && !matchAllFilters) {
criteriaBuilder.or(predicateArray);
}
TypedQuery<DatapointReply> query = entityManager.createQuery(criteriaQuery);
// Define the Max Results
if (maxResults > SearchEngine.SEARCH_MAX_RESULTS_ALL) {
query.setMaxResults(maxResults);
}
return query.getResultList();
}
// Get Predicate based on parameter
private Predicate buildSearchCriterion(String[] values, boolean isWildcardSearch, JunctionType criteriaType,
Root<PartDatapointReply> datapointReplyRoot, CriteriaBuilder criteriaBuilder, String... attributeNames) {
// Build the Search Criteria as Single or Multiple Column Search
Predicate finalCriterion = null;
if (!isWildcardSearch) {
List<Predicate> criteria = new ArrayList<>();
for (String attributeName : attributeNames) {
Predicate attributeCriterion = criteriaBuilder.in(datapointReplyRoot.get(attributeName)).value(values);
criteria.add(attributeCriterion);
}
if (criteriaType == null || criteriaType == JunctionType.OR) {
finalCriterion = criteriaBuilder.or(criteria.toArray(new Predicate[criteria.size()]));
} else {
finalCriterion = criteriaBuilder.and(criteria.toArray(new Predicate[criteria.size()]));
}
} else if (isWildcardSearch) {
// Wildcard OR search on a single attribute
// Search through each Search Term, ignoring white space, and add
// each term to the Criteria Restrictions.
List<Predicate> criteria = new ArrayList<>();
for (String attributeName : attributeNames) {
List<Predicate> attributeCriteria = new ArrayList<>();
Predicate attributeCriterion;
for (String value : values) {
Predicate valueCriterion;
// Wrap the Value in Wild Cards if it is a Wild Card Search
if (isWildcardSearch) {
value = "%" + value + "%";
}
valueCriterion = criteriaBuilder.like(criteriaBuilder.lower(datapointReplyRoot.get(attributeName)), value.toLowerCase());
attributeCriteria.add(valueCriterion);
}
attributeCriterion = criteriaBuilder.or(attributeCriteria.toArray(new Predicate[attributeCriteria.size()]));
criteria.add(attributeCriterion);
}
if (criteriaType == null || criteriaType == JunctionType.OR) {
finalCriterion = criteriaBuilder.or(criteria.toArray(new Predicate[criteria.size()]));
} else {
finalCriterion = criteriaBuilder.and(criteria.toArray(new Predicate[criteria.size()]));
}
}
return finalCriterion;
}
Getting below error message:
java.lang.IllegalArgumentException: Encountered array-valued parameter binding, but was expecting [java.lang.String (n/a)]
at org.hibernate.query.spi.QueryParameterBindingValidator.validateArrayValuedParameterBinding(QueryParameterBindingValidator.java:142) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:49) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:27) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at ```
Changing type of values from String[] to Arrays.asList(values) in below lines will fix the problem.
Predicate attributeCriterion = criteriaBuilder.in(datapointReplyRoot.get(attributeName)).value(Arrays.asList(values));
I am trying to iterate query result of Querydsl. For iterating query result I am using for each loop.But I am getting class cast exception.
I want nBuildId for finding building name which is in building table. So how I can iterate this List<Tuple> for getting column.
I tried like this
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 != null && nBuildId !=0) {
query.where(room.nBuildId.eq(nBuildId));
}
if(sFloor != null) {
query.where(room.sFloor.eq(sFloor));
}
List<Tuple> queryResult = query.fetch();
for(Tuple row : queryResult) {
System.out.println("Build Id " +row.get(room.nBuildId));
}
return queryResult;
}
Error
message: "com.spacestudy.model.Room cannot be cast to com.querydsl.core.Tuple",
Exception: "java.lang.ClassCastException"
public List<Room> loadUnclamiedRoomGrid(Integer nBuildId, String sFloor) {
QRoom room = QRoom.room;
QRoomDepartmentMapping roomDepartmentMapping = QRoomDepartmentMapping.roomDepartmentMapping;
JPAQuery<Room> query = new JPAQuery<Room>(em);
query
.from(room)
.where(room.nRoomId.notIn
(JPAExpressions.select(roomDepartmentMapping.nRoomId)
.from(roomDepartmentMapping)
)
);
if (nBuildId != null && nBuildId !=0) {
query.where(room.nBuildId.eq(nBuildId));
}
if(sFloor != null) {
query.where(room.sFloor.eq(sFloor));
}
List<Room> queryResult = query.fetch();
for(Room row : queryResult) {
System.out.println("Build Id " + room.nBuildId);
}
return queryResult;
}
As I build a project with Entity Framework 6 (using EF for the first time), I noticed that when I only Update the relationships of an Entity, EF updates the main Entity too.
I can tell this is happening because I'm using System Versioned tables on Sql Server 2017.
This is a made up scenario, but most of the concept is here.
public async Task<ActionResult> Edit([Bind(Include="Id,Name,LocationTimes")] LocationViewModel locationVM) {
if (ModelState.IsValid) {
var location = await _db.Locations.FirstOrDefaultAsync(l => l.Id == locationsViewModel.Id && l.UserId == UserId);
if (location == null) {
return HttpNotFound();
}
location.Name = locationsViewModel.Name;
// ... other properties
foreach (var day in locationsViewModel.LocationTimes.Days) {
var time = new Time {
Day = day.Day,
OpenTime = day.OpenTime,
CloseTime = day.CloseTime,
};
// Find current Time or keep newly created
time = await time.FindByTimeAsync(time, _db) ?? time;
// Find LocationTime with same day
var locationTime = location.LocationTimes.FirstOrDefault(lt => lt.Time.Day == day.Day);
// If all times are the same, skip (continue)
if (locationTime != null && locationTime.Time.OpenTime == time.OpenTime && locationTime.Time.CloseTime == time.CloseTime)
continue;
if (locationTime != null && (locationTime.Time.OpenTime != time.OpenTime || locationTime.Time.CloseTime != time.CloseTime)) {
// Remove, At least one of the Times do not match
locationTime.Time = time;
_db.Entry(locationTime).State = EntityState.Modified;
} else {
location.LocationTimes.Add(new LocationTime {
Location = location,
Time = time,
});
}
}
_db.Entry(location).State = EntityState.Modified;
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
}
I assume, that by marking the entire Entity as Modified, EF will call the update statement.
How can I avoid an UPDATE to the parent Entity, if no properties have changed on the parent, but still Add/Update the child relationships?
I assume I have to check that each property has not changed and therefore I should not be setting location state to Modified, but how would I handle the newly added Times?
Update #1
So I tried what I mentioned and it works, but is this the correct way to do this?
public async Task<ActionResult> Edit([Bind(Include="Id,Name,LocationTimes")] LocationViewModel locationVM) {
if (ModelState.IsValid) {
var location = await _db.Locations.FirstOrDefaultAsync(l => l.Id == locationsViewModel.Id && l.UserId == UserId);
if (location == null) {
return HttpNotFound();
}
/*******************
This is new part
*******************/
if (
location.Name != locationsViewModel.Name
// || ... test other properties
) {
location.Name = locationsViewModel.Name;
// ... other properties
_db.Entry(location).State = EntityState.Modified;
} else {
_db.Entry(location).State = EntityState.Unchanged;
}
/*******************/
foreach (var day in locationsViewModel.LocationTimes.Days) {
var time = new Time {
Day = day.Day,
OpenTime = day.OpenTime,
CloseTime = day.CloseTime,
};
// Find current Time or keep newly created
time = await time.FindByTimeAsync(time, _db) ?? time;
// Find LocationTime with same day
var locationTime = location.LocationTimes.FirstOrDefault(lt => lt.Time.Day == day.Day);
// If all times are the same, skip (continue)
if (locationTime != null && locationTime.Time.OpenTime == time.OpenTime && locationTime.Time.CloseTime == time.CloseTime)
continue;
if (locationTime != null && (locationTime.Time.OpenTime != time.OpenTime || locationTime.Time.CloseTime != time.CloseTime)) {
// Remove, At least one of the Times do not match
locationTime.Time = time;
_db.Entry(locationTime).State = EntityState.Modified;
} else {
location.LocationTimes.Add(new LocationTime {
Location = location,
Time = time,
});
}
}
/* removed, added above */
//_db.Entry(location).State = EntityState.Modified;
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
}
So after trial and error, I guess I misunderstood how EF handles the EntityState. I though if a child was Modified, you had to set the parent as Modified as well.
Gladly, that's not the case and the code below works as desired.
public async Task<ActionResult> Edit([Bind(Include="Id,Name,LocationTimes")] LocationViewModel locationVM) {
if (ModelState.IsValid) {
var location = await _db.Locations.FirstOrDefaultAsync(l => l.Id == locationsViewModel.Id && l.UserId == UserId);
if (location == null) {
return HttpNotFound();
}
/*******************
This is new part
check if at least one property was changed
*******************/
if (
location.Name != locationsViewModel.Name
|| location.Ref != locationsViewModel.Ref
// || ... test other properties
) {
location.Name = locationsViewModel.Name;
location.Ref = locationsViewModel.Ref;
// ... other properties
// Tell EF that the Entity has been modified (probably not needed, but just in case)
_db.Entry(location).State = EntityState.Modified;
} else {
// Tell EF that the Entity has *NOT* been modified
_db.Entry(location).State = EntityState.Unchanged;
}
/*******************/
foreach (var day in locationsViewModel.LocationTimes.Days) {
var time = new Time {
Day = day.Day,
OpenTime = day.OpenTime,
CloseTime = day.CloseTime,
};
// Find current Time or keep newly created
time = await time.FindByTimeAsync(time, _db) ?? time;
// Find LocationTime with same day
var locationTime = location.LocationTimes.FirstOrDefault(lt => lt.Time.Day == day.Day);
// If all times are the same, skip (continue)
if (locationTime != null && locationTime.Time.OpenTime == time.OpenTime && locationTime.Time.CloseTime == time.CloseTime)
continue;
if (locationTime != null && (locationTime.Time.OpenTime != time.OpenTime || locationTime.Time.CloseTime != time.CloseTime)) {
// Remove, At least one of the Times do not match
locationTime.Time = time;
_db.Entry(locationTime).State = EntityState.Modified;
} else {
location.LocationTimes.Add(new LocationTime {
Location = location,
Time = time,
});
}
}
/* removed, added above */
//_db.Entry(location).State = EntityState.Modified;
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
}
I want to build a query with where condition for filter properties when they have a value (not null). The multiple filters must be a AND combination if they are used.
How can combine/add each predicate when the according property value is not null to get a final query ready for filtering?
IQueryable<Customer> filter = null;
Expression<Func<Customer, bool>> predicatetest1 = res => res.test1 == request.test1;
Expression<Func<Customer, bool>> predicatetest2 = res => res.test2 == request.test2;
Expression<Func<Customer, bool>> predicatetest3 = res => res.test3 == request.test3;
if (request.test1 != null)
{
// add the above predicate to the filter
}
if (request.test2 != null)
{
// add the above predicate to the filter
}
if (request.test3 != null)
{
// add the above predicate to the filter
}
IQueryable<Customer> filter = Context.Customers;
if (request.test1 != null)
{
filter = filter.Where(predicatetest1);
}
if (request.test2 != null)
{
filter = filter.Where(predicatetest2);
}
if (request.test3 != null)
{
filter = filter.Where(predicatetest3);
}
var customers = filter.ToList();
The following would be equivalent, when all 3 properties where not null
Context.Customers.Where(predicatetest1).Where(predicatetest2).Where(predicatetest3).ToList();