So I have tables comment and author. I want to build complex search bar with many optional parameters. I want filter comments with optional author firstname/lastname and some flags like popularity(based on comment rating).
As I dont know how to write it using spring data jpa repository I ve been thinking on writing it as native query with @Query annotation, smth like this should work
Select c.* from comment c join author a on a.id = c.author_id
Where (:firstname = '' or (:firstname = a.firstname))
And (:lastname = '' or (:lastname = a.lastname))
And (:popular = false or (c.rating > 25))
Is there option to write it using spring data jpa?
In future I plan to add more parameters and pagination for example. Using spring it would be like 1 minute with sql query I will lost few hours.
Is there are some best practices in such cases?
I suggest to use JpaSpecificationExecutor
repository method findAll(Specification<T> spec, Pageable pageable)
. This solution allows you to extend the parameters list using the same repository and service API
Entities:
@Entity
@Table(name = "author")
public class Author {
@Id
@GeneratedValue
@Column(name = "id")
private Long id;
@Column(name = "firstname")
String firstname ;
@Column(name = "lastname")
String lastname ;
// getters, setters, equals, hashcode, toString ...
}
@Entity
@Table(name = "comment")
public class Comment {
@Id
@GeneratedValue
@Column(name = "id")
private Long id;
@ManyToOne
@JoinColumn(name = "author_id")
Author author;
@Column(name = "rating")
Integer rating;
// getters, setters, equals, hashcode, toString ...
}
Repository:
@Repository
public interface CommentRepository
extends JpaRepository<Comment, Long>, JpaSpecificationExecutor<Comment> {
}
Specifications: org.springframework.data.jpa.domain.Specification
public class CommentSpecs {
/** if firstname == null then specification is ignored */
public static Specification<Comment> authorFirstnameEquals(String firstname) {
return (root, query, builder) ->
firstname == null ?
builder.conjunction() :
builder.equal(root.get("author").get("firstname"), firstname);
}
/** if lastname == null then specification is ignored */
public static Specification<Comment> authorLastnameEquals(String lastname) {
return (root, query, builder) ->
lastname == null ?
builder.conjunction() :
builder.equal(root.get("author").get("lastname"), lastname);
}
/** if rating == null then specification is ignored */
public static Specification<Comment> ratingGreaterThan(Integer rating) {
return (root, query, builder) ->
rating == null ?
builder.conjunction() :
builder.greaterThan(root.get("rating"), rating);
}
}
Service method parameters:
public class CommentParameters {
String authorFirstname;
String authorLastname;
Integer rating;
// getters, setters
}
All parameters are nullable. You can set the parameters you need only. If the parameter is null it is ignored by our specifications
Service:
@Service
public class CommentService {
@Autowired
CommentRepository repository;
public List<Comment> getComments(CommentParameters params, Pageable pageable) {
Specification spec1 = CommentSpecs.authorFirstnameEquals(params.getAuthorFirstname());
Specification spec2 = CommentSpecs.authorLastnameEquals(params.getAuthorLastname());
Specification spec3 = CommentSpecs.ratingGreaterThan(params.getRating());
Specification spec = Specifications.where(spec1).or(spec2).or(spec3);
return repository.findAll(spec, pageable);
}
}
I have written the code using a text editor, so it needs a revision. But I think the main point is easy to spot
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