All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.foreach.across.modules.entity.query.EntityQuery Maven / Gradle / Ivy

There is a newer version: 4.5.1
Show newest version
/*
 * Copyright 2014 the original author or authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.foreach.across.modules.entity.query;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.foreach.across.modules.entity.query.support.SortDeserializer;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * Abstraction layer for *simple* query construction.  An EntityQuery is a simple structure that has the support classes
 * to be constructed from maps, de-serialized to and from JSON and later converted to specific query structures like
 * JPA or QueryDSL.
 * 

* An EntityQuery is limited in what it supports because it provides a common denominator for different query types. * * @author Arne Vandamme * @see com.foreach.across.modules.entity.query.jpa.EntityQueryJpaUtils * @see com.foreach.across.modules.entity.query.querydsl.EntityQueryQueryDslUtils */ public class EntityQuery implements EntityQueryExpression { /** * Ordering of the results. */ @JsonDeserialize(using = SortDeserializer.class) @Getter @Setter private Sort sort; private EntityQueryOps operand = EntityQueryOps.AND; private List expressions = new ArrayList<>(); public EntityQuery() { } public EntityQuery( EntityQueryOps operand ) { setOperand( operand ); } public EntityQuery( EntityQuery entityQuery ) { this.operand = entityQuery.operand; this.expressions = new ArrayList<>( entityQuery.expressions ); this.sort = entityQuery.sort; } public final void add( EntityQueryExpression expression ) { if ( expression instanceof EntityQuery ) { EntityQuery subQuery = new EntityQuery( (EntityQuery) expression ); if ( subQuery.hasSort() && !hasSort() ) { setSort( subQuery.getSort() ); } subQuery.sort = null; if ( !subQuery.expressions.isEmpty() ) { if ( subQuery.expressions.size() == 1 ) { expressions.add( subQuery.expressions.get( 0 ) ); } else { expressions.add( subQuery ); } } } else if ( expression != null ) { expressions.add( expression ); } } public EntityQueryOps getOperand() { return operand; } public void setOperand( EntityQueryOps operand ) { Assert.notNull( operand, "A valid operand is required" ); if ( operand != EntityQueryOps.AND && operand != EntityQueryOps.OR ) { throw new IllegalArgumentException( "EntityQuery operand type must be either AND or OR" ); } this.operand = operand; } public List getExpressions() { return expressions; } public void setExpressions( List expressions ) { Assert.notNull( expressions, "expression collection may not be null" ); this.expressions = expressions; } @Override public boolean equals( Object o ) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } EntityQuery that = (EntityQuery) o; if ( expressions != null ? !expressions.equals( that.expressions ) : that.expressions != null ) { return false; } if ( operand != that.operand ) { return false; } if ( sort != null ? !sort.equals( that.sort ) : that.sort != null ) { return false; } return true; } @Override public int hashCode() { int result = operand != null ? operand.hashCode() : 0; result = 31 * result + ( expressions != null ? expressions.hashCode() : 0 ); return result; } @Override public String toString() { return StringUtils.trim( operand.toString( null, expressions.toArray() ) + ( sort != null ? " " + toString( sort ) : "" ) ); } private String toString( Sort sort ) { return "order by " + StreamSupport.stream( sort.spliterator(), false ) .map( o -> o.getProperty() + " " + o.getDirection() ) .collect( Collectors.joining( ", " ) ); } /** * @return true if one or more expressions are present */ public boolean hasExpressions() { return !expressions.isEmpty(); } /** * @return true if a sort has been set on this query */ public boolean hasSort() { return sort != null; } /** * @return new EntityQuery instance that will return all entities. */ public static EntityQuery all() { return new EntityQuery(); } /** * @return new EntityQuery instance that will return all entities, sorted accordingly */ public static EntityQuery all( Sort sort ) { final EntityQuery entityQuery = new EntityQuery(); entityQuery.setSort( sort ); return entityQuery; } /** * Converts an EQL statement into a (raw) {@link EntityQuery}. Not validation or translation of any * kind will be done on the elements, this will simply convert the {@link String} tokens into an {@link EntityQuery} object. *

* You should use {@link EntityQueryParser#parse(String)} of the relevant entity if you want to parse an EQL into a fully * executable {@link EntityQuery}. You can use {@link EntityQueryParser#prepare(EntityQuery)} to convert the raw query * object into an executable one. *

* Exceptions will be thrown if parsing fails. * * @param eql to convert * @return EntityQuery */ public static EntityQuery parse( String eql ) { return EntityQueryParser.parseRawQuery( eql ); } /** * Merges one or more entity queries expressions together using {@link EntityQueryOps#AND}. * If expressions are sub-queries that define sort values, only the first non-null sort value will be kept. *

* This method is null safe: {@code null} values will simply be ignored and a query will always be returned. * * @param expressions to merge * @return merged query */ public static EntityQuery and( EntityQueryExpression... expressions ) { return create( EntityQueryOps.AND, expressions ); } /** * Merges one or more entity queries expressions together using {@link EntityQueryOps#OR}. * If expressions are sub-queries that define sort values, only the first non-null sort value will be kept. *

* This method is null safe: {@code null} values will simply be ignored and a query will always be returned. * * @param expressions to merge * @return merged query */ public static EntityQuery or( EntityQueryExpression... expressions ) { return create( EntityQueryOps.OR, expressions ); } public static EntityQuery create( EntityQueryOps operand, EntityQueryExpression... expressions ) { EntityQuery query = new EntityQuery( operand ); Stream.of( expressions ) .filter( Objects::nonNull ) .forEach( query::add ); return query; } /** * @param eql eql statement * @return entity query * @see EntityQuery#parse(String) */ public static EntityQuery of( String eql ) { return EntityQuery.parse( eql ); } /** * Create a duplicate of an existing query. If the value passed in is {@code null}, a query instance for all values will be returned. * As such, this method can be used to return the default query if you have a null value. * * @param query to duplicate * @return query - never null */ public static EntityQuery of( EntityQuery query ) { if ( query == null ) { return EntityQuery.all(); } return new EntityQuery( query ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy