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
Related
I have to write a method where I pass it some arguments and depending on which arguments are empty it modifies the query I need to execute at the end.
E.g. if I want to select from a table Customer that has an ID, name, surname and address the starting query would be SELECT * FROM Customer WHERE ID=myId AND name=myName AND surname=mySurname (and now we check if the address I sent is empty, if it is NOT, add it to the query).
I know how to do this in C#, by simply doing a starting string and then just adding to it and using ExecuteStoreQuery directly, but how would this look in Java? Is Criteria Builder a viable tool for this?
Thank you so much!
Using a CriteriaQuery, it is possible to do something like that (using Static JPA Metamodel Classes):
public List<Customer> findCustomer(Long myId, String myName, String mySurName, String myAddress) {
CriteriaBuilder cb = enitityManager.getCriteriaBuilder();
CriteriaQuery<Customer> query = cb.createQuery(Customer.class);
Root<Customer> root = query.from(Customer.class);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get(Customer_.ID), myId));
predicates.add(cb.equal(root.get(Customer_.NAME), myName));
predicates.add(cb.equal(root.get(Customer_.SURNAME), mySurName));
if(address != null) {
predicates.add(cb.equal(root.get(Customer_.ADDRESS), myAddress));
}
query.select(root).where(cb.and(predicates.toArray(new Predicate[0])));
return entityManager.createQuery(query).getResultList();
}
In this case, the address will only be checked if it is not null.
Another example without using Static JPA Metamodel (not 100% sure about this):
public List<Customer> findCustomer(Long myId, String myName, String mySurName, String myAddress) {
CriteriaBuilder cb = enitityManager.getCriteriaBuilder();
CriteriaQuery<Customer> query = cb.createQuery(Customer.class);
Root<Customer> root = query.from(Customer.class);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get("ID"), myId));
predicates.add(cb.equal(root.get("name"), myName));
predicates.add(cb.equal(root.get("surename"), mySurName));
if(address != null) {
predicates.add(cb.equal(root.get("address"), myAddress));
}
query.select(root).where(cb.and(predicates.toArray(new Predicate[0])));
return entityManager.createQuery(query).getResultList();
}
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();
}
I need to return the latest data. Date fields are called dateModified. Since the old data is not deleted in any way, there are a lot of similar records, they differ only in the dateModified field. The current query returns all records, and I need only the most recent in the field dateModified.
public List<SomeDto> search(SearchDto criteria) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<SomeDto> query = cb.createQuery(SomeDto.class);
final Root<SomeModel> root = query.from(SomeModel.class);
query.multiselect(
root.get(ID),
root.get(SOME_ENTERNAL_ID),
root.get(SOME_NAME_ID));
query.where(createPredicates(cb, query, root, criteria));
return entityManager.createQuery(query).getResultList();
}
Try to order all the results in descending order and get the first result, something like this:
public List<SomeDto> search(SearchDto criteria) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<SomeDto> query = cb.createQuery(SomeDto.class);
final Root<SomeModel> root = query.from(SomeModel.class);
query.multiselect(
root.get(ID),
root.get(SOME_ENTERNAL_ID),
root.get(SOME_NAME_ID));
query.where(createPredicates(cb, query, root, criteria))
.orderBy(cb.desc(root.get("dateModified")));
return entityManager.createQuery(query)
.setFirstResult(0)
.setMaxResults(1)
.getResultList();
}
The method will return a list with one element which has the most recent dateModified.
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 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)