new logic join

This commit is contained in:
uPagge 2021-04-15 11:47:29 +03:00
parent 9d90770bd9
commit 5534b18573
No known key found for this signature in database
GPG Key ID: 964B40928E4C9088
6 changed files with 203 additions and 47 deletions

View File

@ -6,12 +6,12 @@
<parent>
<groupId>org.sadtech.haiti</groupId>
<artifactId>haiti</artifactId>
<version>0.0.1-RELEASE</version>
<version>0.0.2-SNAPSHOT</version>
</parent>
<groupId>org.sadtech.haiti.filter</groupId>
<artifactId>haiti-filter-criteria</artifactId>
<version>0.0.2-RELEASE</version>
<version>0.0.3-SNAPSHOT</version>
<name>Haiti Filter Criteria</name>
<description>Fast creation of filtering requests using the Criteria Api wrapper.</description>
@ -82,6 +82,11 @@
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.29.Final</version>
</dependency>
</dependencies>
<profiles>

View File

@ -1,9 +1,13 @@
package org.sadtech.haiti.filter.criteria;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.jpa.domain.Specification;
import java.util.List;
import java.util.UUID;
/**
* // TODO: 09.11.2020 Добавить описание.
*
@ -11,9 +15,20 @@ import org.springframework.data.jpa.domain.Specification;
*/
@Getter
@Setter
@NoArgsConstructor
public class Container<T> {
private String joinTable;
private final String uuid = UUID.randomUUID().toString();
private List<JoinTable> joinTables;
private Specification<T> specification;
private Container(List<JoinTable> joinTables) {
this.joinTables = joinTables;
}
public static <T> Container<T> of(List<JoinTable> joinTables) {
return new Container<>(joinTables);
}
}

View File

@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
import org.sadtech.haiti.filter.FilterQuery;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@ -14,8 +15,8 @@ import java.util.stream.Collectors;
public class CriteriaQuery<T> implements FilterQuery {
private static final CriteriaQuery<?> EMPTY = new CriteriaQuery<>(new ArrayList<>());
private final List<Container<T>> specifications;
private String joinTable;
private final List<Container<T>> containers;
private List<JoinTable> joinTables = new ArrayList<>();
private final SimpleCriteriaQuery<T> simpleCriteriaQuery = new SimpleCriteriaQuery();
public static <T> FilterQuery create() {
@ -28,15 +29,38 @@ public class CriteriaQuery<T> implements FilterQuery {
*
* @param fieldName Имя поля сущности, которое отвечает за название таблицы.
*/
public CriteriaQuery<T> join(@NonNull String fieldName) {
joinTable = fieldName;
public CriteriaQuery<T> join(@NonNull String... fieldName) {
joinTables = Arrays.stream(fieldName)
.map(JoinTable::of)
.collect(Collectors.toList());
return this;
}
public CriteriaQuery<T> join(@NonNull JoinTable... joinTables) {
this.joinTables = Arrays.stream(joinTables).collect(Collectors.toList());
return this;
}
@Override
public <Y extends Comparable<? super Y>> FilterQuery between(@NonNull String field, Y from, Y to) {
if (from != null && to != null) {
specifications.add(simpleCriteriaQuery.between(joinTable, field, from, to));
containers.add(simpleCriteriaQuery.between(joinTables, field, from, to));
}
return this;
}
@Override
public <Y extends Comparable<? super Y>> FilterQuery greaterThan(@NonNull String field, Y value) {
if (value != null) {
containers.add(simpleCriteriaQuery.greaterThan(joinTables, field, value));
}
return this;
}
@Override
public <Y extends Comparable<? super Y>> FilterQuery lessThan(@NonNull String field, Y value) {
if (value != null) {
containers.add(simpleCriteriaQuery.lessThan(joinTables, field, value));
}
return this;
}
@ -44,7 +68,7 @@ public class CriteriaQuery<T> implements FilterQuery {
@Override
public FilterQuery matchPhrase(@NonNull String field, Object value) {
if (value != null) {
specifications.add(simpleCriteriaQuery.matchPhrase(joinTable, field, value));
containers.add(simpleCriteriaQuery.matchPhrase(joinTables, field, value));
}
return this;
}
@ -52,9 +76,20 @@ public class CriteriaQuery<T> implements FilterQuery {
@Override
public <U> FilterQuery matchPhrase(@NonNull String field, Set<U> values) {
if (values != null && !values.isEmpty()) {
specifications.addAll(
containers.addAll(
values.stream()
.map(value -> simpleCriteriaQuery.matchPhrase(joinTable, field, value))
.map(value -> simpleCriteriaQuery.matchPhrase(joinTables, field, value))
.collect(Collectors.toList())
);
}
return this;
}
public <U extends List<U>> CriteriaQuery<T> collectionElementsIn(List<U> values) {
if (values != null && !values.isEmpty()) {
containers.addAll(
values.stream()
.map(value -> simpleCriteriaQuery.collectionElements(joinTables, value))
.collect(Collectors.toList())
);
}
@ -64,17 +99,17 @@ public class CriteriaQuery<T> implements FilterQuery {
@Override
public FilterQuery exists(String field) {
if (field != null) {
specifications.add(simpleCriteriaQuery.exists(joinTable, field));
containers.add(simpleCriteriaQuery.exists(joinTables, field));
}
return this;
}
@Override
public FilterQuery like(@NonNull String field, String value, boolean ignoreCase) {
specifications.add(
containers.add(
ignoreCase
? simpleCriteriaQuery.likeIgnoreCase(joinTable, field, value)
: simpleCriteriaQuery.like(joinTable, field, value)
? simpleCriteriaQuery.likeIgnoreCase(joinTables, field, value)
: simpleCriteriaQuery.like(joinTables, field, value)
);
return this;
}
@ -82,8 +117,8 @@ public class CriteriaQuery<T> implements FilterQuery {
@Override
public FilterQuery checkBoolInt(@NonNull String field, Boolean flag) {
if (flag != null) {
specifications.add(
simpleCriteriaQuery.between(joinTable, field, 0, Integer.MAX_VALUE)
containers.add(
simpleCriteriaQuery.between(joinTables, field, 0, Integer.MAX_VALUE)
);
}
return this;
@ -91,7 +126,7 @@ public class CriteriaQuery<T> implements FilterQuery {
@Override
public <Q> Q build() {
return (Q) specifications.stream()
return (Q) containers.stream()
.map(Container::getSpecification)
.collect(Collectors.toList());
}

View File

@ -0,0 +1,28 @@
package org.sadtech.haiti.filter.criteria;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* // TODO: 15.04.2021 Добавить описание.
*
* @author upagge 15.04.2021
*/
@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class JoinTable {
private final String tableName;
private final JoinTypeOperation joinTypeOperation;
public static JoinTable of(@NonNull String tableName) {
return new JoinTable(tableName, JoinTypeOperation.LEFT);
}
public static JoinTable of(@NonNull String tableName, JoinTypeOperation joinType) {
return new JoinTable(tableName, joinType);
}
}

View File

@ -0,0 +1,22 @@
package org.sadtech.haiti.filter.criteria;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import javax.persistence.criteria.JoinType;
/**
* // TODO: 15.04.2021 Добавить описание.
*
* @author upagge 15.04.2021
*/
@Getter
@RequiredArgsConstructor
public enum JoinTypeOperation {
LEFT(JoinType.LEFT),
INNER(JoinType.INNER);
private final JoinType criteriaType;
}

View File

@ -1,63 +1,114 @@
package org.sadtech.haiti.filter.criteria;
import lombok.NonNull;
import org.hibernate.query.criteria.internal.path.PluralAttributeJoinSupport;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.From;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class SimpleCriteriaQuery<T> {
private final Map<String, Join> joinMap = new HashMap<>();
private From<Object, Object> lastJoin;
private final Set<String> unique = new HashSet<>();
private final Map<String, From<Object, Object>> joinMap = new HashMap<>();
public Container<T> matchPhrase(String joinTable, @NonNull String field, @NonNull Object value) {
final Container<T> container = getContainer(joinTable);
container.setSpecification((root, query, cb) -> cb.equal(getPath(root, joinTable, field), value));
public Container<T> matchPhrase(List<JoinTable> joinTables, @NonNull String field, @NonNull Object value) {
final Container<T> container = Container.of(joinTables);
container.setSpecification((root, query, cb) -> cb.equal(getPath(root, container).get(field), value));
return container;
}
public Container<T> exists(String joinTable, @NonNull String field) {
final Container<T> container = getContainer(joinTable);
container.setSpecification((root, query, cb) -> cb.isNotNull(getPath(root, joinTable, field)));
public <U> Container<T> collectionElements(List<JoinTable> joinTables, List<U> values) {
final Container<T> container = Container.of(joinTables);
container.setSpecification(
(root, query, builder) -> {
Predicate where = builder.conjunction();
return builder.and(where, getPath(root, container).in(values));
}
);
return container;
}
public Container<T> likeIgnoreCase(String joinTable, @NonNull String field, String value) {
final Container<T> container = getContainer(joinTable);
container.setSpecification((root, query, cb) -> cb.like(cb.lower(getPath(root, joinTable, field)), value));
public Container<T> exists(List<JoinTable> joinTables, @NonNull String field) {
final Container<T> container = Container.of(joinTables);
container.setSpecification((root, query, cb) -> cb.isNotNull(getPath(root, container).get(field)));
return container;
}
public Container<T> like(String joinTable, @NonNull String field, String value) {
final Container<T> container = getContainer(joinTable);
container.setSpecification((root, query, cb) -> cb.like(getPath(root, joinTable, field), value));
public Container<T> likeIgnoreCase(List<JoinTable> joinTables, @NonNull String field, String value) {
final Container<T> container = Container.of(joinTables);
container.setSpecification((root, query, cb) -> cb.like(cb.lower(getPath(root, container).get(field)), value));
return container;
}
public <Y extends Comparable<? super Y>> Container<T> between(String joinTable, @NonNull String field, Y from, Y to) {
final Container<T> container = getContainer(joinTable);
container.setSpecification((root, query, cb) -> cb.between(getPath(root, joinTable, field), from, to));
public Container<T> like(List<JoinTable> joinTables, @NonNull String field, String value) {
final Container<T> container = Container.of(joinTables);
container.setSpecification((root, query, cb) -> cb.like(getPath(root, container).get(field), value));
return container;
}
private Container<T> getContainer(String joinTableName) {
final Container<T> container = new Container<>();
container.setJoinTable(joinTableName);
public <Y extends Comparable<? super Y>> Container<T> between(List<JoinTable> joinTables, @NonNull String field, Y from, Y to) {
final Container<T> container = Container.of(joinTables);
container.setSpecification((root, query, cb) -> cb.between(getPath(root, container).get(field), from, to));
return container;
}
private <Z> Expression<Z> getPath(Root<T> root, String joinTable, String field) {
if (joinTable != null) {
Join join = joinMap.get(joinTable);
if (join == null) {
join = root.join(joinTable);
joinMap.put(joinTable, join);
public <Y extends Comparable<? super Y>> Container<T> greaterThan(List<JoinTable> joinTables, @NonNull String field, Y value) {
final Container<T> container = Container.of(joinTables);
container.setSpecification(((root, query, cb) -> cb.greaterThan(getPath(root, container).get(field), value)));
return container;
}
public <Y extends Comparable<? super Y>> Container<T> lessThan(List<JoinTable> joinTables, @NonNull String field, Y value) {
final Container<T> container = Container.of(joinTables);
container.setSpecification(((root, query, cb) -> cb.lessThan(getPath(root, container).get(field), value)));
return container;
}
private From<Object, Object> getPath(Root root, Container<T> container) {
final List<JoinTable> joinTables = container.getJoinTables();
final String uuid = container.getUuid();
From<Object, Object> join = root;
if (!joinTables.isEmpty()) {
check(uuid);
for (JoinTable table : joinTables) {
final String tableName = table.getTableName();
final JoinType criteriaType = table.getJoinTypeOperation().getCriteriaType();
if (joinMap.containsKey(tableName)) {
join = joinMap.get(tableName);
} else {
final Set<String> attributes = ((Root<Object>) root).getModel().getAttributes().stream()
.map(Attribute::getName)
.collect(Collectors.toSet());
if (attributes.contains(tableName)) {
join = root.join(tableName, criteriaType);
} else {
join = lastJoin.join(tableName, criteriaType);
}
if (join instanceof PluralAttributeJoinSupport && !((PluralAttributeJoinSupport) join).isBasicCollection()) {
lastJoin = join;
}
joinMap.put(tableName, join);
}
}
return join.get(field);
}
return root.get(field);
return join;
}
private void check(String uuid) {
if (unique.contains(uuid)) {
joinMap.clear();
}
unique.add(uuid);
}
}