Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

新增BetweenSearchFilter,支持between条件查询写法 #477

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -5,9 +5,11 @@
*******************************************************************************/
package org.springside.examples.quickstart.repository;

import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springside.examples.quickstart.entity.User;

public interface UserDao extends PagingAndSortingRepository<User, Long> {
User findByLoginName(String loginName);
public interface UserDao extends PagingAndSortingRepository<User, Long>,
JpaSpecificationExecutor<User> {
User findByLoginName(String loginName);
}
@@ -0,0 +1,56 @@
/**
* Alipay.com Inc.
* Copyright (c) 2004-2015 All Rights Reserved.
*/
package org.springside.examples.quickstart.repository;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springside.examples.quickstart.entity.User;
import org.springside.modules.persistence.DynamicSpecifications;
import org.springside.modules.persistence.SearchFilter;
import org.springside.modules.test.spring.SpringTransactionalTestCase;
import org.springside.modules.utils.Clock;

/**
* userDao测试
* @author Fei
* @version $Id: UserDaoTest.java, v 0.1 2015年9月26日 下午8:48:20 Fei Exp $
*/
@ContextConfiguration(locations = { "/applicationContext.xml" })
public class UserDaoTest extends SpringTransactionalTestCase {

@Autowired
private UserDao userDao;

@Test
public void searchRegisterDate() throws Exception {
Map<String, Object> searchParams = new HashMap<String, Object>();
searchParams.put("EQ_name", "Calvin");

org.springside.modules.utils.Clock.MockClock clock = new Clock.MockClock();
Date start = clock.getCurrentDate();
clock.update(new Date());
Date end = clock.getCurrentDate();

searchParams.put("BTW_registerDate_start", start);
searchParams.put("BTW_registerDate_end", end);
Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);

Specification<User> spec = DynamicSpecifications.bySearchFilter(filters.values(),
User.class);
List<User> userList = userDao.findAll(spec);
assertThat(userList).hasSize(1);
assertThat(userList.get(0).getName()).isEqualTo("Calvin");

}
}
@@ -0,0 +1,41 @@
/**
* Alipay.com Inc.
* Copyright (c) 2004-2015 All Rights Reserved.
*/
package org.springside.modules.persistence;

/**
* 基于between两个参数实现的searchFilter
* @author Fei
* @version $Id: BetweenSearchFilter.java, v 0.1 2015年9月26日 下午8:22:32 Fei Exp $
*/
public class BetweenSearchFilter extends SearchFilter {

/** 开始 */
public Object start;
/** 结束 */
public Object end;

/**
* @param fieldName
* @param operator
* @param value
* @param startDate
* @param endDate
*/
public BetweenSearchFilter(String fieldName, Object start, Object end) {
super(fieldName, Operator.BTW, null);
this.start = start;
this.end = end;
}

/**
* @param fieldName
* @param operator
* @param value
*/
public BetweenSearchFilter(String fieldName, Operator operator, Object value) {
super(fieldName, operator, value);
}

}
Expand Up @@ -22,52 +22,63 @@

public class DynamicSpecifications {

public static <T> Specification<T> bySearchFilter(final Collection<SearchFilter> filters, final Class<T> entityClazz) {
return new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
if (Collections3.isNotEmpty(filters)) {
public static <T> Specification<T> bySearchFilter(final Collection<SearchFilter> filters,
final Class<T> entityClazz) {
return new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
if (Collections3.isNotEmpty(filters)) {

List<Predicate> predicates = Lists.newArrayList();
for (SearchFilter filter : filters) {
// nested path translate, 如Task的名为"user.name"的filedName, 转换为Task.user.name属性
String[] names = StringUtils.split(filter.fieldName, ".");
Path expression = root.get(names[0]);
for (int i = 1; i < names.length; i++) {
expression = expression.get(names[i]);
}
List<Predicate> predicates = Lists.newArrayList();
for (SearchFilter filter : filters) {
// nested path translate, 如Task的名为"user.name"的filedName, 转换为Task.user.name属性
String[] names = StringUtils.split(filter.fieldName, ".");
Path expression = root.get(names[0]);
for (int i = 1; i < names.length; i++) {
expression = expression.get(names[i]);
}
// logic operator
switch (filter.operator) {
case BTW:
BetweenSearchFilter betweenFilter = (BetweenSearchFilter) filter;
predicates.add(
builder.between(expression, (Comparable) betweenFilter.start,
(Comparable) betweenFilter.end));
break;
case EQ:
predicates.add(builder.equal(expression, filter.value));
break;
case LIKE:
predicates.add(builder.like(expression, "%" + filter.value + "%"));
break;
case GT:
predicates.add(
builder.greaterThan(expression, (Comparable) filter.value));
break;
case LT:
predicates
.add(builder.lessThan(expression, (Comparable) filter.value));
break;
case GTE:
predicates.add(builder.greaterThanOrEqualTo(expression,
(Comparable) filter.value));
break;
case LTE:
predicates.add(builder.lessThanOrEqualTo(expression,
(Comparable) filter.value));
break;
}
}

// logic operator
switch (filter.operator) {
case EQ:
predicates.add(builder.equal(expression, filter.value));
break;
case LIKE:
predicates.add(builder.like(expression, "%" + filter.value + "%"));
break;
case GT:
predicates.add(builder.greaterThan(expression, (Comparable) filter.value));
break;
case LT:
predicates.add(builder.lessThan(expression, (Comparable) filter.value));
break;
case GTE:
predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.value));
break;
case LTE:
predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.value));
break;
}
}
// 将所有条件用 and 联合起来
if (!predicates.isEmpty()) {
return builder.and(predicates.toArray(new Predicate[predicates.size()]));
}
}

// 将所有条件用 and 联合起来
if (!predicates.isEmpty()) {
return builder.and(predicates.toArray(new Predicate[predicates.size()]));
}
}

return builder.conjunction();
}
};
}
return builder.conjunction();
}
};
}
}
Expand Up @@ -14,47 +14,85 @@

public class SearchFilter {

public enum Operator {
EQ, LIKE, GT, LT, GTE, LTE
}

public String fieldName;
public Object value;
public Operator operator;

public SearchFilter(String fieldName, Operator operator, Object value) {
this.fieldName = fieldName;
this.value = value;
this.operator = operator;
}

/**
* searchParams中key的格式为OPERATOR_FIELDNAME
*/
public static Map<String, SearchFilter> parse(Map<String, Object> searchParams) {
Map<String, SearchFilter> filters = Maps.newHashMap();

for (Entry<String, Object> entry : searchParams.entrySet()) {
// 过滤掉空值
String key = entry.getKey();
Object value = entry.getValue();
if (StringUtils.isBlank((String) value)) {
continue;
}

// 拆分operator与filedAttribute
String[] names = StringUtils.split(key, "_");
if (names.length != 2) {
throw new IllegalArgumentException(key + " is not a valid search filter name");
}
String filedName = names[1];
Operator operator = Operator.valueOf(names[0]);

// 创建searchFilter
SearchFilter filter = new SearchFilter(filedName, operator, value);
filters.put(key, filter);
}

return filters;
}
public enum Operator {
EQ, LIKE, GT, LT, GTE, LTE, BTW
}

public String fieldName;
public Object value;
public Operator operator;

public SearchFilter(String fieldName, Operator operator, Object value) {
this.fieldName = fieldName;
this.value = value;
this.operator = operator;
}

/**
* searchParams中key的格式为OPERATOR_FIELDNAME
*/
public static Map<String, SearchFilter> parse(Map<String, Object> searchParams) {
Map<String, SearchFilter> filters = Maps.newHashMap();

for (Entry<String, Object> entry : searchParams.entrySet()) {
// 过滤掉空值
String key = entry.getKey();
Object value = entry.getValue();
if (value == null
|| ((value instanceof String) && (StringUtils.isBlank((String) value)))) {
continue;
}

// 拆分operator与filedAttribute
String[] names = StringUtils.split(key, "_");
if (names.length != 2 && names.length != 3) {
throw new IllegalArgumentException(key + " is not a valid search filter name");
}
String filedName = names[1];
Operator operator = Operator.valueOf(names[0]);

// 创建searchFilter
SearchFilter filter = null;

if (operator == Operator.BTW) {
//针对between filter做处理,key做特殊处理,修改为标准模式 operator_字段名
key = names[0] + "_" + names[1];
filter = parseBetweenFilter(key, names, filedName, value, filters);
} else {
filter = new SearchFilter(filedName, operator, value);
}

filters.put(key, filter);
}

return filters;
}

/**
* 针对betweenFilter的解析
* @param key
* @param names
* @param filedName
* @param value
* @param filters
* @return
*/
public static SearchFilter parseBetweenFilter(String key, String[] names, String filedName,
Object value, Map<String, SearchFilter> filters) {

SearchFilter filter = null;
filter = filters.get(key);
if (filter == null) {
filter = new BetweenSearchFilter(filedName, Operator.BTW, null);
}
String index = names[2];
if (StringUtils.equalsIgnoreCase("start", index)) {
((BetweenSearchFilter) filter).start = value;
} else if (StringUtils.equalsIgnoreCase("end", index)) {
((BetweenSearchFilter) filter).end = value;
} else {
throw new IllegalArgumentException(key + " is not a valid search filter name");
}
return filter;
}
}