mirror of
https://codeup.aliyun.com/64f7d6b8ce01efaafef1e678/coal/coal.git
synced 2026-01-24 23:41:21 +08:00
优化
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -54,6 +54,11 @@
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
<version>2.3.32</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
|
||||
@@ -149,6 +149,13 @@ public class CommonQuery {
|
||||
parseKey(root, x.key), c.convert(x.value, String.class));
|
||||
});
|
||||
|
||||
map.put(
|
||||
Tuple.of("eq", Boolean.class),
|
||||
(Root root, CriteriaBuilder criteriaBuilder, QueryItem x, ConversionService c) -> {
|
||||
return criteriaBuilder.equal(
|
||||
parseKey(root, x.key), c.convert(x.value, Boolean.class));
|
||||
});
|
||||
|
||||
map.put(
|
||||
Tuple.of("eq", Integer.class),
|
||||
(Root root, CriteriaBuilder criteriaBuilder, QueryItem x, ConversionService c) -> {
|
||||
@@ -207,6 +214,12 @@ public class CommonQuery {
|
||||
return criteriaBuilder.notEqual(
|
||||
parseKey(root, x.key), c.convert(x.value, String.class));
|
||||
});
|
||||
map.put(
|
||||
Tuple.of("neq", Boolean.class),
|
||||
(Root root, CriteriaBuilder criteriaBuilder, QueryItem x, ConversionService c) -> {
|
||||
return criteriaBuilder.notEqual(
|
||||
parseKey(root, x.key), c.convert(x.value, Boolean.class));
|
||||
});
|
||||
|
||||
map.put(
|
||||
Tuple.of("neq", Integer.class),
|
||||
|
||||
338
src/main/java/cn/lihongjie/coal/common/FreeMakerUtils.java
Normal file
338
src/main/java/cn/lihongjie/coal/common/FreeMakerUtils.java
Normal file
@@ -0,0 +1,338 @@
|
||||
package cn.lihongjie.coal.common;
|
||||
|
||||
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.*;
|
||||
import freemarker.template.utility.DeepUnwrap;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.RegExUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@UtilityClass
|
||||
public class FreeMakerUtils {
|
||||
|
||||
private static final Configuration cfg;
|
||||
|
||||
private static final Cache<String, Template> templateCache =
|
||||
Caffeine.newBuilder().maximumSize(10_000).build();
|
||||
|
||||
static {
|
||||
|
||||
// Create your Configuration instance, and specify if up to what FreeMarker
|
||||
// version (here 2.3.32) do you want to apply the fixes that are not 100%
|
||||
// backward-compatible. See the Configuration JavaDoc for details.
|
||||
cfg = new Configuration(Configuration.VERSION_2_3_32);
|
||||
|
||||
// Specify the source where the template files come from. Here I set a
|
||||
// plain directory for it, but non-file-system sources are possible too:
|
||||
|
||||
// From here we will set the settings recommended for new projects. These
|
||||
// aren't the defaults for backward compatibilty.
|
||||
|
||||
// Set the preferred charset template files are stored in. UTF-8 is
|
||||
// a good choice in most applications:
|
||||
cfg.setDefaultEncoding("UTF-8");
|
||||
|
||||
// Sets how errors will appear.
|
||||
// During web page *development* TemplateExceptionHandler.HTML_DEBUG_HANDLER is better.
|
||||
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
||||
|
||||
// Don't log exceptions inside FreeMarker that it will thrown at you anyway:
|
||||
cfg.setLogTemplateExceptions(false);
|
||||
|
||||
// Wrap unchecked exceptions thrown during template processing into TemplateException-s:
|
||||
cfg.setWrapUncheckedExceptions(true);
|
||||
|
||||
// Do not fall back to higher scopes when reading a null loop variable:
|
||||
cfg.setFallbackOnNullLoopVariable(false);
|
||||
|
||||
// To accomodate to how JDBC returns values; see Javadoc!
|
||||
cfg.setSQLDateAndTimeTimeZone(TimeZone.getDefault());
|
||||
|
||||
cfg.setNumberFormat("c");
|
||||
|
||||
cfg.setSharedVariable("if", new MyBatisIf());
|
||||
cfg.setSharedVariable("trim", new MyBatisTrim());
|
||||
cfg.setSharedVariable("where", new MyBatisTrim("where", "AND |OR "));
|
||||
cfg.setSharedVariable("set", new MyBatisTrim("set", ","));
|
||||
cfg.setSharedVariable("foreach", new MyBatisForeach());
|
||||
}
|
||||
|
||||
private static String getString(Map params, String key) throws TemplateModelException {
|
||||
return getString(params, key, true, null);
|
||||
}
|
||||
|
||||
private static String getString(Map params, String key, String defaultVal)
|
||||
throws TemplateModelException {
|
||||
return getString(params, key, false, defaultVal);
|
||||
}
|
||||
|
||||
private static String getString(Map params, String key, boolean required, String defaultVal)
|
||||
throws TemplateModelException {
|
||||
|
||||
Object o = params.get(key);
|
||||
|
||||
if (o == null && required) {
|
||||
throw new TemplateModelException(key + " is required");
|
||||
}
|
||||
|
||||
if (o == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
if (!(o instanceof SimpleScalar s)) {
|
||||
throw new TemplateModelException(key + " must be string");
|
||||
}
|
||||
return StringUtils.defaultIfEmpty(s.getAsString(), defaultVal);
|
||||
}
|
||||
|
||||
private static Boolean getBoolean(Map params, String key, boolean required, Boolean defaultVal)
|
||||
throws TemplateModelException {
|
||||
|
||||
Object o = params.get(key);
|
||||
|
||||
if (o == null && required) {
|
||||
throw new TemplateModelException(key + " is required");
|
||||
}
|
||||
|
||||
if (o == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
if ((o instanceof SimpleScalar s)) {
|
||||
return Boolean.parseBoolean(s.getAsString());
|
||||
} else if (o instanceof TemplateBooleanModel s) {
|
||||
return s.getAsBoolean();
|
||||
} else {
|
||||
|
||||
throw new TemplateModelException(key + " must be boolean");
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String render( String template, Object model) {
|
||||
|
||||
if (StringUtils.isBlank(template)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (model == null) {
|
||||
model = new HashMap<>();
|
||||
}
|
||||
|
||||
String hex = DigestUtils.md5DigestAsHex(template.getBytes(StandardCharsets.UTF_8));
|
||||
Template ft =
|
||||
templateCache
|
||||
.asMap()
|
||||
.computeIfAbsent(
|
||||
hex,
|
||||
k -> {
|
||||
try {
|
||||
return new Template(hex, template, cfg);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
StringWriter out = new StringWriter();
|
||||
ft.process(model, out);
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
public static class MyBatisIf implements TemplateDirectiveModel {
|
||||
|
||||
@Override
|
||||
public void execute(
|
||||
Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
|
||||
throws TemplateException, IOException {
|
||||
|
||||
Object o = params.get("test");
|
||||
if (o == null) {
|
||||
throw new TemplateException("test is required", env);
|
||||
}
|
||||
|
||||
if (o instanceof TemplateBooleanModel b) {
|
||||
|
||||
if (b.getAsBoolean() && body != null) {
|
||||
body.render(env.getOut());
|
||||
}
|
||||
} else {
|
||||
throw new TemplateException("test must be boolean", env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <foreach item="item" index="index" collection="list" open="ID in (" separator="," close=")"
|
||||
* nullable="true"> #{item} </foreach>
|
||||
*/
|
||||
public static class MyBatisForeach implements TemplateDirectiveModel {
|
||||
|
||||
@Override
|
||||
public void execute(
|
||||
Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
|
||||
throws TemplateException, IOException {
|
||||
|
||||
String item = getString(params, "item", "__item");
|
||||
String index = getString(params, "index", "__index");
|
||||
String open = getString(params, "open", "");
|
||||
String separator = getString(params, "separator", "");
|
||||
String close = getString(params, "close", "");
|
||||
Boolean nullable = getBoolean(params, "nullable", false, true);
|
||||
|
||||
Object collection = params.get("collection");
|
||||
|
||||
if (nullable && collection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (collection == null) {
|
||||
throw new TemplateException("collection is null and nullable set to false", env);
|
||||
}
|
||||
|
||||
Object object = DeepUnwrap.permissiveUnwrap((TemplateModel) collection);
|
||||
|
||||
|
||||
|
||||
if (object instanceof String){
|
||||
object =
|
||||
DeepUnwrap.permissiveUnwrap(
|
||||
env.getDataModelOrSharedVariable((String) object));
|
||||
|
||||
}else if (object instanceof TemplateModelAdapter adapter){
|
||||
object = DeepUnwrap.unwrap(adapter.getTemplateModel());
|
||||
}
|
||||
|
||||
|
||||
env.getOut().write(open);
|
||||
|
||||
if (object instanceof Iterable<?> iterable) {
|
||||
|
||||
int idx = 0;
|
||||
for (Object o : iterable) {
|
||||
|
||||
env.setVariable(item, env.getObjectWrapper().wrap(o));
|
||||
env.setVariable(index, env.getObjectWrapper().wrap(idx));
|
||||
idx++;
|
||||
|
||||
body.render(env.getOut());
|
||||
|
||||
env.getOut().write(separator);
|
||||
}
|
||||
|
||||
} else if (object instanceof Map<?, ?> map) {
|
||||
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
env.setVariable(item, env.getObjectWrapper().wrap(entry.getValue()));
|
||||
env.setVariable(index, env.getObjectWrapper().wrap(entry.getKey()));
|
||||
|
||||
body.render(env.getOut());
|
||||
|
||||
env.getOut().write(separator);
|
||||
}
|
||||
|
||||
} else if (object.getClass().isArray()) {
|
||||
|
||||
Object[] array = (Object[]) object;
|
||||
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
env.setVariable(item, env.getObjectWrapper().wrap(array[i]));
|
||||
env.setVariable(index, env.getObjectWrapper().wrap(i));
|
||||
|
||||
body.render(env.getOut());
|
||||
|
||||
env.getOut().write(separator);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new TemplateException("collection must be iterable/map/array", env);
|
||||
}
|
||||
|
||||
env.getOut().write(close);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MyBatisTrim implements TemplateDirectiveModel {
|
||||
|
||||
private SimpleScalar defaultPrefix;
|
||||
|
||||
private SimpleScalar defaultPrefixOverrides;
|
||||
|
||||
public MyBatisTrim() {}
|
||||
|
||||
public MyBatisTrim(String defaultPrefix, String defaultPrefixOverrides) {
|
||||
|
||||
Assert.hasLength(defaultPrefix, "defaultPrefix is required");
|
||||
Assert.hasLength(defaultPrefixOverrides, "defaultPrefixOverrides is required");
|
||||
|
||||
this.defaultPrefix = new SimpleScalar(defaultPrefix);
|
||||
this.defaultPrefixOverrides = new SimpleScalar(defaultPrefixOverrides);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(
|
||||
Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
|
||||
throws TemplateException, IOException {
|
||||
|
||||
Object p = ObjectUtils.defaultIfNull(params.get("prefix"), defaultPrefix);
|
||||
Object po =
|
||||
ObjectUtils.defaultIfNull(
|
||||
params.get("prefixOverrides"), defaultPrefixOverrides);
|
||||
|
||||
try {
|
||||
|
||||
Assert.isInstanceOf(SimpleScalar.class, p, "prefix must be string");
|
||||
Assert.isInstanceOf(SimpleScalar.class, po, "prefixOverrides must be string");
|
||||
|
||||
String prefix = ((SimpleScalar) p).getAsString();
|
||||
String prefixOverrides = ((SimpleScalar) po).getAsString();
|
||||
|
||||
if (body != null) {
|
||||
StringWriter out = new StringWriter();
|
||||
body.render(out);
|
||||
|
||||
String bodyStr = out.toString();
|
||||
|
||||
if (StringUtils.isNotBlank(bodyStr)) {
|
||||
|
||||
Iterable<String> iterable =
|
||||
Splitter.on("|").omitEmptyStrings().split(prefixOverrides);
|
||||
|
||||
for (String s : iterable) {
|
||||
|
||||
bodyStr =
|
||||
RegExUtils.replaceFirst(
|
||||
bodyStr,
|
||||
Pattern.compile(
|
||||
"^(\\s*)" + Pattern.quote(s), CASE_INSENSITIVE),
|
||||
"$1");
|
||||
}
|
||||
}
|
||||
bodyStr = prefix + " " + bodyStr;
|
||||
|
||||
env.getOut().write(bodyStr);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new TemplateException(e.getMessage(), e, env);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,33 @@
|
||||
package cn.lihongjie.coal.common;
|
||||
|
||||
import static org.springframework.data.jpa.repository.query.QueryUtils.toOrders;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
|
||||
import jakarta.persistence.Tuple;
|
||||
import jakarta.persistence.TupleElement;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.support.PageableUtils;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -26,11 +40,15 @@ public class JpaUtils {
|
||||
(String name) -> CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name);
|
||||
|
||||
public static List<Map> convertTuplesToRawMap(List<Tuple> tuples) {
|
||||
return convertTuplesToMap(tuples, Function.identity(), Function.identity()).stream().map(x -> (Map) x).collect(Collectors.toList());
|
||||
return convertTuplesToMap(tuples, Function.identity(), Function.identity()).stream()
|
||||
.map(x -> (Map) x)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<Map<String, Object>> convertTuplesToMap(List<Tuple> tuples) {
|
||||
return convertTuplesToMap(tuples, Function.identity(), Function.identity());
|
||||
}
|
||||
|
||||
public static Function<Object, Object> handleArray =
|
||||
(Object value) -> {
|
||||
if (value == null) {
|
||||
@@ -51,14 +69,14 @@ public class JpaUtils {
|
||||
};
|
||||
|
||||
public static <T> void mergeMapToPojo(
|
||||
Iterable<T> pojos,
|
||||
Iterable<Map> maps,
|
||||
ConversionService conversionService) {
|
||||
|
||||
|
||||
mergeMapToPojo(pojos, x -> ((Function<Object, String>) ReflectUtils::getId).apply(x), maps, "id", conversionService);
|
||||
|
||||
Iterable<T> pojos, Iterable<Map> maps, ConversionService conversionService) {
|
||||
|
||||
mergeMapToPojo(
|
||||
pojos,
|
||||
x -> ((Function<Object, String>) ReflectUtils::getId).apply(x),
|
||||
maps,
|
||||
"id",
|
||||
conversionService);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -93,12 +111,11 @@ public class JpaUtils {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Map map = mapMap.get(id);
|
||||
|
||||
for (Object key : map.keySet()) {
|
||||
|
||||
if (StringUtils.equals(key.toString(), mapKey)){
|
||||
if (StringUtils.equals(key.toString(), mapKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -121,13 +138,154 @@ public class JpaUtils {
|
||||
|
||||
Field field = ReflectUtils.getField(pojo.getClass(), fieldName).get();
|
||||
|
||||
ReflectUtils.writeField(pojo, fieldName, conversionService.convert(value, field.getType()));
|
||||
|
||||
|
||||
ReflectUtils.writeField(
|
||||
pojo, fieldName, conversionService.convert(value, field.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> execNativeQuery(
|
||||
EntityManager entityManager, String sql, Object parameter, Class<T> clazz) {
|
||||
|
||||
if (clazz.isAssignableFrom(Map.class)) {
|
||||
Query query = entityManager.createNativeQuery(sql, Tuple.class);
|
||||
setQueryParameter(query, parameter);
|
||||
return convertTuplesToMap(query.getResultList(), underscoreToCamelCase);
|
||||
} else {
|
||||
|
||||
Query query = entityManager.createNativeQuery(sql, clazz);
|
||||
|
||||
setQueryParameter(query, parameter);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setQueryParameter(Query query, Object parameter) {
|
||||
|
||||
if (query == null || parameter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameter instanceof Map) {
|
||||
Map<String, Object> map = (Map<String, Object>) parameter;
|
||||
for (String key : map.keySet()) {
|
||||
query.setParameter(key, map.get(key));
|
||||
}
|
||||
} else if (parameter instanceof Iterable<?> it) {
|
||||
|
||||
query.setParameter("param", it);
|
||||
|
||||
int index = 0;
|
||||
for (Object o : it) {
|
||||
|
||||
query.setParameter("param" + index, o);
|
||||
}
|
||||
} else {
|
||||
|
||||
ReflectUtils.getAllFieldsList(parameter.getClass()).stream()
|
||||
.filter(x -> !Modifier.isStatic(x.getModifiers()))
|
||||
.forEach(
|
||||
x -> {
|
||||
Object value = ReflectUtils.getFieldValue(parameter, x.getName());
|
||||
query.setParameter(x.getName(), value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static <S extends T> TypedQuery<S> getQuery(
|
||||
EntityManager entityManager, Specification<S> spec, Class<S> domainClass, Sort sort) {
|
||||
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<S> query = builder.createQuery(domainClass);
|
||||
|
||||
Root<S> root = applySpecificationToCriteria(entityManager, spec, domainClass, query);
|
||||
query.select(root);
|
||||
|
||||
if (sort.isSorted()) {
|
||||
query.orderBy(toOrders(sort, root, builder));
|
||||
}
|
||||
|
||||
return (entityManager.createQuery(query));
|
||||
}
|
||||
|
||||
public static <S extends T> Page<S> readPage(
|
||||
EntityManager entityManager,
|
||||
TypedQuery<S> query,
|
||||
final Class<S> domainClass,
|
||||
Pageable pageable,
|
||||
@Nullable Specification<S> spec) {
|
||||
|
||||
if (pageable.isPaged()) {
|
||||
query.setFirstResult(PageableUtils.getOffsetAsInteger(pageable));
|
||||
query.setMaxResults(pageable.getPageSize());
|
||||
}
|
||||
|
||||
return PageableExecutionUtils.getPage(
|
||||
query.getResultList(),
|
||||
pageable,
|
||||
() -> executeCountQuery(getCountQuery(entityManager, spec, domainClass)));
|
||||
}
|
||||
|
||||
private static long executeCountQuery(TypedQuery<Long> query) {
|
||||
|
||||
Assert.notNull(query, "TypedQuery must not be null");
|
||||
|
||||
List<Long> totals = query.getResultList();
|
||||
long total = 0L;
|
||||
|
||||
for (Long element : totals) {
|
||||
total += element == null ? 0 : element;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public static <S extends T> TypedQuery<Long> getCountQuery(
|
||||
EntityManager entityManager, @Nullable Specification<S> spec, Class<S> domainClass) {
|
||||
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> query = builder.createQuery(Long.class);
|
||||
|
||||
Root<S> root = applySpecificationToCriteria(entityManager, spec, domainClass, query);
|
||||
|
||||
if (query.isDistinct()) {
|
||||
query.select(builder.countDistinct(root));
|
||||
} else {
|
||||
query.select(builder.count(root));
|
||||
}
|
||||
|
||||
// Remove all Orders the Specifications might have applied
|
||||
query.orderBy(Collections.emptyList());
|
||||
|
||||
return (entityManager.createQuery(query));
|
||||
}
|
||||
|
||||
private static <S, U extends T> Root<U> applySpecificationToCriteria(
|
||||
EntityManager entityManager,
|
||||
@Nullable Specification<U> spec,
|
||||
Class<U> domainClass,
|
||||
CriteriaQuery<S> query) {
|
||||
|
||||
Assert.notNull(domainClass, "Domain class must not be null");
|
||||
Assert.notNull(query, "CriteriaQuery must not be null");
|
||||
|
||||
Root<U> root = query.from(domainClass);
|
||||
|
||||
if (spec == null) {
|
||||
return root;
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
Predicate predicate = spec.toPredicate(root, query, builder);
|
||||
|
||||
if (predicate != null) {
|
||||
query.where(predicate);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public static List<Map<String, Object>> convertTuplesToMap(
|
||||
List<Tuple> tuples, Function<String, String> keyMapper) {
|
||||
return convertTuplesToMap(tuples, keyMapper, Function.identity());
|
||||
|
||||
@@ -16,12 +16,14 @@ import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@UtilityClass
|
||||
public class ReflectUtils {
|
||||
@@ -94,12 +96,23 @@ public class ReflectUtils {
|
||||
return getFieldCache.get(
|
||||
new Tuple2<>(cls, fieldName),
|
||||
() -> {
|
||||
return fieldCache.get(cls, () -> FieldUtils.getAllFieldsList(cls)).stream()
|
||||
return doGetAllFields(cls).stream()
|
||||
.filter(x -> x.getName().equals(fieldName))
|
||||
.findFirst();
|
||||
});
|
||||
}
|
||||
|
||||
private static @NotNull List<Field> doGetAllFields(Class cls) throws ExecutionException {
|
||||
return fieldCache.get(cls, () -> FieldUtils.getAllFieldsList(cls));
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public static List<Field> getAllFieldsList(Class cls){
|
||||
|
||||
return doGetAllFields(cls);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static Object invokeMethod(Object object, String methodName) {
|
||||
return MethodUtils.invokeMethod(object, methodName);
|
||||
|
||||
@@ -462,6 +462,11 @@ public class WeightDeviceDataEntity extends OrgCommonEntity {
|
||||
|
||||
private LocalDateTime minTime;
|
||||
|
||||
|
||||
private Boolean invalid = false;
|
||||
|
||||
private Boolean finished = true;
|
||||
|
||||
@Override
|
||||
public void prePersist() {
|
||||
super.prePersist();
|
||||
@@ -481,5 +486,9 @@ public class WeightDeviceDataEntity extends OrgCommonEntity {
|
||||
.filter(Objects::nonNull)
|
||||
.min(LocalDateTime::compareTo)
|
||||
.orElse(null);
|
||||
|
||||
|
||||
this.finished = this.pzTime!=null && this.mzTime!=null && this.ecgbTime!=null && this.ycgbTIme!=null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,11 @@ public class WeightDeviceDataService
|
||||
where += " and d.specification = :specification ";
|
||||
}
|
||||
|
||||
where += " and ( d.invalid is null or !d.invalid ) ";
|
||||
where += " and ( d.finished is null or d.finished ) ";
|
||||
|
||||
|
||||
|
||||
var sql =
|
||||
"select DATE_TRUNC('"
|
||||
+ request.getTimeDimension()
|
||||
@@ -259,6 +264,8 @@ public class WeightDeviceDataService
|
||||
countQuery.setParameter("specification", "%" + request.getSpecification() + "%");
|
||||
}
|
||||
|
||||
|
||||
|
||||
var resultList = JpaUtils.convertTuplesToMap(selectQuery.getResultList());
|
||||
|
||||
var ans =
|
||||
|
||||
9
src/main/java/freemarker_implicit.ftl
Normal file
9
src/main/java/freemarker_implicit.ftl
Normal file
@@ -0,0 +1,9 @@
|
||||
[#ftl]
|
||||
[#-- @implicitly included --]
|
||||
|
||||
[#macro foreach item index collection open separator close nullable][/#macro]
|
||||
[#macro where][/#macro]
|
||||
[#macro set][/#macro]
|
||||
|
||||
[#macro trim prefix prefixOverrides][/#macro]
|
||||
[#macro if test][/#macro]
|
||||
165
src/test/java/cn/lihongjie/coal/common/FreeMakerUtilsTest.java
Normal file
165
src/test/java/cn/lihongjie/coal/common/FreeMakerUtilsTest.java
Normal file
@@ -0,0 +1,165 @@
|
||||
package cn.lihongjie.coal.common;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import freemarker.template.TemplateException;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class FreeMakerUtilsTest {
|
||||
|
||||
@Test
|
||||
void testIf() {
|
||||
|
||||
Assertions.assertEquals(
|
||||
"t",
|
||||
FreeMakerUtils.render(
|
||||
"""
|
||||
<@if test=b>
|
||||
|
||||
t
|
||||
</@if>
|
||||
|
||||
""",
|
||||
Map.of("b", true))
|
||||
.trim());
|
||||
|
||||
Assertions.assertThrows(
|
||||
TemplateException.class,
|
||||
() -> {
|
||||
FreeMakerUtils.render(
|
||||
"""
|
||||
<@if test=b>
|
||||
|
||||
t
|
||||
</@if>
|
||||
|
||||
""",
|
||||
Map.of("b", "1"))
|
||||
.trim();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTrim() {
|
||||
|
||||
Assertions.assertEquals(
|
||||
"where 1=1",
|
||||
FreeMakerUtils.render(
|
||||
"""
|
||||
<@trim prefix="where" prefixOverrides="AND |OR ">
|
||||
and 1=1
|
||||
</@trim>
|
||||
|
||||
""",
|
||||
Map.of("b", true))
|
||||
.trim());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhere() {
|
||||
|
||||
Assertions.assertEquals(
|
||||
"where 1=1",
|
||||
FreeMakerUtils.render(
|
||||
"""
|
||||
<@where >
|
||||
and 1=1
|
||||
</@where>
|
||||
|
||||
""",
|
||||
Map.of("b", true))
|
||||
.trim());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSet() {
|
||||
|
||||
Assertions.assertEquals(
|
||||
"set a=1",
|
||||
FreeMakerUtils.render(
|
||||
"""
|
||||
<@set >
|
||||
,a=1
|
||||
</@set>
|
||||
|
||||
""",
|
||||
Map.of("b", true))
|
||||
.trim());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testObject() {
|
||||
Assertions.assertEquals(
|
||||
"set a=1",
|
||||
FreeMakerUtils.render(
|
||||
"""
|
||||
<@set >
|
||||
,a=1
|
||||
</@set>
|
||||
|
||||
""",
|
||||
new Demo("1") )
|
||||
.trim());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testForeach() {
|
||||
Assertions.assertEquals(
|
||||
"""
|
||||
where id in ( 1
|
||||
, 2
|
||||
, 3
|
||||
,)
|
||||
""".trim(),
|
||||
FreeMakerUtils.render(
|
||||
"""
|
||||
<@where>
|
||||
<@foreach item="item" index="index" collection="ids"
|
||||
open="id in (" separator="," close=")" nullable="true">
|
||||
${item}
|
||||
</@foreach>
|
||||
</@where>
|
||||
|
||||
""",
|
||||
Map.of("ids", List.of(1, 2, 3)))
|
||||
.trim());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testForeach2() {
|
||||
Assertions.assertEquals(
|
||||
"""
|
||||
where id in ( :item_0
|
||||
, :item_1
|
||||
, :item_2
|
||||
,)
|
||||
"""
|
||||
.trim(),
|
||||
FreeMakerUtils.render(
|
||||
"""
|
||||
<@where>
|
||||
<@foreach item="item" index="index" collection="ids"
|
||||
open="id in (" separator="," close=")" nullable="true">
|
||||
:item_${index}
|
||||
</@foreach>
|
||||
</@where>
|
||||
|
||||
""",
|
||||
Map.of("ids", List.of(1, 2, 3)))
|
||||
.trim());
|
||||
}
|
||||
|
||||
record Demo(String k){
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user