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

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> getMapQuery(String queryName) { return topiaHibernateSupport.getMapQuery(getEntityClass().getName() + "::" + queryName); } @Override public final NativeQuery getSqlQuery(String queryName) { return topiaHibernateSupport.getSqlQuery(getEntityClass().getName() + "::" + queryName); } @Override public final Query getQuery(String queryName) { return topiaHibernateSupport.getQuery(getEntityClass().getName() + "::" + queryName); } @Override public void init(AbstractTopiaPersistenceContext persistenceContext) { init(persistenceContext.getJpaSupport(), persistenceContext.getHibernateSupport(), persistenceContext.getSqlSupport(), persistenceContext.getTopiaIdFactory(), persistenceContext); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy