Last active
November 9, 2021 05:10
-
-
Save brunnels/ab980094e0f7c5a5d5f65eff0b5f5613 to your computer and use it in GitHub Desktop.
Generic REST Query Language with RSQL for Spring Data JPA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.kraven.repository; | |
import org.kraven.domain.DeviceData; | |
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; | |
/** | |
* Spring Data JPA repository for the DeviceData entity. | |
*/ | |
@SuppressWarnings("unused") | |
public interface DeviceDataRepository extends JpaRepository<DeviceData,Long>, JpaSpecificationExecutor<DeviceData> { | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.kraven.repository.specification.util; | |
import cz.jirutka.rsql.parser.ast.ComparisonOperator; | |
import cz.jirutka.rsql.parser.ast.RSQLOperators; | |
/** | |
* Created by brunnels on 10/26/2016. | |
*/ | |
public enum RsqlSearchOperation { | |
EQUAL(RSQLOperators.EQUAL), | |
NOT_EQUAL(RSQLOperators.NOT_EQUAL), | |
GREATER_THAN(RSQLOperators.GREATER_THAN), | |
GREATER_THAN_OR_EQUAL(RSQLOperators.GREATER_THAN_OR_EQUAL), | |
LESS_THAN(RSQLOperators.LESS_THAN), | |
LESS_THAN_OR_EQUAL(RSQLOperators.LESS_THAN_OR_EQUAL), | |
IN(RSQLOperators.IN), | |
NOT_IN(RSQLOperators.NOT_IN); | |
private ComparisonOperator operator; | |
private RsqlSearchOperation(final ComparisonOperator operator) { | |
this.operator = operator; | |
} | |
public static RsqlSearchOperation getSimpleOperator(final ComparisonOperator operator) { | |
for (final RsqlSearchOperation operation : values()) { | |
if (operation.getOperator() == operator) { | |
return operation; | |
} | |
} | |
return null; | |
} | |
public ComparisonOperator getOperator() { | |
return operator; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.kraven.repository.specification; | |
import cz.jirutka.rsql.parser.ast.ComparisonOperator; | |
import org.kraven.repository.specification.util.RsqlSearchOperation; | |
import org.springframework.data.jpa.domain.Specification; | |
import javax.persistence.criteria.CriteriaBuilder; | |
import javax.persistence.criteria.CriteriaQuery; | |
import javax.persistence.criteria.Predicate; | |
import javax.persistence.criteria.Root; | |
import java.util.ArrayList; | |
import java.util.List; | |
/** | |
* Created by brunnels on 10/26/2016. | |
*/ | |
public class RsqlSpecification<T> implements Specification<T> { | |
private String property; | |
private ComparisonOperator operator; | |
private List<String> arguments; | |
public RsqlSpecification(final String property, final ComparisonOperator operator, final List<String> arguments) { | |
super(); | |
this.property = property; | |
this.operator = operator; | |
this.arguments = arguments; | |
} | |
@Override | |
public Predicate toPredicate(final Root<T> root, final CriteriaQuery<?> query, final CriteriaBuilder builder) { | |
final List<Object> args = castArguments(root); | |
final Object argument = args.get(0); | |
RsqlSearchOperation oper = RsqlSearchOperation.getSimpleOperator(operator); | |
if(oper != null) { | |
switch (oper) { | |
case EQUAL: { | |
if (argument instanceof String) { | |
return builder.like(root.<String>get(property), argument.toString().replace('*', '%')); | |
} else if (argument == null) { | |
return builder.isNull(root.get(property)); | |
} else { | |
return builder.equal(root.get(property), argument); | |
} | |
} | |
case NOT_EQUAL: { | |
if (argument instanceof String) { | |
return builder.notLike(root.<String>get(property), argument.toString().replace('*', '%')); | |
} else if (argument == null) { | |
return builder.isNotNull(root.get(property)); | |
} else { | |
return builder.notEqual(root.get(property), argument); | |
} | |
} | |
case GREATER_THAN: { | |
return builder.greaterThan(root.<String>get(property), argument.toString()); | |
} | |
case GREATER_THAN_OR_EQUAL: { | |
return builder.greaterThanOrEqualTo(root.<String>get(property), argument.toString()); | |
} | |
case LESS_THAN: { | |
return builder.lessThan(root.<String>get(property), argument.toString()); | |
} | |
case LESS_THAN_OR_EQUAL: { | |
return builder.lessThanOrEqualTo(root.<String>get(property), argument.toString()); | |
} | |
case IN: | |
return root.get(property).in(args); | |
case NOT_IN: | |
return builder.not(root.get(property).in(args)); | |
} | |
} | |
return null; | |
} | |
// === private | |
private List<Object> castArguments(final Root<T> root) { | |
final List<Object> args = new ArrayList<Object>(); | |
final Class<? extends Object> type = root.get(property).getJavaType(); | |
for (final String argument : arguments) { | |
if (type.equals(Integer.class)) { | |
args.add(Integer.parseInt(argument)); | |
} else if (type.equals(Long.class)) { | |
args.add(Long.parseLong(argument)); | |
} else { | |
args.add(argument); | |
} | |
} | |
return args; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.kraven.repository.specification; | |
import cz.jirutka.rsql.parser.ast.ComparisonNode; | |
import cz.jirutka.rsql.parser.ast.LogicalNode; | |
import cz.jirutka.rsql.parser.ast.LogicalOperator; | |
import cz.jirutka.rsql.parser.ast.Node; | |
import org.springframework.data.jpa.domain.Specification; | |
import org.springframework.data.jpa.domain.Specifications; | |
import java.util.ArrayList; | |
import java.util.List; | |
/** | |
* Created by brunnels on 10/26/2016. | |
*/ | |
public class RsqlSpecificationBuilder<T> { | |
public Specifications<T> createSpecification(final Node node) { | |
if (node instanceof LogicalNode) { | |
return createSpecification((LogicalNode) node); | |
} | |
if (node instanceof ComparisonNode) { | |
return createSpecification((ComparisonNode) node); | |
} | |
return null; | |
} | |
public Specifications<T> createSpecification(final LogicalNode logicalNode) { | |
final List<Specifications<T>> specs = new ArrayList<Specifications<T>>(); | |
Specifications<T> temp; | |
for (final Node node : logicalNode.getChildren()) { | |
temp = createSpecification(node); | |
if (temp != null) { | |
specs.add(temp); | |
} | |
} | |
Specifications<T> result = specs.get(0); | |
if (logicalNode.getOperator() == LogicalOperator.AND) { | |
for (int i = 1; i < specs.size(); i++) { | |
result = Specifications.where(result).and(specs.get(i)); | |
} | |
} else if (logicalNode.getOperator() == LogicalOperator.OR) { | |
for (int i = 1; i < specs.size(); i++) { | |
result = Specifications.where(result).or(specs.get(i)); | |
} | |
} | |
return result; | |
} | |
public Specifications<T> createSpecification(final ComparisonNode comparisonNode) { | |
Specification<T> spec = new RsqlSpecification<T>(comparisonNode.getSelector(), comparisonNode.getOperator(), comparisonNode.getArguments()); | |
final Specifications<T> result = Specifications.where(spec); | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.kraven.repository.specification.util; | |
import cz.jirutka.rsql.parser.ast.AndNode; | |
import cz.jirutka.rsql.parser.ast.ComparisonNode; | |
import cz.jirutka.rsql.parser.ast.OrNode; | |
import cz.jirutka.rsql.parser.ast.RSQLVisitor; | |
import org.kraven.repository.specification.RsqlSpecificationBuilder; | |
import org.springframework.data.jpa.domain.Specification; | |
/** | |
* Created by brunnels on 10/26/2016. | |
*/ | |
public class RsqlVisitor<T> implements RSQLVisitor<Specification<T>, Void> { | |
private RsqlSpecificationBuilder<T> builder; | |
public RsqlVisitor() { | |
builder = new RsqlSpecificationBuilder<T>(); | |
} | |
@Override | |
public Specification<T> visit(final AndNode node, final Void param) { | |
return builder.createSpecification(node); | |
} | |
@Override | |
public Specification<T> visit(final OrNode node, final Void param) { | |
return builder.createSpecification(node); | |
} | |
@Override | |
public Specification<T> visit(final ComparisonNode node, final Void params) { | |
return builder.createSpecification(node); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Service Implementation for managing DeviceData. | |
*/ | |
@Service | |
@Transactional | |
public class DeviceDataService { | |
private final Logger log = LoggerFactory.getLogger(DeviceDataService.class); | |
@Inject | |
private DeviceDataRepository deviceDataRepository; | |
@Inject | |
private DeviceDataMapper deviceDataMapper; | |
/** | |
* Get all the deviceData. | |
* | |
* @search the RSQL search string | |
* @param pageable the pagination information | |
* @return the list of entities | |
*/ | |
@Transactional(readOnly = true) | |
public Page<DeviceDataDTO> findAll(Pageable pageable, String search) { | |
log.debug("Request to get all DeviceData"); | |
Page<DeviceData> result; | |
if(search != null) { | |
final Node rootNode = new RSQLParser().parse(search); | |
Specification<DeviceData> spec = rootNode.accept(new RsqlVisitor<DeviceData>()); | |
result = deviceDataRepository.findAll(spec, pageable); | |
} | |
else { | |
result = deviceDataRepository.findAll(pageable); | |
} | |
return result.map(deviceData -> deviceDataMapper.deviceDataToDeviceDataDTO(deviceData)); | |
} | |
} |
Do we have anything for mybatis ? Most of Rest controller query search to criteria are mostly for JPA.
I want to use it with MyBatis Example Criterion. Any suggestions ?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Generic REST Query Language with RSQL using Spring Data JPA Repositories.
Based on REST Query Language with RSQL and utilizing rsql-parser library.
Maven Dependency for rsql-query