I am stuck with this problem
I want to create Criteria Query which translates to
select * from table
where (col1,col2) in ((val1,val2),(val2,val3),....)
I understood how to create it for IN clause on single column
But I want to have IN clause on combination of two columns where values in IN clause is list of tuple of values for these two columns.
Kindly suggest.
EntityManager entityManager;
public List<YourEntity> getEntitiesByParams(Map<Field1Class, Field2Class> params) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<YourEntity> query = cb.createQuery(YourEntity.class);
Root<YourEntity> root = query.from(YourEntity.class);
final List<Predicate> predicates = new ArrayList<>();
for(Entry<Field1Class, Field2Class> entry : params.entrySet()) {
Predicate predicate1 = cb.equal(root.get("field1"), params.getKey());
Predicate predicate2 = cb.equal(root.get("field2"), params.getValue());
predicates.add(
cb.and(predicate1, predicate2)
);
}
query.select(root)
.where(
cb.or(predicates.toArray(new Predicate[predicates.size()]))
);
return entityManager.createQuery(query).getResultList();
}
Related
Basically I am trying to select a field where I calculate with using a Postgres postgis query ST_distance_sphere.
Below code allows me to filter out results based on the distance. If the distance is greater than the given value record is filtered. I created the following query with the below predicate builder. However I also want to select the distance value returned by the ST_distance_sphere function as "distance"
Here is how I use JPQL:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<RestaurantEntity> criteriaQuery = criteriaBuilder.createQuery(RestaurantEntity.class).distinct(true);
Root<RestaurantEntity> restaurantEntityRoot = criteriaQuery.from(RestaurantEntity.class);
Predicate[] searchPredicates = createPredicates(criteriaBuilder, restaurantEntityRoot, restaurantSearch);
List<Order> orders = getSortingOrders(restaurantSearch, criteriaBuilder, restaurantEntityRoot);
criteriaQuery.where(searchPredicates).orderBy(orders);
List<RestaurantEntity> searchResults = entityManager.createQuery(criteriaQuery)
.setMaxResults(restaurantSearch.getSize())
.setFirstResult((restaurantSearch.getPage()) * restaurantSearch.getSize())
.getResultList();
Here is searchByDistance function and JpaRestaurantPredicateBuilder class I use.
class JpaRestaurantPredicateBuilder {
private CriteriaBuilder criteriaBuilder;
private Root<RestaurantEntity> restaurantEntityRoot;
private List<Predicate> predicateList;
JpaRestaurantPredicateBuilder(CriteriaBuilder criteriaBuilder, Root<RestaurantEntity> restaurantEntityRoot) {
this.criteriaBuilder = criteriaBuilder;
this.restaurantEntityRoot = restaurantEntityRoot;
this.predicateList = new ArrayList<>();
}
JpaRestaurantPredicateBuilder searchByDistance(Float lon, Float lat, Long maxDistance) {
Point userLocation = new GeometryFactory().createPoint(new Coordinate(lon, lat));
Join address = this.restaurantEntityRoot.join("addressEntity", JoinType.INNER);
predicateList.add(new WithinDistancePredicate((CriteriaBuilderImpl) criteriaBuilder, address.get("location"), userLocation, maxDistance));
return this;
}
Predicate[] buildArray() {
return predicateList.toArray(new Predicate[0]);
}
}
Here is createPredicates:
private Predicate[] createPredicates(CriteriaBuilder criteriaBuilder, Root<RestaurantEntity> restaurantEntityRoot, RestaurantSearch restaurantSearch) {
return new JpaRestaurantPredicateBuilder(criteriaBuilder, restaurantEntityRoot)
.chainId(restaurantSearch.getChainId())
.searchTerm(retrieveRestaurantIdsForSearchTerm(restaurantSearch.getSearchTerm()))
.cityIdAndDistrictId(restaurantSearch.getCityId(), restaurantSearch.getDistrictId())
.status(restaurantSearch.getStatus())
.searchByDistance(restaurantSearch.getLon(), restaurantSearch.getLat(), restaurantSearch.getMaxDistance())
.buildArray();
}
And here is the generated sql:
select distinct restaurant0_.id
from restaurant restaurant0_
inner join address addressent1_ on restaurant0_.address_id = addressent1_.id
where st_distance_sphere(addressent1_.location, ?) < 1000
order by restaurant0_.id desc
limit ?
Here is the sql I want to generate using JPA (note the line where I select the distance):
select distinct restaurant0_.id,
st_distance_sphere(addressent1_.location, ?) as distance
from restaurant restaurant0_
inner join address addressent1_ on restaurant0_.address_id = addressent1_.id
where st_distance_sphere(addressent1_.location, ?) < 1000
order by restaurant0_.id desc
limit ?
I couldn't find any other answers related with this question. How can I achieve this query using JPA? I tried to use CriteriaQuery's select method to no avail.
I have two java objects.
User(every user has an index column)
Address( every address has an user_index column too)
I have a List of all the users index list, usersIndexList as given input and I want to fetch all of the address objects based on this usersIndexList. I found an example on another thread. And tried to follow it but it does not work.
JPA CriteriaBuilder - How to use "IN" comparison operator
My code:
List<String> usersIndexList= new ArrayList<String> ();
for (User u : usersList) {
usersIndexList.add(u.getIndex());
}
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<User> subQuery = criteriaBuilder.createQuery(User.class);
Root<User> fromUser= subQuery.from(User.class);
Expression<String> exp = fromUser.get("user_index");
Predicate predicate = exp.in(usersIndexList);
subQuery.where(predicate);
TypedQuery<User> query = getEntityManager().createQuery(subQuery);
return query.getResultList();
But this query is not returning the desired result :(
Can someone please tell me, where I am doing wrong or give me an alternate solutions if its possible via nativequery or namedquery or any other way
As per your question, you want to fetch all of the address objects based on this usersIndexList. But in your code you are selecting User objects, not the Address. Is my understanding Correct? If yes, then please change your root to Address as below -
List<String> usersIndexList= new ArrayList<String> ();
for (User u : usersList) {
usersIndexList.add(u.getIndex());
}
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Address> subQuery = criteriaBuilder.createQuery(Address.class);
Root<Address> fromAddress= subQuery.from(Address.class);
Expression<String> exp = fromAddress.get("user_index");
Predicate predicate = exp.in(usersIndexList);
subQuery.where(predicate);
TypedQuery<Address> query = getEntityManager().createQuery(subQuery);
return query.getResultList();
I'm trying to find some results from my Oracle database and because of the data model, I have repeated data which I don't want to be shown.
In order to do this, I thought using DISTINCT in my CriteriaQuery should do the trick. But here I encountered the problem:
The distinct has to be made on 2 columns and this makes the code to throw the following exception: "ORA-00909: invalid number of arguments".
After some google search, found that using this things: || to separate the columns works, but I don't have a single clue to do this either.
So:
The correct way to distinct my data is like this:
SELECT DISTINCT column1, column2 FROM ...
But Hibernate is building it like this:
SELECT DISTINCT COUNT( DISTINCT column1, column2) FROM
The latter query is the one that throws the exception. Is there a way to tell Hibernate to do the first of the two queries?
Thanks in advance.
EDIT
The code involved is this:
return new Specification<ProfilingInstructionAccessEntity>() {
#Override
public Predicate toPredicate(Root<ProfilingInstructionAccessEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
final List<Predicate> predicates = new ArrayList<>();
List<FilterDTO> activeFilters = filtersService.findAllActive();
filters.forEach((k, v) -> {
if (!StringUtils.isBlank(v)) {
SpecificationHelperEnum helperEnum = SpecificationHelperEnum.getByKey(k);
SpecFilter filter = (SpecFilter) appContext.getBean(helperEnum.getFilter());
// query.select(root.get("concated")).distinct(true); <-- This line is what I tried. Also tried this: query.distinct(true);
Predicate predicate = filter.createSmartPredicate(root, cb, v, helperEnum.getFilterId(), activeFilters);
predicates.add(predicate);
}
});
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
#Override
public Predicate createSmartPredicate(Root<ProfilingInstructionAccessEntity> root, CriteriaBuilder cb, String value, Integer filterId, List<FilterDTO> activeFilters) {
List<AssetDTO> assets = service.findBySpec("origin", value);
List<Predicate> assetPredicates = new ArrayList<>();
Join<Object, Object> aux = root.join(SearchPathEnum.ACCESS.getPath()).join(SearchPathEnum.ACCESS_FILTERS.getPath());
for (AssetDTO assetDTO : assets) {
List<Predicate> filterPredicates = new ArrayList<>();
for (FilterDTO filterDTO : activeFilters) {
filterPredicates.add(buildPredicate(assetDTO, filterDTO.getMappedValue(), aux, cb));
}
assetPredicates.add(cb.or(filterPredicates.toArray(new Predicate[filterPredicates.size()])));
}
return cb.or(assetPredicates.toArray(new Predicate[assetPredicates.size()]));
}
private Predicate buildPredicate(AssetDTO assetDTO, String mappedValue, Join<Object, Object> aux, CriteriaBuilder cb) {
Predicate filterCode = cb.equal(aux.get(SearchPathEnum.FILTER.getPath()).get(SearchPathEnum.MAPPED_VALUE.getPath()), mappedValue);
String value = (String) ReflectionComponent.invokeByReflection(assetDTO, "get" + StringUtils.capitalize(mappedValue), null);
Predicate filterValue = cb.like(aux.get(SearchPathEnum.FILTER_VALUE.getPath()), "%" + value.trim() + "%");
return cb.and(filterCode, filterValue);
}
It's a bit complex but in the end it is something like a 'smart' search. I have like 20 filters and any single one of them trigger the search by the others.
If more information is needed please feel free to ask for it. Thanks!
count(distinct column1, column2) is invalid.
That would, actually, be
select count(*)
from (select distinct column1, column2 from some_table);
I don't know hibernate, but - maybe code posted above rings your bell :)
Register new function in Dialect:
registerFunction("count_distinct", new SQLFunctionTemplate(IntegerType.INSTANCE, "count(distinct ?1)"));
And concatenate:
select count_distinct(column1 || column2) from some_table
I have the following native SQL query that I am trying to convert to JPA criteria:
select et.* from t_empl_tx et, t_dept d
where et.assigned_dept = d.dept (+)
and et.employee_id = :employee_id
and (et.start_date >= d.dept_est_date and
et.start_date <= d.dept_close_date or
et.start_date is null or
d.dept is null)
(Note that (+) is roughly equivalent to a left outer join in this case. Yes, I know it denotes the OPTIONAL table, etc, etc).
Here is my attempt at the code:
EntityManager entityManager = getEntityManager();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<EmployeeTransaction> criteriaQuery = criteriaBuilder.createQuery(EmployeeTransaction.class);
Root<EmployeeTransaction> root = criteriaQuery.from(EmployeeTransaction.class);
// this line bombs!
Join<EmployeeTransaction, Department> join =
root.join(EmployeeTransaction_.assignedDepartment).join(Department_.id).join(DepartmentCompositeId_.department, JoinType.LEFT);
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get(EmployeeTransaction_.id).get(EmployeeTransactionCompositeId_.employeeId), employeeId));
predicates.add(criteriaBuilder.or(
criteriaBuilder.and(
criteriaBuilder.greaterThanOrEqualTo(root.<Date>get(EmployeeTransaction_.requestedStartDate), join.get(Department_.id).<Date>get(DepartmentCompositeId_.departmentCreationDate)),
criteriaBuilder.lessThanOrEqualTo(root.<Date>get(EmployeeTransaction_.requestedStartDate), join.<Date>get(Department_.departmentCloseDate))
),
criteriaBuilder.isNull(root.get(EmployeeTransaction_.requestedStartDate)),
criteriaBuilder.isNull(join.get(Department_.id).get(DepartmentCompositeId_.departmentCreationDate))
));
criteriaQuery.select(root).where(predicates.toArray(new Predicate[]{}));
TypedQuery<EmployeeTransaction> query = entityManager.createQuery(criteriaQuery);
List<EmployeeTransaction> result = query.getResultList();
This issue seems to be that I'm trying to join a string column, assigedDepartment, to a single field of a composite ID. This is perfectly legal in SQL, but not so easy in the code.
One option is to convert to a number of subqueries, which seems to kill the point of the left outer join entirely.
Can anyone point out what I'm doing wrong?
Jason
You should post your entities so that the answers can be more specific.
However, I'll give a try.
If I am right, you can rewrite the query:
select et.*
from t_empl_tx et
left join t_dept d on et.assigned_dept = d.dept
where
et.employee_id = :employee_id
and (
et.start_date >= d.dept_est_date
and et.start_date <= d.dept_close_date
or et.start_date is null
or d.dept is null)
So, shortly, you have to move the JoinType.LEFT to assignedDepartment join:
EntityManager entityManager = getEntityManager();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<EmployeeTransaction> criteriaQuery = criteriaBuilder.createQuery(EmployeeTransaction.class);
Root<EmployeeTransaction> root = criteriaQuery.from(EmployeeTransaction.class);
Join<EmployeeTransaction, Department> department = root.join(EmployeeTransaction_.assignedDepartment, JoinType.LEFT);
Path<Date> employeeTransactionRequestedStartDate = root.get(EmployeeTransaction_.requestedStartDate);
Path<DepartmentCompositeId> departmentId = department.get(Department_.id);
Path<Date> departmentCreationDate = departmentId.get(DepartmentCompositeId_.departmentCreationDate)
Path<Date> departmentCloseDate = departmentId.get(DepartmentCompositeId_.departmentCloseDate)
criteriaQuery.select(root).where(
criteriaBuilder.equal(root.get(EmployeeTransaction_.id).get(EmployeeTransactionCompositeId_.employeeId), employeeId),
criteriaBuilder.or(
criteriaBuilder.and(
criteriaBuilder.greaterThanOrEqualTo(employeeTransactionRequestedStartDate, departmentCreationDate)),
criteriaBuilder.lessThanOrEqualTo(employeeTransactionRequestedStartDate, departmentCloseDate)
),
criteriaBuilder.isNull(employeeTransactionRequestedStartDate),
criteriaBuilder.isNull(departmentCreationDate)
)
);
TypedQuery<EmployeeTransaction> query = entityManager.createQuery(criteriaQuery);
List<EmployeeTransaction> result = query.getResultList();
I am trying to select a single column from a related table. I have a table (Item) with many Values. I would like to select Value.valueString.
Basically, the query is supposed to pass in a bunch of values and pull any ValueFields that contain those values. The SQL might look something like this:
select ItemValues.valueString from ItemEntity
join StockItem on ItemEntity.stockItemId = StockItem.id
join ItemValues on ItemEntity.id = ItemValues.itemId
where StockItem.vendor = vendorId
AND (ItemValues.valueString like '%test%' OR ItemValues.valueString like '%test2%'...);
Here is my code:
final CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
final CriteriaQuery<String> query = builder.createQuery(String.class);
final Root<ItemEntity> root = query.from(ItemEntity.class);
query.select(root.join("ItemValues").<String>get("ValueString"));
final List<Predicate> filters = new LinkedList<Predicate>();
filters.add(builder.equal(root.join("StockItem").get("id"), vendorNumber));
final List<Predicate> filterNamesCriteria = new LinkedList<Predicate>();
if (filenames.length > 0) {
for (String fileName : filenames) {
filterNamesCriteria.add(builder.like(root.join("ItemValues").<String>get("ValueString"), fileName));
}
filters.add(builder.or(filterNamesCriteria.toArray(new Predicate[0])));
}
query.where(filters.toArray(new Predicate[0]));
final TypedQuery<String> resolvedQuery = this.entityManager.createQuery(query);
return resolvedQuery.getResultList();
I want the result to return a List of Strings (valueString column), but it's not returning anything.
Am I doing something wrong? When I say "builder.createQuery(String.class)", is that correct?
I found the problem:
filters.add(builder.equal(root.join("StockItem").get("id"), vendorNumber));
I was joining based on the StockItem id and not the StockItem.itemNumber
I used two queries to solve the issue of joining the Itemvalues map (it was returning 32,000+ results)