Skip to content

Commit e9f2463

Browse files
author
Eugen
committed
Merge pull request eugenp#142 from Doha2012/master
JPA specifications more operations
2 parents 4acb3ee + c16add0 commit e9f2463

File tree

8 files changed

+162
-49
lines changed

8 files changed

+162
-49
lines changed

spring-security-login-and-registration/src/main/java/org/baeldung/spring/MvcConfig.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ public void addViewControllers(final ViewControllerRegistry registry) {
3939
registry.addViewController("/logout.html");
4040
registry.addViewController("/homepage.html");
4141
registry.addViewController("/expiredAccount.html");
42-
registry.addViewController("/regitrationConfirm.html");
4342
registry.addViewController("/badUser.html");
4443
registry.addViewController("/emailError.html");
4544
registry.addViewController("/home.html");

spring-security-login-and-registration/src/main/java/org/baeldung/web/controller/RegistrationController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public GenericResponse resendRegistrationToken(final HttpServletRequest request,
129129
final VerificationToken newToken = userService.generateNewVerificationToken(existingToken);
130130
final User user = userService.getUser(newToken.getToken());
131131
final String appUrl = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
132-
final SimpleMailMessage email = constructResetVerificationTokenEmail(appUrl, request.getLocale(), newToken, user);
132+
final SimpleMailMessage email = constructResendVerificationTokenEmail(appUrl, request.getLocale(), newToken, user);
133133
mailSender.send(email);
134134

135135
return new GenericResponse(messages.getMessage("message.resendToken", null, request.getLocale()));
@@ -187,7 +187,7 @@ public GenericResponse savePassword(final Locale locale, @RequestParam("password
187187

188188
// NON-API
189189

190-
private final SimpleMailMessage constructResetVerificationTokenEmail(final String contextPath, final Locale locale, final VerificationToken newToken, final User user) {
190+
private final SimpleMailMessage constructResendVerificationTokenEmail(final String contextPath, final Locale locale, final VerificationToken newToken, final User user) {
191191
final String confirmationUrl = contextPath + "/regitrationConfirm.html?token=" + newToken.getToken();
192192
final String message = messages.getMessage("message.resendToken", null, locale);
193193
final SimpleMailMessage email = new SimpleMailMessage();

spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecification.java

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,44 @@
66
import javax.persistence.criteria.Root;
77

88
import org.baeldung.persistence.model.User;
9-
import org.baeldung.web.util.SearchCriteria;
9+
import org.baeldung.web.util.SpecSearchCriteria;
1010
import org.springframework.data.jpa.domain.Specification;
1111

1212
public class UserSpecification implements Specification<User> {
1313

14-
private final SearchCriteria criteria;
14+
private SpecSearchCriteria criteria;
1515

16-
public UserSpecification(final SearchCriteria criteria) {
16+
public UserSpecification(final SpecSearchCriteria criteria) {
1717
super();
1818
this.criteria = criteria;
1919
}
2020

21-
public SearchCriteria getCriteria() {
21+
public SpecSearchCriteria getCriteria() {
2222
return criteria;
2323
}
2424

2525
@Override
2626
public Predicate toPredicate(final Root<User> root, final CriteriaQuery<?> query, final CriteriaBuilder builder) {
27-
if (criteria.getOperation().equalsIgnoreCase(">")) {
28-
return builder.greaterThanOrEqualTo(root.<String> get(criteria.getKey()), criteria.getValue().toString());
29-
} else if (criteria.getOperation().equalsIgnoreCase("<")) {
27+
switch (criteria.getOperation()) {
28+
case EQUALITY:
29+
return builder.equal(root.get(criteria.getKey()), criteria.getValue());
30+
case NEGATION:
31+
return builder.notEqual(root.get(criteria.getKey()), criteria.getValue());
32+
case GREATER_THAN:
33+
return builder.greaterThan(root.<String> get(criteria.getKey()), criteria.getValue().toString());
34+
case LESS_THAN:
3035
return builder.lessThanOrEqualTo(root.<String> get(criteria.getKey()), criteria.getValue().toString());
31-
} else if (criteria.getOperation().equalsIgnoreCase(":")) {
32-
if (root.get(criteria.getKey()).getJavaType() == String.class) {
33-
return builder.like(root.<String> get(criteria.getKey()), "%" + criteria.getValue() + "%");
34-
} else {
35-
return builder.equal(root.get(criteria.getKey()), criteria.getValue());
36-
}
36+
case LIKE:
37+
return builder.like(root.<String> get(criteria.getKey()), criteria.getValue().toString());
38+
case STARTS_WITH:
39+
return builder.like(root.<String> get(criteria.getKey()), criteria.getValue() + "%");
40+
case ENDS_WITH:
41+
return builder.like(root.<String> get(criteria.getKey()), "%" + criteria.getValue());
42+
case CONTAINS:
43+
return builder.like(root.<String> get(criteria.getKey()), "%" + criteria.getValue() + "%");
44+
default:
45+
return null;
3746
}
38-
return null;
3947
}
4048

4149
}

spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,39 @@
44
import java.util.List;
55

66
import org.baeldung.persistence.model.User;
7-
import org.baeldung.web.util.SearchCriteria;
7+
import org.baeldung.web.util.SearchOperation;
8+
import org.baeldung.web.util.SpecSearchCriteria;
89
import org.springframework.data.jpa.domain.Specification;
910
import org.springframework.data.jpa.domain.Specifications;
1011

1112
public final class UserSpecificationsBuilder {
1213

13-
private final List<SearchCriteria> params;
14+
private final List<SpecSearchCriteria> params;
1415

1516
public UserSpecificationsBuilder() {
16-
params = new ArrayList<SearchCriteria>();
17+
params = new ArrayList<SpecSearchCriteria>();
1718
}
1819

1920
// API
2021

21-
public final UserSpecificationsBuilder with(final String key, final String operation, final Object value) {
22-
params.add(new SearchCriteria(key, operation, value));
22+
public final UserSpecificationsBuilder with(final String key, final String operation, final Object value, final String prefix, final String suffix) {
23+
SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0));
24+
if (op != null) {
25+
if (op == SearchOperation.EQUALITY) // the operation may be complex operation
26+
{
27+
final boolean startWithAsterisk = prefix.contains("*");
28+
final boolean endWithAsterisk = suffix.contains("*");
29+
30+
if (startWithAsterisk && endWithAsterisk) {
31+
op = SearchOperation.CONTAINS;
32+
} else if (startWithAsterisk) {
33+
op = SearchOperation.ENDS_WITH;
34+
} else if (endWithAsterisk) {
35+
op = SearchOperation.STARTS_WITH;
36+
}
37+
}
38+
params.add(new SpecSearchCriteria(key, op, value));
39+
}
2340
return this;
2441
}
2542

@@ -29,7 +46,7 @@ public Specification<User> build() {
2946
}
3047

3148
final List<Specification<User>> specs = new ArrayList<Specification<User>>();
32-
for (final SearchCriteria param : params) {
49+
for (final SpecSearchCriteria param : params) {
3350
specs.add(new UserSpecification(param));
3451
}
3552

spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.baeldung.persistence.model.MyUser;
1414
import org.baeldung.persistence.model.User;
1515
import org.baeldung.web.util.SearchCriteria;
16+
import org.baeldung.web.util.SearchOperation;
1617
import org.springframework.beans.factory.annotation.Autowired;
1718
import org.springframework.data.jpa.domain.Specification;
1819
import org.springframework.http.HttpStatus;
@@ -24,6 +25,7 @@
2425
import org.springframework.web.bind.annotation.ResponseBody;
2526
import org.springframework.web.bind.annotation.ResponseStatus;
2627

28+
import com.google.common.base.Joiner;
2729
import com.google.common.base.Preconditions;
2830
import com.mysema.query.types.expr.BooleanExpression;
2931

@@ -63,10 +65,11 @@ public List<User> findAll(@RequestParam(value = "search", required = false) fina
6365
@ResponseBody
6466
public List<User> findAllBySpecification(@RequestParam(value = "search") final String search) {
6567
final UserSpecificationsBuilder builder = new UserSpecificationsBuilder();
66-
final Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),");
68+
final String operationSetExper = Joiner.on("|").join(SearchOperation.SIMPLE_OPERATION_SET);
69+
final Pattern pattern = Pattern.compile("(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),");
6770
final Matcher matcher = pattern.matcher(search + ",");
6871
while (matcher.find()) {
69-
builder.with(matcher.group(1), matcher.group(2), matcher.group(3));
72+
builder.with(matcher.group(1), matcher.group(2), matcher.group(4), matcher.group(3), matcher.group(5));
7073
}
7174

7275
final Specification<User> spec = builder.build();
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.baeldung.web.util;
2+
3+
public enum SearchOperation {
4+
EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS;
5+
6+
public static final String[] SIMPLE_OPERATION_SET = { ":", "!", ">", "<", "~" };
7+
8+
public static SearchOperation getSimpleOperation(final char input) {
9+
switch (input) {
10+
case ':':
11+
return EQUALITY;
12+
case '!':
13+
return NEGATION;
14+
case '>':
15+
return GREATER_THAN;
16+
case '<':
17+
return LESS_THAN;
18+
case '~':
19+
return LIKE;
20+
default:
21+
return null;
22+
}
23+
}
24+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.baeldung.web.util;
2+
3+
public class SpecSearchCriteria {
4+
5+
private String key;
6+
private SearchOperation operation;
7+
private Object value;
8+
9+
public SpecSearchCriteria() {
10+
11+
}
12+
13+
public SpecSearchCriteria(final String key, final SearchOperation operation, final Object value) {
14+
super();
15+
this.key = key;
16+
this.operation = operation;
17+
this.value = value;
18+
}
19+
20+
public String getKey() {
21+
return key;
22+
}
23+
24+
public void setKey(final String key) {
25+
this.key = key;
26+
}
27+
28+
public SearchOperation getOperation() {
29+
return operation;
30+
}
31+
32+
public void setOperation(final SearchOperation operation) {
33+
this.operation = operation;
34+
}
35+
36+
public Object getValue() {
37+
return value;
38+
}
39+
40+
public void setValue(final Object value) {
41+
this.value = value;
42+
}
43+
44+
}

spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationsTest.java renamed to spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationTest.java

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
import org.baeldung.persistence.dao.UserSpecification;
1111
import org.baeldung.persistence.model.User;
1212
import org.baeldung.spring.PersistenceConfig;
13-
import org.baeldung.web.util.SearchCriteria;
13+
import org.baeldung.web.util.SearchOperation;
14+
import org.baeldung.web.util.SpecSearchCriteria;
1415
import org.junit.Before;
1516
import org.junit.Test;
1617
import org.junit.runner.RunWith;
@@ -25,7 +26,7 @@
2526
@ContextConfiguration(classes = { PersistenceConfig.class })
2627
@Transactional
2728
@TransactionConfiguration
28-
public class JPASpecificationsTest {
29+
public class JPASpecificationTest {
2930

3031
@Autowired
3132
private UserRepository repository;
@@ -51,51 +52,68 @@ public void init() {
5152
repository.save(userTom);
5253
}
5354

54-
@Test
55-
public void givenLast_whenGettingListOfUsers_thenCorrect() {
56-
final UserSpecification spec = new UserSpecification(new SearchCriteria("lastName", ":", "doe"));
57-
final List<User> results = repository.findAll(spec);
58-
59-
assertThat(userJohn, isIn(results));
60-
assertThat(userTom, isIn(results));
61-
}
62-
6355
@Test
6456
public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() {
65-
final UserSpecification spec = new UserSpecification(new SearchCriteria("firstName", ":", "john"));
66-
final UserSpecification spec1 = new UserSpecification(new SearchCriteria("lastName", ":", "doe"));
57+
final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.EQUALITY, "john"));
58+
final UserSpecification spec1 = new UserSpecification(new SpecSearchCriteria("lastName", SearchOperation.EQUALITY, "doe"));
6759
final List<User> results = repository.findAll(Specifications.where(spec).and(spec1));
6860

6961
assertThat(userJohn, isIn(results));
7062
assertThat(userTom, not(isIn(results)));
7163
}
7264

7365
@Test
74-
public void givenLastAndAge_whenGettingListOfUsers_thenCorrect() {
75-
final UserSpecification spec = new UserSpecification(new SearchCriteria("age", ">", "25"));
76-
final UserSpecification spec1 = new UserSpecification(new SearchCriteria("lastName", ":", "doe"));
77-
final List<User> results = repository.findAll(Specifications.where(spec).and(spec1));
66+
public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() {
67+
final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.NEGATION, "john"));
68+
final List<User> results = repository.findAll(Specifications.where(spec));
7869

7970
assertThat(userTom, isIn(results));
8071
assertThat(userJohn, not(isIn(results)));
8172
}
8273

8374
@Test
84-
public void givenWrongFirstAndLast_whenGettingListOfUsers_thenCorrect() {
85-
final UserSpecification spec = new UserSpecification(new SearchCriteria("firstName", ":", "Adam"));
86-
final UserSpecification spec1 = new UserSpecification(new SearchCriteria("lastName", ":", "Fox"));
87-
final List<User> results = repository.findAll(Specifications.where(spec).and(spec1));
75+
public void givenMinAge_whenGettingListOfUsers_thenCorrect() {
76+
final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.GREATER_THAN, "25"));
77+
final List<User> results = repository.findAll(Specifications.where(spec));
8878

79+
assertThat(userTom, isIn(results));
8980
assertThat(userJohn, not(isIn(results)));
81+
}
82+
83+
@Test
84+
public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() {
85+
final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.STARTS_WITH, "jo"));
86+
final List<User> results = repository.findAll(spec);
87+
88+
assertThat(userJohn, isIn(results));
89+
assertThat(userTom, not(isIn(results)));
90+
}
91+
92+
@Test
93+
public void givenFirstNameSuffix_whenGettingListOfUsers_thenCorrect() {
94+
final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.ENDS_WITH, "n"));
95+
final List<User> results = repository.findAll(spec);
96+
97+
assertThat(userJohn, isIn(results));
9098
assertThat(userTom, not(isIn(results)));
9199
}
92100

93101
@Test
94-
public void givenPartialFirst_whenGettingListOfUsers_thenCorrect() {
95-
final UserSpecification spec = new UserSpecification(new SearchCriteria("firstName", ":", "jo"));
102+
public void givenFirstNameSubstring_whenGettingListOfUsers_thenCorrect() {
103+
final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.CONTAINS, "oh"));
96104
final List<User> results = repository.findAll(spec);
97105

98106
assertThat(userJohn, isIn(results));
99107
assertThat(userTom, not(isIn(results)));
100108
}
101-
}
109+
110+
@Test
111+
public void givenAgeRange_whenGettingListOfUsers_thenCorrect() {
112+
final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.GREATER_THAN, "20"));
113+
final UserSpecification spec1 = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.LESS_THAN, "25"));
114+
final List<User> results = repository.findAll(Specifications.where(spec).and(spec1));
115+
116+
assertThat(userJohn, isIn(results));
117+
assertThat(userTom, not(isIn(results)));
118+
}
119+
}

0 commit comments

Comments
 (0)