I am trying to build a specifications to predicate subquery for the below query.
Select u.* from User u where u.login in (select ur.role_id from userRoles ur where ur.role_Id = roleId).
till now this is the part I have built
public static Specification<User> userRoleId(String roleId) {
return new Specification<User>() {
#Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
//return builder.equal(root.<LocalDate> get("scheduledDisbursementDate"), scheduledDisbursementDate);
Subquery<UserRole> subquery = query.subquery(UserRole.class);
Root<UserRole> subqueryRoot = subquery.from(UserRole.class);
subquery.select(subqueryRoot);
Predicate roleIdList = builder.equal(subqueryRoot.get("roleId"), roleId);
subquery.select(subqueryRoot).where(roleIdList);
return builder.exists(subquery);
}
};
}
Can you please help me out to link the subquery with main query.
Note: There are no joins defined in the Entity classes. evrything should be done through subquerys only
Found Answer
first predicate will join UserRole userId column with and User table login column.
Second predicate will filter the condition based on roleId.
public static Specification<User> userRoleId(String roleId) {
return new Specification<User>() {
#Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Subquery<UserRole> subquery = query.subquery(UserRole.class);
Root<UserRole> subqueryRoot = subquery.from(UserRole.class);
subquery.select(subqueryRoot);
Predicate userIdPredicate = builder.equal(subqueryRoot.get("userId"), root.<String> get("login"));
Predicate rolePredicate = builder.equal(subqueryRoot.get("roleId"), roleId);
subquery.select(subqueryRoot).where(userIdPredicate, rolePredicate);
return builder.exists(subquery);
}
};
}
Related
public Page<MyObject> findByCriteria(MySearchFilters mySearchFilters, PageRequest pageRequest) {
Page<MyObject> all = myObjectRepository.findAll(new Specification<MyObject>() {
#Override
public Predicate toPredicate(Root<MyObject> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
................ here is lots of different optional fields that might or not get added to query
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
private void addLikeCriteria(String field, String fieldName, CriteriaBuilder criteriaBuilder, Root<MyObject> root, List<Predicate> predicates) {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get(fieldName).as(String.class)), "%" + field.toLowerCase() + "%"));
}
}, pageRequest);
return all;
}
I am calling the method which in the background does the also count(field) query that is very costly! I analyzed it and if it would do count(*) instead it would be 10x faster as then it does not have to check the value.
Page<T> findAll(#Nullable Specification<T> spec, Pageable pageable);
Is it possible to make it count() instead or should I make a custom Specification, pageable repository method that would not do the count(field) and do the count() myself to attach to the result?
Here is my code.
#RequestMapping("/datatable")
#ResponseBody
public DataTablesOutput<Person> listPersons(DataTablesInput input, HttpSession session,
HttpServletRequest request, #RequestParam Map<String, String> allRequestParams) {
// create specification
Specification<Person> specification = new Specification<Person>() {
#Override
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
// define predicates
List<Predicate> predicates = new ArrayList<Predicate>();
// PREDICATE TO GET RECORD OF SPECIFIC MONTH FOR EXAMPLE IF I PASS BIRTH MONTH LIKE '05'
// IT WILL RETURN ALL THE BIRTHDATE RECORD OF 'MAY' MONTH
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
DataTablesOutput<Person> persons = employeeMasterService.findAll(input, specification);
return persons;
}
If I pass the month number for example '05'. It should return all the record of 'May' month of database. I am new in criteria query so please help me.
You can use the CriteriaBuilder#function() method.
In Criteria API following method of CriteriaBuilder can be used to execute a database function.
public static Specification<Person> personSpecification(int month) {
return (Specification<Person>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder
.equal(criteriaBuilder.function("month", Integer.class, root.get("birthday") ), month);
}
If you send month=5, it will return all person born in the 5th month.
I am passing following json from front end :
{names: 'ABC MKL-56-2,ABC MKL-56-3'};
In service layer,I am trying to run in query with the help of criteria builder as follows :
public List<APDetails> getWP(String names) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<APDetails> query = builder.createQuery(APDetails.class);
Root<APDetails> root = query.from(APDetails.class);
Predicate hasA = builder.in(root.get(APDetails_.names).in(Arrays.asList(names.split(","))));
query.where(builder.and(hasA));
List<APDetails> APs = em.createQuery(query.select(root)).getResultList();
return APs;
}
I am getting following error :
Error message: org.hibernate.hql.internal.ast.QuerySyntaxException:
unexpected token: in near line 1, column 163 [select generatedAlias0
from com.app.ow.APDetails as generatedAlias0 where generatedAlias0.names in (:param0, :param1) in ()]
First of all, if you're using springboot, I suggest you extend the JpaSpecificationExecutor class (check here, here, and here for more information) from your APDetailsRepository (I believe you're using them somewhere...):
public interface APDetailsRepository extends JpaRepository<APDetails, Long>, JpaSpecificationExecutor<APDetails> {
Then, try this:
#Autowired
public APDetailsRepository apDetailsRepository;
........
public List<APDetails> getWP(String names) {
List<String> namesAsList = Arrays.asList(names.split(","));
List<APDetails> listAPDetails = this.apDetailsRepository.findAll(createSpecification(namesAsList));
return listAPDetails;
}
public Specification<APDetails> createSpecification(List<String> names) {
return new Specification<APDetails>() {
private static final long serialVersionUID = 1L;
#Override
public Predicate toPredicate(Root<APDetails> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<Predicate>();
if (names!= null && !names.isEmpty()) {
List<Predicate> predicatesNames = new ArrayList<Predicate>();
for (String name : names) {
predicatesNames.add(builder.equal(root.<String>get("names"), name));
//I believe that the "APDetails_.names" attribute is a String...
}
predicates.add(builder.or(predicatesNames.toArray(new Predicate[] {})));
}
return builder.and(predicates.toArray(new Predicate[] {}));
}
};
}
I have a class and need to get the max version of each setting from database.
#Table(name = "SETTINGS")
class SettingsEntity {
String id;
int version;
String name;
boolean is_deleted;
}
following is the query that i want to execute
Select s.id, s.version, s.name from SETTINGS as s
join (select id, max(version) as version from SETTINGS group by id) as r
on(s.id = r.id and s.version = r.version) where s.is_deleted = false;
I have a specification that needs to run this query.
public class SettingsQuerySpec implements Specification<SettingsEntity>
{
#Override
public Predicate toPredicate(Root<SettingsEntity> settingsEntityRoot, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder)
{
// how to write the query in specification.
}
}
I was working on java using jdo where I used to write query functions like below, which queries from an Entity based on what parameters are passed to the function.
Now Im moving to spring-boot, and want to know if I can achieve the same using spring-boot.Any help or suggestions would be heartfully appreciated.Thank you!!
public List<Result> getQueryResult(int filter1, String filter2,Float filter3,Long id){
Query query = new Query("select from Entity1");
String filter = "id == "+id;
if(filter1 != null){
filter = filter+" && filter1 == "+filter1+";
}
if(filter2 != null){
filter = filter+" && filter2 == '"+filter2+"'";
}
if(filter3 != null){
filter = filter+"filter3 == "+filter3;
}
query.setFIlter(filter);
List<Result> results = query.excute();
return results;
}
You have two options - you can use JPA Criteria Builder or JPA Specifications
class Person {
String firstName;
String lastName;
int age;
}
JPA Criteria Builder
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Person> query = builder.createQuery(Person.class);
Root<Person> root = query.from(Person.class);
Predicate sellAlcohol = builder.ge(root.get(Person_.age), 21);
Predicate toMindy = builder.equal(root.get(Person_.firstName), "Mindy");
Usage
query.where(builder.and(sellAlcohol, toMindy));
em.createQuery(query.select(root)).getResultList();
Specificatons
public PersonSpecifications {
public static Specification<Person> sellAlcohol() {
return new Specification<Person> {
public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
return cb.ge(root.get(Person_.age), 21);
}
};
}
public static Specification<Person> toMindy() {
return new Specification<Person> {
public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
return cb.equal(root.get(Person_.firstName), "Mindy");
}
};
}
}
Usage
personRepository.findAll(where(sellAlcohol()).and(toMindy()));