I'm using Spring Boot with Spring JPA and Specification Executor. I have my Specification/Predicate combo successfully searching the simple attributes within my class. However, I am having difficulties searching the objects within. Do they need a separate Specification? I have a class that has 2 Many To One mapping classes within and would like to search those fields from within the same class.
Predicate Implementation
public Specification<User> getSpecification(SpecificationField field, Object searchCriteria){
return new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
if(searchCriteria instanceof String){
searchCriteria.toString().trim().toLowerCase();
}
switch(field){
case USER_GROUP:
return cb.equal(cb.lower(root.<String> get("group").get("name")), searchCriteria);
case USER_ROLE:
return cb.equal(cb.lower(root.<String> get("role").get("name")), searchCriteria);
case USER_EMAIL:
return cb.equal(cb.lower(root.<String> get("email")), searchCriteria);
case USER_FIRSTNAME:
return cb.equal(cb.lower(root.<String> get("firstName")), searchCriteria);
case USER_LASTNAME:
return cb.equal(cb.lower(root.<String> get("lastName")), searchCriteria);
case USER_USERNAME:
return cb.equal(cb.lower(root.<String> get("username")), searchCriteria);
default:
assert true;
return null;
}
User Class
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long id;
@Column(name = "USER_FIRSTNAME", nullable = false)
private String firstName;
@Column(name = "USER_LASTNAME", nullable = false)
private String lastName;
@Column(name = "USER_EMAIL", nullable = false, unique = true)
private String email;
@Column(name = "USER_USERNAME", nullable = false, unique = true)
private String username;
@Column(name = "USER_PASSWORD", nullable = false)
private String encryptedPassword;
@ManyToOne
@JoinColumn(name = "USER_ROLE", nullable = false)
private Role role;
@ManyToOne
@JoinColumn(name = "USER_GROUP", nullable = false)
private Group group;
Role Class
@Id
@GeneratedValue
@Column(name = "ROLE_ID")
private Long id;
@Column(name = "ROLE_NAME", nullable = false, unique = true)
private String name;
Group Class
@Id
@GeneratedValue
@Column(name = "GROUP_ID")
private Long id;
@Column(name = "GROUP_NAME", nullable = false, unique = true)
private String name;
@Column(name = "GROUP_TOKEN", nullable = false, unique = true)
private String token;
EDIT:
Came up with this Predicate implementation for the nested objects.
Root<Group> group = query.from(Group.class);
Root<Role> role = query.from(Role.class);
switch(field){
case USER_GROUP:
return cb.equal(cb.lower(group.<String> get("name")), searchCriteria);
case USER_ROLE:
return cb.equal(cb.lower(role.<String> get("name")), searchCriteria);
This is working better, but not great. I have the possible group name values of DEV, UAT, or PROD. If I give any of these I get all the Users in my Database. If I don't I don't get any Users back. How can I do a text search on the name of this nested object?
Thanks.
Spring Data JPA Specifications allow us to create dynamic database queries by using the JPA Criteria API. It defines a specification as a predicate over an entity. Spring has a wrapper around the JPA criteria API (that uses predicates) and is called the specification API.
The JpaSpecificationExecutor<T> interface declares the methods that can be used to invoke database queries that use the JPA Criteria API. This interface has one type parameter T that describes the type of the queried entity.
SPring Data Jpa Specifications helps us to create dynamic queries based on the requirement at run time. Spring Data Jpa Specifications allows a combination of the attributes or properties of a domain or entity class and creates a query.
I found the answer myself, but wanted to share with everybody so that it may help other people with the same issue.
@Override
public Predicate toPredicate(Root<User> userRoot, CriteriaQuery<?> query, CriteriaBuilder cb) {
switch(field){
case USER_GROUP:
Join<User, Group> groupJoin = userRoot.join("group");
return cb.equal(cb.lower(groupJoin.<String> get("name")), searchCriteria);
case USER_ROLE:
Join<User, Role> roleJoin = userRoot.join("role");
return cb.equal(cb.lower(roleJoin.<String> get("name")), searchCriteria);
Using the Join class from the javax.persistence library I could search the nested objects within my class with the same Specification.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With