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

net.cofcool.chaos.server.data.jpa.util.SpecificationUtils Maven / Gradle / Ivy

There is a newer version: 0.6.0
Show newest version
/*
 * Copyright 2019 cofcool
 *
 * 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 net.cofcool.chaos.server.data.jpa.util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.query.criteria.LiteralHandlingMode;
import org.hibernate.query.criteria.internal.compile.ExplicitParameterInfo;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.predicate.CompoundPredicate;
import org.hibernate.type.Type;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.Specification;

/**
 * Specification 的一些方便方法
 *
 * @author CofCool
 */
public class SpecificationUtils {

    public static > Specification andGreaterOrEqualTo(Specification sp, String property, Y val) {
        return sp.and((root, query, criteriaBuilder) ->
                    criteriaBuilder.greaterThanOrEqualTo(root.get(property), val));
    }

    public static > Specification andLessOrEqualTo(Specification sp, String property, Y val) {
        return sp.and((root, query, criteriaBuilder) ->
                    criteriaBuilder.lessThanOrEqualTo(root.get(property), val));
    }

    public static > Specification andBetween(Specification sp, String property, Y minVal, Y maxVal) {
        return sp.and((root, query, criteriaBuilder) ->
                    criteriaBuilder.between(root.get(property), minVal, maxVal));
    }

    public static > Specification andEqualTo(Specification sp, String property, Y val) {
        return sp.and((root, query, criteriaBuilder) ->
            criteriaBuilder.equal(root.get(property), val));
    }

    public static > Specification andNotEqualTo(Specification sp, String property, Y val) {
        return sp.and((root, query, criteriaBuilder) ->
            criteriaBuilder.notEqual(root.get(property), val));
    }

    public static > Specification andLikeTo(Specification sp, String property, Y val, boolean fullLike) {
        return sp.and((root, query, criteriaBuilder) ->
                criteriaBuilder.like(root.get(property), fullLike ? ("%" + val + "%") : (val + "%")));
    }

    public static  Specification andIn(Specification sp, String property, List vals) {
        return sp.and((root, query, criteriaBuilder) -> {
            Path pro = root.get(property);
            CriteriaBuilder.In in = criteriaBuilder.in(pro);
            for (Y data : vals){
                in.value(data);
            }
            return criteriaBuilder.and(in);
        });
    }

    /**
     * 根据 Specification 生成 SQL 语句
     * @param baseSql 未带条件的基本 SQL
     * @param alias Entity 别名
     * @param sp Specification
     * @param entityManager SessionImplementor
     * @param type 表对应的 Entity 类型
     * @param sort 排序
     * @param  表对应的 Entity 类型
     * @return 根据 Specification 生成的新 SQL
     */
    public static  String render(String baseSql, String alias, Specification sp, SessionImplementor entityManager, Class type, @Nullable Sort sort) {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery query = builder.createQuery(type);
        Root root = query.from(type);
        Predicate p = sp.toPredicate(root, query, builder);

        StringBuilder sql = new StringBuilder(baseSql)
            .append(" where ")
            .append(
                ((CompoundPredicate)p)
                    .render(
                        CACHED_RENDERING_CONTEXTS.computeIfAbsent(
                            alias,
                            a1 -> new SimpleRenderingContext(alias, entityManager)
                        )
                    )
            );
        if (sort != null && sort.isSorted()) {
            sql.append(" order by ");

            renderSort(alias, sort, sql);
        }

        return sql.toString();
    }


    /**
     * 根据 Sort 生成 SQL 语句(不包括"order by")
     * @param alias 别名
     * @param sort 排序
     * @return 排序语句
     */
    public static String renderSort(String alias, Sort sort) {
        StringBuilder sql = new StringBuilder();
        renderSort(alias, sort, sql);

        return sql.toString();
    }

    /**
     * 根据 Sort 生成 SQL 语句(不包括"order by")
     * @param alias 别名
     * @param sort 排序
     * @param sql SQL 字符串
     */
    public static void renderSort(String alias, Sort sort, StringBuilder sql) {
        Iterator sortIterator = sort.iterator();
        while (sortIterator.hasNext()) {
            Order order = sortIterator.next();
            sql.append(alias)
                .append(".")
                .append(order.getProperty())
                .append(" ")
                .append(order.getDirection().toString());
            if (sortIterator.hasNext()) {
                sql.append(", ");
            }
        }
    }

    /**
     * 

* JDK 8 的 HashMap 在并发操作(如computeIfAbsent)时并不会抛出 ConcurrentModificationException 异常, * 但是 JDK 11 会抛出异常, 因此改为 ConcurrentHashMap。 *

     *     int mc = modCount;
     *     V v = mappingFunction.apply(key);
     *     if (mc != modCount) { throw new ConcurrentModificationException(); }
     * 
*

*/ private static final Map CACHED_RENDERING_CONTEXTS = new ConcurrentHashMap<>(); /** * @see org.hibernate.query.criteria.internal.compile.CriteriaCompiler */ private static class SimpleRenderingContext implements RenderingContext { private final Map, ExplicitParameterInfo> explicitParameterInfoMap = new HashMap<>(); private String alias; private SessionImplementor entityManager; private Dialect dialect; public SimpleRenderingContext(String alias, SessionImplementor entityManager) { this.alias = alias; this.entityManager = entityManager; this.dialect = entityManager.getSessionFactory().getServiceRegistry().getService( JdbcServices.class ).getDialect(); } @Override public String generateAlias() { return alias; } @SuppressWarnings("unchecked") @Override public ExplicitParameterInfo registerExplicitParameter( ParameterExpression criteriaQueryParameter) { ExplicitParameterInfo parameterInfo = explicitParameterInfoMap.get( criteriaQueryParameter ); if ( parameterInfo == null ) { if ( StringHelper.isNotEmpty( criteriaQueryParameter.getName() ) ) { parameterInfo = new ExplicitParameterInfo( criteriaQueryParameter.getName(), null, criteriaQueryParameter.getJavaType() ); } else if ( criteriaQueryParameter.getPosition() != null ) { parameterInfo = new ExplicitParameterInfo( null, criteriaQueryParameter.getPosition(), criteriaQueryParameter.getJavaType() ); } else { throw new IllegalArgumentException("can not invoke for generated parameter"); } explicitParameterInfoMap.put( criteriaQueryParameter, parameterInfo ); } return parameterInfo; } public String registerLiteralParameterBinding(final Object literal, final Class javaType) { return literal.toString(); } public String getCastType(Class javaType) { SessionFactoryImplementor factory = entityManager.getFactory(); Type hibernateType = factory.getTypeResolver().heuristicType( javaType.getName() ); if ( hibernateType == null ) { throw new IllegalArgumentException( "Could not convert java type [" + javaType.getName() + "] to Hibernate type" ); } return hibernateType.getName(); } @Override public Dialect getDialect() { return dialect; } @Override public LiteralHandlingMode getCriteriaLiteralHandlingMode() { return LiteralHandlingMode.INLINE; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy