
org.nuiton.topia.persistence.internal.AbstractTopiaDao Maven / Gradle / Ivy
The newest version!
package org.nuiton.topia.persistence.internal;
/*
* #%L
* ToPIA Extension :: API
* %%
* Copyright (C) 2018 - 2022 Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;
import org.nuiton.topia.persistence.HqlAndParametersBuilder;
import org.nuiton.topia.persistence.QueryMissingOrderException;
import org.nuiton.topia.persistence.TopiaDao;
import org.nuiton.topia.persistence.TopiaDaoSupplier;
import org.nuiton.topia.persistence.TopiaEntity;
import org.nuiton.topia.persistence.TopiaEntityEnum;
import org.nuiton.topia.persistence.TopiaIdFactory;
import org.nuiton.topia.persistence.TopiaNoResultException;
import org.nuiton.topia.persistence.TopiaNonUniqueResultException;
import org.nuiton.topia.persistence.TopiaPersistenceContext;
import org.nuiton.topia.persistence.TopiaQueryBuilderAddCriteriaOrRunQueryStep;
import org.nuiton.topia.persistence.pager.FilterRuleGroupOperator;
import org.nuiton.topia.persistence.pager.PaginationOrder;
import org.nuiton.topia.persistence.pager.PaginationParameter;
import org.nuiton.topia.persistence.pager.PaginationResult;
import org.nuiton.topia.persistence.support.TopiaHibernateSupport;
import org.nuiton.topia.persistence.support.TopiaJpaSupport;
import org.nuiton.topia.persistence.support.TopiaSqlQuery;
import org.nuiton.topia.persistence.support.TopiaSqlSupport;
import org.nuiton.topia.persistence.util.TopiaUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
*
* This class has the common methods usable for each Dao managed by Topia. It is not JPA implementation dependent, it
* only relies on {@link TopiaJpaSupport}.
*
*
* This class is directly extended by the GeneratedXyzTopiaDao which groups all the Xyz specific methods.
*
* Instances are created bt the model's specific {@link TopiaPersistenceContext}, which implements the
* {@link TopiaDaoSupplier} contract.
*
* @param the managed entity type
* @author Benjamin Poussin - [email protected]
* @author Arnaud Thimel (Code Lutin)
* @author Tony Chemit - [email protected]
*/
public abstract class AbstractTopiaDao implements TopiaDao {
protected static final Function PAGINATION_ORDER_TO_HQL = input -> String.format("%s %s", input.getClause(), input.isDesc() ? "DESC" : "ASC");
/**
* Default batch size used to iterate on data.
*
* @since 2.6.14
*/
protected int batchSize = 1000;
protected TopiaJpaSupport topiaJpaSupport;
protected TopiaHibernateSupport topiaHibernateSupport;
protected TopiaSqlSupport topiaSqlSupport;
protected TopiaIdFactory topiaIdFactory;
protected TopiaDaoSupplier topiaDaoSupplier;
public abstract TopiaEntityEnum getTopiaEntityEnum();
public abstract Class getEntityClass();
/**
* When AbstractTopiaContext create the TopiaDAOHibernate, it must call this
* method just after.
*
* @param topiaJpaSupport FIXME
* @param topiaHibernateSupport FIXME
* @param topiaSqlSupport FIXME
* @param topiaIdFactory FIXME
* @param topiaDaoSupplier FIXME
*/
public void init(
TopiaJpaSupport topiaJpaSupport,
TopiaHibernateSupport topiaHibernateSupport,
TopiaSqlSupport topiaSqlSupport,
TopiaIdFactory topiaIdFactory,
TopiaDaoSupplier topiaDaoSupplier) {
this.topiaJpaSupport = topiaJpaSupport;
this.topiaHibernateSupport = topiaHibernateSupport;
this.topiaSqlSupport = topiaSqlSupport;
this.topiaIdFactory = topiaIdFactory;
this.topiaDaoSupplier = topiaDaoSupplier;
}
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
protected String newFromClause() {
return newFromClause(null);
}
protected String newFromClause(String alias) {
String hql = "from " + getTopiaEntityEnum().getImplementationFQN();
if (StringUtils.isNotBlank(alias)) {
hql += " " + alias;
}
return hql;
}
@Override
public E newInstance(Map properties) {
E result = newInstance();
try {
for (Map.Entry e : properties.entrySet()) {
String propertyName = e.getKey();
Object value = e.getValue();
PropertyUtils.setProperty(result, propertyName, value);
}
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException eee) {
throw new IllegalArgumentException(
"Can't put properties on new Object", eee);
}
return result;
}
@Override
public E newInstance(String propertyName, Object propertyValue, Object... otherPropertyNamesAndValues) {
Map properties =
TopiaUtil.convertPropertiesArrayToMap(propertyName, propertyValue, otherPropertyNamesAndValues);
return newInstance(properties);
}
@Override
public PaginationResult initPagination(int pageSize) {
return initPagination(newFromClause(), new HashMap<>(), pageSize);
}
@Override
public PaginationResult initPagination(String hql, Map params, int pageSize) {
PaginationParameter firstPage = PaginationParameter.of(0, pageSize);
if (hqlContainsOrderBy(hql)) {
// must remove the order by clause, otherwise some sql queries won't work.
hql = hql.substring(0, hql.toLowerCase().indexOf("order by"));
}
String countCondition = "*";
if (hqlStartsWithSelect(hql)) {
// must remove the select, otherwise some sql queries won't work.
int selectIndex = hql.toLowerCase().indexOf("select");
int fromIndex = hql.toLowerCase().indexOf("from");
// A: select * from ... → select count(*) from ...
// B: select a from ... → select count(*) from ...
// C: select a,b from ... → select count(*) from ...
// D: select distinct a from ... → select count(distinct a) from ...
// Here is a fix for case D
String selectCondition = hql.toLowerCase().substring(selectIndex + "select".length(), fromIndex);
if (selectCondition.contains("distinct")) {
// AThimel 18/07/14 Hibernate does not support "select count(distinct(name))", need to use "select count(distinct name)"
Preconditions.checkState(!selectCondition.replaceAll(" ", "").toLowerCase().contains("distinct("),
"This method needs to run count(...), but Hibernate does not support " +
"\"select count(distinct(name))\", please use \"select distinct name\" (without brackets)");
countCondition = selectCondition;
}
hql = hql.substring(fromIndex);
} else if (hqlStartsWithFrom(hql)) {
int fromIndex = hql.toLowerCase().indexOf("from ");
int aliasIndex = hql.toLowerCase().indexOf(" ", fromIndex + 5);
int endAliasIndex = hql.toLowerCase().indexOf(" ", aliasIndex + 1);
if (aliasIndex > -1 && endAliasIndex > -1) {
countCondition = hql.substring(aliasIndex, endAliasIndex).trim();
}
}
String countHql = String.format("SELECT COUNT(%s) %s", countCondition, hql);
long count = count(countHql, params);
List emptyList = Lists.newArrayList(); // AThimel 22/05/14 To keep the old behavior, we do not load the elements
return PaginationResult.of(emptyList, count, firstPage);
}
@Override
public E initId(E entity) {
// first set topiaId
if (!entity.isPersisted()) {
// only set id if not already on
String topiaId = topiaIdFactory.newTopiaId(getEntityClass(), entity);
entity.setTopiaId(topiaId);
}
return entity;
}
@Override
public E create(E entity) {
// first set topiaId
entity = initId(entity);
// save entity
topiaJpaSupport.save(entity);
return entity;
}
@Override
public E create(String propertyName, Object propertyValue, Object... otherPropertyNamesAndValues) {
Map properties =
TopiaUtil.convertPropertiesArrayToMap(propertyName, propertyValue, otherPropertyNamesAndValues);
return create(properties);
}
@Override
public E create(Map properties) {
E result = newInstance(properties);
create(result);
return result;
}
@Override
public E create() {
E result = newInstance();
create(result);
return result;
}
@Override
public E update(E entity) {
topiaJpaSupport.saveOrUpdate(entity);
return entity;
}
@Override
public void delete(E entity) {
topiaJpaSupport.delete(entity);
entity.notifyDeleted();
}
protected HqlAndParametersBuilder newHqlAndParametersBuilder(FilterRuleGroupOperator filterRuleGroupOperator) {
return new HqlAndParametersBuilder<>(getEntityClass(), filterRuleGroupOperator);
}
protected HqlAndParametersBuilder newHqlAndParametersBuilder() {
return newHqlAndParametersBuilder(FilterRuleGroupOperator.AND);
}
protected HqlAndParametersBuilder getHqlForProperties(String propertyName,
Object propertyValue,
Object... otherPropertyNamesAndValues) {
Map properties =
TopiaUtil.convertPropertiesArrayToMap(propertyName, propertyValue, otherPropertyNamesAndValues);
return getHqlForProperties(properties);
}
protected HqlAndParametersBuilder getHqlForNoConstraint() {
Map properties = Collections.emptyMap();
return getHqlForProperties(properties);
}
protected HqlAndParametersBuilder getHqlForProperties(Map properties) {
HqlAndParametersBuilder result = newHqlAndParametersBuilder();
for (Map.Entry property : properties.entrySet()) {
result.addEquals(property.getKey(), property.getValue());
}
return result;
}
protected AbstractTopiaDaoQueryBuilderRunQueryStep forHql(String hql) {
Map properties = Collections.emptyMap();
return forHql(hql, properties);
}
protected AbstractTopiaDaoQueryBuilderRunQueryStep forHql(String hql, Map hqlParameters) {
//FIXME tchemit-2016-05-01 Should we scan in hql code if there is an orderBy ?
boolean withOrderByClause = false;
return new AbstractTopiaDaoQueryBuilderRunQueryStep<>(this, true, withOrderByClause, hql, hqlParameters);
}
protected AbstractTopiaDaoQueryBuilderRunQueryStep forHql(String hql, String parameterName,
Object parameterValue,
Object... otherParameterNamesAndValues) {
Map hqlParameters =
TopiaUtil.convertPropertiesArrayToMap(parameterName, parameterValue, otherParameterNamesAndValues);
return forHql(hql, hqlParameters);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep forAll() {
return newQueryBuilder();
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep forProperties(Map properties) {
HqlAndParametersBuilder hqlAndParametersBuilder = getHqlForProperties(properties);
return new AbstractTopiaDaoQueryBuilderAddCriteriaOrRunQueryStep<>(this, hqlAndParametersBuilder);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep forProperties(String propertyName,
Object propertyValue,
Object... otherPropertyNamesAndValues) {
HqlAndParametersBuilder hqlAndParametersBuilder = getHqlForProperties(propertyName, propertyValue, otherPropertyNamesAndValues);
return new AbstractTopiaDaoQueryBuilderAddCriteriaOrRunQueryStep<>(this, hqlAndParametersBuilder);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep forContains(String propertyName, Object propertyValue) {
return newQueryBuilder().addContains(propertyName, propertyValue);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep forEquals(String propertyName, Object propertyValue) {
return newQueryBuilder().addEquals(propertyName, propertyValue);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep forIn(String propertyName, Collection> propertyValues) {
return newQueryBuilder().addIn(propertyName, propertyValues);
}
protected boolean exists(String hql, Map hqlParameters) {
// TODO AThimel 12/09/14 Avoid loading entity, just count the results
E entity = topiaJpaSupport.findAny(hql, hqlParameters);
return entity != null;
}
protected long count(String hql, Map hqlParameters) {
Preconditions.checkArgument(hql.toLowerCase().trim().startsWith("select count("), "Your HQL query must start with \"select count(\"");
return findUnique(hql, hqlParameters);
}
protected O findUnique(String hql, Map hqlParameters) throws TopiaNoResultException, TopiaNonUniqueResultException {
O result = findUniqueOrNull(hql, hqlParameters);
if (result == null) {
throw new TopiaNoResultException(hql, hqlParameters);
}
return result;
}
protected Optional tryFindUnique(String hql, Map hqlParameters) throws TopiaNonUniqueResultException {
O uniqueOrNull = findUniqueOrNull(hql, hqlParameters);
return Optional.ofNullable(uniqueOrNull);
}
protected O findUniqueOrNull(String hql, Map hqlParameters) throws TopiaNonUniqueResultException {
Preconditions.checkNotNull(hql);
Preconditions.checkNotNull(hqlParameters);
return topiaJpaSupport.findUnique(hql, hqlParameters);
}
protected O findFirst(String hql, Map hqlParameters) throws QueryMissingOrderException {
O result = findFirstOrNull(hql, hqlParameters);
if (result == null) {
throw new TopiaNoResultException(hql, hqlParameters);
}
return result;
}
protected Optional tryFindFirst(String hql, Map hqlParameters) throws QueryMissingOrderException {
O firstOrNull = findFirstOrNull(hql, hqlParameters);
return Optional.ofNullable(firstOrNull);
}
protected O findFirstOrNull(String hql, Map hqlParameters) throws QueryMissingOrderException {
if (!hqlContainsOrderBy(hql)) {
throw new QueryMissingOrderException(hql, hqlParameters);
}
return findAnyOrNull(hql, hqlParameters);
}
protected O findAny(String hql, Map hqlParameters) throws TopiaNoResultException {
O result = findAnyOrNull(hql, hqlParameters);
if (result == null) {
throw new TopiaNoResultException(hql, hqlParameters);
}
return result;
}
protected Optional tryFindAny(String hql, Map hqlParameters) {
O anyOrNull = findAnyOrNull(hql, hqlParameters);
return Optional.ofNullable(anyOrNull);
}
protected O findAnyOrNull(String hql) {
Preconditions.checkNotNull(hql);
Map hqlParameters = Collections.emptyMap();
return findAnyOrNull(hql, hqlParameters);
}
protected O findAnyOrNull(String hql, Map hqlParameters) {
Preconditions.checkNotNull(hql);
Preconditions.checkNotNull(hqlParameters);
return topiaJpaSupport.findAny(hql, hqlParameters);
}
protected List findAll(String hql) {
Preconditions.checkNotNull(hql);
Map hqlParameters = Collections.emptyMap();
return findAll(hql, hqlParameters);
}
public List findAll(String hql, Map hqlParameters) {
Preconditions.checkNotNull(hql);
Preconditions.checkNotNull(hqlParameters);
return topiaJpaSupport.findAll(hql, hqlParameters);
}
protected Stream stream(String hql) {
Preconditions.checkNotNull(hql);
Map hqlParameters = Collections.emptyMap();
return stream(hql, hqlParameters);
}
public Stream stream(String hql, Map hqlParameters) {
Preconditions.checkNotNull(hql);
Preconditions.checkNotNull(hqlParameters);
return topiaJpaSupport.stream(hql, hqlParameters);
}
protected List find(String hql, int startIndex, int endIndex) {
Preconditions.checkNotNull(hql);
Map hqlParameters = Collections.emptyMap();
return find(hql, hqlParameters, startIndex, endIndex);
}
protected List find(String hql, Map hqlParameters, int startIndex, int endIndex) {
Preconditions.checkNotNull(hql);
Preconditions.checkNotNull(hqlParameters);
return topiaJpaSupport.find(hql, startIndex, endIndex, hqlParameters);
}
protected List find(String hql, Map hqlParameters, PaginationParameter page) {
Preconditions.checkNotNull(hql);
Preconditions.checkNotNull(hqlParameters);
Preconditions.checkNotNull(page);
boolean hqlContainsOrderClause = hqlContainsOrderBy(hql);
boolean pageContainsOrderClause = !page.getOrderClauses().isEmpty();
if (!hqlContainsOrderClause && !pageContainsOrderClause) {
throw new QueryMissingOrderException(hql, hqlParameters, page);
}
// Must have one (and only one) order by clause in query
Preconditions.checkArgument(
hqlContainsOrderClause ^ pageContainsOrderClause,
String.format(
"One 'order by' clause (and only one) must be specified. [orderByInHql=%b] [orderByInPage=%b]",
hqlContainsOrderClause,
pageContainsOrderClause)
);
if (pageContainsOrderClause) {
hql += " ORDER BY ";
Iterable orderClauses = page.getOrderClauses().stream().map(PAGINATION_ORDER_TO_HQL).collect(Collectors.toList());
hql += Joiner.on(", ").join(orderClauses);
}
return topiaJpaSupport.find(
hql,
page.getStartIndex(),
page.getEndIndex(),
hqlParameters);
}
protected PaginationResult findPage(String hql, Map hqlParameters, PaginationParameter page) {
List elements = find(hql, hqlParameters, page);
String countHql = "select count(topiaId) ";
if (hqlStartsWithSelect(hql)) {
// must remove the from clause, otherwise some sql queries won't work.
countHql += hql.substring(hql.toLowerCase().indexOf(" from "));
} else {
countHql += hql;
}
if (hqlContainsOrderBy(countHql)) {
// must remove the order by clause, otherwise some sql queries won't work.
countHql = countHql.substring(0, countHql.toLowerCase().indexOf("order by"));
}
long count = count(countHql, hqlParameters);
return PaginationResult.of(elements, count, page);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep forTopiaIdEquals(String topiaId) {
Preconditions.checkArgument(StringUtils.isNotBlank(topiaId), "given topiaId is blank");
return forEquals(TopiaEntity.PROPERTY_TOPIA_ID, topiaId);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep forTopiaIdIn(Collection topiaIds) {
Preconditions.checkNotNull(topiaIds, "given topiaIds is null");
return forIn(TopiaEntity.PROPERTY_TOPIA_ID, topiaIds);
}
@Override
public List findAllIds() {
return newQueryBuilder().findAllIds();
}
@Override
public List findAll() {
return newQueryBuilder().findAll();
}
@Override
public Stream streamAll() {
return newQueryBuilder().stream();
}
@Override
public long count() {
return newQueryBuilder().count();
}
protected boolean hqlContainsOrderBy(String hql) {
return hql.toLowerCase().contains("order by");
}
protected boolean hqlStartsWithSelect(String hql) {
return hql.toLowerCase().trim().startsWith("select ");
}
protected boolean hqlContainsCount(String hql) {
return hql.toLowerCase().contains("count(");
}
@Override
public long findSingleResult(TopiaSqlQuery query) {
return topiaSqlSupport.findSingleResult(query);
}
@Override
public TopiaDaoSupplier topiaDaoSupplier() {
return topiaDaoSupplier;
}
@Override
public E newInstance() {
return newInstance0();
}
@Override
public List findMultipleResult(TopiaSqlQuery query) {
return topiaSqlSupport.findMultipleResult(query);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep newQueryBuilder() {
return newQueryBuilder(FilterRuleGroupOperator.AND);
}
@Override
public TopiaQueryBuilderAddCriteriaOrRunQueryStep newQueryBuilder(FilterRuleGroupOperator filterRuleGroupOperator) {
HqlAndParametersBuilder hqlAndParametersBuilder = new HqlAndParametersBuilder<>(getEntityClass(), "main", filterRuleGroupOperator);
return new AbstractTopiaDaoQueryBuilderAddCriteriaOrRunQueryStep<>(this, hqlAndParametersBuilder);
}
protected boolean hqlStartsWithFrom(String hql) {
return hql.toLowerCase().trim().startsWith("from ");
}
@Override
public TopiaSqlSupport getTopiaSqlSupport() {
return topiaSqlSupport;
}
@Override
public final Query
© 2015 - 2025 Weber Informatics LLC | Privacy Policy