Spring with Querydsl :java.lang.ClassCastException - spring-data-jpa

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;
}

Related

Generic Entity Framework Pagination

I am trying to write a generic way to provide pagination on my Link query to my MySQL database. However this required me to have a generic way to where, as the field used may differ from table to table. The issue i am facing it that Linq can't translate my generic types. Are there a way to do this that would work with Linq?
public static class PaginationExtentions {
public static async Task<PaginationResult<T>> Pagination<T, J>(this DbSet<J> dbSet, Pagination pagination, Func<IQueryable<J>, IQueryable<T>>? select) where J : class {
bool previousPage = false;
bool nextPage = false;
string startCursor = null;
string endCursor = null;
var property = typeof(J).GetProperty(pagination.SortBy);
string after = pagination.After != null ? Base64Decode(pagination.After) : null;
bool backwardMode = after != null && after.ToLower().StartsWith("prev__");
string cursor = after != null ? after.Split("__", 2).Last() : null;
List<J> items;
if (cursor == null) {
items = await (from s in dbSet
orderby GetPropertyValue<J, string>(s, pagination.SortBy) ascending
select s).Take(pagination.First)
.ToListAsync();
}
else if (backwardMode) {
items = await (from subject in (
from s in dbSet
where string.Compare( (string?)property.GetValue(s), cursor) < 0
orderby (string?)property.GetValue(s) descending
select s).Take(pagination.First)
orderby (string?)property.GetValue(subject) ascending
select subject).ToListAsync();
}
else {
items = await (from s in dbSet
where string.Compare((string?)property.GetValue(s), cursor) > 0
orderby GetPropertyValue<J, string>(s, pagination.SortBy) ascending
select s).Take(pagination.First)
.ToListAsync();
}
previousPage = await dbSet.Where(s => string.Compare((string?)property.GetValue(s), cursor) < 0).AnyAsync();
nextPage = items.Count == 0 ? false : await dbSet.Where(s => string.Compare((string?)property.GetValue(s), (string?)property.GetValue(items.Last())) > 0).AnyAsync();
var backwardsCursor = !previousPage ? null : "prev__" + cursor;
var forwardsCursor = !nextPage ? null : items.Count > 0 ? "next__" + (string?)property.GetValue(items.Last()) : null;
return new PaginationResult<T>() {
Items = select(items.AsQueryable()).ToList(),
TotalCount = await dbSet.CountAsync(),
HasPreviousPage = previousPage,
HasNextPage = nextPage,
StartCusor = Base64Encode(backwardsCursor),
EndCursor = Base64Encode(forwardsCursor)
};
}
private static U GetPropertyValue<T, U>(T obj, string propertyName) {
return (U)obj.GetType().GetProperty(propertyName).GetValue(obj, null);
}
public static string Base64Encode(string plainText) {
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
public static string Base64Decode(string base64EncodedData) {
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
}

Encountered array-valued parameter binding, but was expecting [java.lang.String (n/a)]

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));

Add OR condition to query

I am wondering how it is possible to add an OR condition to the Envers criteria api:
public IEnumerable<Guid> GetHistory(object id, params string[] props)
{
var auditQuery = AuditReaderFactory.Get(Session).CreateQuery()
.ForRevisionsOfEntity(typeof(T), false, true);
foreach (var prop in props)
{
auditQuery.Add(AuditEntity.RelatedId(prop).Eq(id)); // <-- adds AND, while OR is required!
}
return auditQuery
.GetResultList<object[]>()
.Select(i => ((T)i[0]).ID)
.Distinct();
}
Use AuditEntity.Disjunction().
In your example, something like...
[..]
var disjunction = AuditEntity.Disjunction();
foreach (var prop in props)
{
disjunction.Add(AuditEntity.RelatedId(prop).Eq(id));
}
auditQuery.Add(disjunction);
[..]
I did like this in Java as #Roger mentioned above. (Just in case if anybody needs)
public List<Employee> getAuditHistory(Session session, int id, String property) {
AuditReader auditReader = AuditReaderFactory.get(session);
List<Employee> employeeHistory = new ArrayList<>();
if (auditReader != null) {
AuditQuery auditQuery = auditReader.createQuery().forRevisionsOfEntity(Employee.class, true, false)
.add(AuditEntity.property(ResultsConstants.Employee_ID).eq(id));
AuditDisjunction auditDisjunction = null;
if (property.equalsIgnoreCase("FULL_NAME")) {
auditDisjunction = AuditEntity.disjunction().add(AuditEntity.property("FIRST_NAME".toUpperCase()).hasChanged())
.add(AuditEntity.property("LAST_NAME".toUpperCase()).hasChanged());
} else {
auditQuery = auditQuery.add(AuditEntity.property(property.toUpperCase()).hasChanged());
}
auditQuery = auditQuery.addOrder(AuditEntity.property("MODIFIED_DATE").desc());
if(null != auditDisjunction){
auditQuery = auditQuery.add(auditDisjunction);
}
if (auditQuery != null) {
if (auditQuery.getResultList().isEmpty()) {
// Log here or throw it back to caller
}
employeeHistory.addAll(auditQuery.getResultList());
}
}
return employeeHistory;
}

entityframework search value only to search not to update

I build a Repository just like Codefirst demo.
public virtual IQueryable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query);
}
else
{
return query;
}
}
But this search has a problem.
Sometimes other person use 'get' function to search value and this value will be used as param inside other function.
Just like this:
UserInfoBll userInfoBll = new UserInfoBll();
UserInfo userInfo = userInfoBll.Get(p => p.UserCode == "8001", null, "CorpShop").First();
ConsumeProcess.process(userInfo);
If userInfo value change in function ConsumeProcess.process when I process savechange.
It will update something I don't want to update, so I want to find a way. The search value is just for search, when value change it, not to update the value.
For this way I wrote this:
public virtual List<TEntity> GetList(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
List<TEntity> tEntityList = Get(filter, orderBy, includeProperties).ToList();
SetEntityStates(EntityState.Detached, tEntityList);
return tEntityList;
}
protected void SetEntityStates(EntityState entityState, IEnumerable<TEntity> tEntity_params)
{
foreach (TEntity tEntity in tEntity_params)
{
if (tEntity != null)
{
context.Entry(tEntity).State = entityState;
}
}
}
Now it won't update, if somebody changed search value. But there is another problem.
If I use code like this, the property which is included can't get
UserInfoBll userInfoBll = new UserInfoBll();
userInfo = userInfoBll.GetList(p => p.UserCode == "8001", null, "CorpShop").First(); // CorpShop is include property
corpShop = userInfo.CorpShop; //userInfo.CorpShop is null
If you have an IQueryable, the easiest way to get entities disconnected from db is to use the AsNoTracking method.
public virtual List<TEntity> GetList(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
return = Get(filter, orderBy, includeProperties).AsNoTracking().ToList();
}

Pagination not working for a Lazy Loaded Data Table on First Loading

I am using JPA named queries for Loading a Lazy Loaded DataTable. and setting first and Max results as shown below.
Query query = entityManager.createNamedQuery("StudyplanCategory.findByStatusAndLimit");
int end=(start*pageNumber);
query.setParameter("status", status);
query.setParameter("start", start);
query.setParameter("end", end);
query.setMaxResults(end - start);
The load method is given below:
public List<StudyplanCategory> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String,String> filters) {
List<StudyplanCategory> data = new ArrayList<StudyplanCategory>();
//System.out.println("Page First Value:"+first+"PageSize Value:"+pageSize);
datasource=categoryService.findDynaEditStudyPlan("NOT_USER_SPECIFIC",first,pageSize);
//filter
for(StudyplanCategory studyplanCategory : datasource) {
boolean match = true;
for(Iterator<String> it = filters.keySet().iterator(); it.hasNext();) {
try {
String filterProperty = it.next();
String filterValue = filters.get(filterProperty).toLowerCase();
String fieldValue = String.valueOf(studyplanCategory.getClass().getDeclaredField(filterProperty).get(studyplanCategory)).toLowerCase();
//System.out.println("fieldValue............."+fieldValue);
if(filterValue == null || fieldValue.startsWith(filterValue)) {
match = true;
}
else {
match = false;
break;
}
} catch(Exception e) {
match = false;
System.out.println("The Exception occured at"+e);
}
}
if(match) {
data.add(studyplanCategory);
}
}
//sort
if(sortField != null) {
Collections.sort(data, new LazySorter(sortField, sortOrder));
}
//rowCount
int dataSize = data.size();
this.setRowCount(dataSize);
//paginate
if(dataSize > pageSize) {
try {
return data.subList(first, first + pageSize);
}
catch(IndexOutOfBoundsException e) {
return data.subList(first, first + (dataSize % pageSize));
}
}
else {
return data;
}
}
But when the table is loaded Next Buttons are not active because I am loading only those data required to load the first page. How can I Solve this.
You need to fire another query which sets the total rowcount. Basically, in LazyDataModel#load():
public List<StudyplanCategory> load(...) {
setRowCount(studyplanCategoryService.count());
return studyplanCategoryService.list(...);
}
Unrelated to the concrete problem, you should actually be using Query#setFirstResult() to set the first record index.