mirror of https://github.com/halo-dev/halo
Automate attribute converter (#1325)
* Deprecate AbstractConverter * Remove unused enum and attribute converter * Add AttributeConverterApplyTest * Add JpaConfiguration * Add AttributeConverterAutoGenerator * Integrate automate-attribute-converter * Rename JpaConfiguration * Remove useless attribute converters * Exclude property enums for auto-generating * Refine JournalType definition * Fix an error about existing injected typepull/1331/head
parent
e6b32ac8c2
commit
8472a7b3d5
|
@ -10,6 +10,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||||
|
@ -19,6 +20,7 @@ import org.springframework.web.client.RestTemplate;
|
||||||
import run.halo.app.cache.AbstractStringCacheStore;
|
import run.halo.app.cache.AbstractStringCacheStore;
|
||||||
import run.halo.app.cache.InMemoryCacheStore;
|
import run.halo.app.cache.InMemoryCacheStore;
|
||||||
import run.halo.app.cache.LevelCacheStore;
|
import run.halo.app.cache.LevelCacheStore;
|
||||||
|
import run.halo.app.config.attributeconverter.AttributeConverterAutoGenerateConfiguration;
|
||||||
import run.halo.app.config.properties.HaloProperties;
|
import run.halo.app.config.properties.HaloProperties;
|
||||||
import run.halo.app.repository.base.BaseRepositoryImpl;
|
import run.halo.app.repository.base.BaseRepositoryImpl;
|
||||||
import run.halo.app.utils.HttpClientUtils;
|
import run.halo.app.utils.HttpClientUtils;
|
||||||
|
@ -35,6 +37,7 @@ import run.halo.app.utils.HttpClientUtils;
|
||||||
@EnableConfigurationProperties(HaloProperties.class)
|
@EnableConfigurationProperties(HaloProperties.class)
|
||||||
@EnableJpaRepositories(basePackages = "run.halo.app.repository", repositoryBaseClass =
|
@EnableJpaRepositories(basePackages = "run.halo.app.repository", repositoryBaseClass =
|
||||||
BaseRepositoryImpl.class)
|
BaseRepositoryImpl.class)
|
||||||
|
@Import(AttributeConverterAutoGenerateConfiguration.class)
|
||||||
public class HaloConfiguration {
|
public class HaloConfiguration {
|
||||||
|
|
||||||
private final HaloProperties haloProperties;
|
private final HaloProperties haloProperties;
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package run.halo.app.config.attributeconverter;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jpa configuration.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public class AttributeConverterAutoGenerateConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
EntityManagerFactoryBuilderCustomizer entityManagerFactoryBuilderCustomizer(
|
||||||
|
ConfigurableListableBeanFactory factory) {
|
||||||
|
return builder -> builder.setPersistenceUnitPostProcessors(
|
||||||
|
new AutoGenerateConverterPersistenceUnitPostProcessor(factory));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package run.halo.app.config.attributeconverter;
|
||||||
|
|
||||||
|
import static net.bytebuddy.description.annotation.AnnotationDescription.Builder.ofType;
|
||||||
|
import static net.bytebuddy.description.type.TypeDescription.Generic.Builder.parameterizedType;
|
||||||
|
import static net.bytebuddy.implementation.FieldAccessor.ofField;
|
||||||
|
import static net.bytebuddy.implementation.MethodDelegation.to;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isDefaultConstructor;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.NamingStrategy;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||||
|
import net.bytebuddy.implementation.MethodCall;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute converter auto generator.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
class AttributeConverterAutoGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto generation suffix.
|
||||||
|
*/
|
||||||
|
public static final String AUTO_GENERATION_SUFFIX = "$AttributeConverterGeneratedByByteBuddy";
|
||||||
|
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
public AttributeConverterAutoGenerator(ClassLoader classLoader) {
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Class<?> generate(Class<T> clazz) {
|
||||||
|
try {
|
||||||
|
return new ByteBuddy()
|
||||||
|
.with(new NamingStrategy.AbstractBase() {
|
||||||
|
@Override
|
||||||
|
protected String name(TypeDescription superClass) {
|
||||||
|
return clazz.getName() + AUTO_GENERATION_SUFFIX;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.subclass(
|
||||||
|
parameterizedType(AttributeConverter.class, clazz, Integer.class).build())
|
||||||
|
.annotateType(ofType(Converter.class).define("autoApply", true).build())
|
||||||
|
.constructor(isDefaultConstructor())
|
||||||
|
.intercept(MethodCall.invoke(Object.class.getDeclaredConstructor())
|
||||||
|
.andThen(ofField("enumType").setsValue(clazz)))
|
||||||
|
.defineField("enumType", Class.class, Modifier.PRIVATE | Modifier.FINAL)
|
||||||
|
.method(named("convertToDatabaseColumn"))
|
||||||
|
.intercept(to(AttributeConverterInterceptor.class))
|
||||||
|
.method(named("convertToEntityAttribute"))
|
||||||
|
.intercept(to(AttributeConverterInterceptor.class))
|
||||||
|
.make()
|
||||||
|
.load(this.classLoader, ClassLoadingStrategy.Default.INJECTION.allowExistingTypes())
|
||||||
|
.getLoaded();
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
// should never happen
|
||||||
|
throw new RuntimeException("Failed to get declared constructor.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isGeneratedByByteBuddy(String className) {
|
||||||
|
return StringUtils.endsWith(className, AUTO_GENERATION_SUFFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package run.halo.app.config.attributeconverter;
|
||||||
|
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.FieldValue;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||||
|
import run.halo.app.model.enums.ValueEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute Converter Interceptor.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
public class AttributeConverterInterceptor {
|
||||||
|
|
||||||
|
private AttributeConverterInterceptor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RuntimeType
|
||||||
|
public static <T extends Enum<T> & ValueEnum<V>, V> V convertToDatabaseColumn(T attribute) {
|
||||||
|
return attribute == null ? null : attribute.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RuntimeType
|
||||||
|
public static <T extends Enum<T> & ValueEnum<V>, V> T convertToEntityAttribute(V dbData,
|
||||||
|
@FieldValue("enumType") Class<T> enumType) {
|
||||||
|
return dbData == null ? null : ValueEnum.valueToEnum(enumType, dbData);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package run.halo.app.config.attributeconverter;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toUnmodifiableSet;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||||
|
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||||
|
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
|
||||||
|
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import run.halo.app.model.enums.ValueEnum;
|
||||||
|
import run.halo.app.model.properties.PropertyEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute converter persistence unit post processor.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
class AutoGenerateConverterPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
|
||||||
|
|
||||||
|
private static final String PACKAGE_TO_SCAN = "run.halo.app";
|
||||||
|
|
||||||
|
private final ConfigurableListableBeanFactory factory;
|
||||||
|
|
||||||
|
public AutoGenerateConverterPersistenceUnitPostProcessor(
|
||||||
|
ConfigurableListableBeanFactory factory) {
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
|
||||||
|
var generator = new AttributeConverterAutoGenerator(factory.getBeanClassLoader());
|
||||||
|
|
||||||
|
findValueEnumClasses()
|
||||||
|
.stream()
|
||||||
|
.map(generator::generate)
|
||||||
|
.map(Class::getName)
|
||||||
|
.forEach(pui::addManagedClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Class<?>> findValueEnumClasses() {
|
||||||
|
var scanner = new ClassPathScanningCandidateComponentProvider(false);
|
||||||
|
// include ValueEnum class
|
||||||
|
scanner.addIncludeFilter(new AssignableTypeFilter(ValueEnum.class));
|
||||||
|
// exclude PropertyEnum class
|
||||||
|
scanner.addExcludeFilter(new AssignableTypeFilter(PropertyEnum.class));
|
||||||
|
|
||||||
|
return scanner.findCandidateComponents(PACKAGE_TO_SCAN)
|
||||||
|
.stream()
|
||||||
|
.filter(bd -> bd.getBeanClassName() != null)
|
||||||
|
.map(bd -> ClassUtils.resolveClassName(bd.getBeanClassName(), null))
|
||||||
|
.collect(toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ public class Journal extends BaseEntity {
|
||||||
private Long likes;
|
private Long likes;
|
||||||
|
|
||||||
@Column(name = "type")
|
@Column(name = "type")
|
||||||
@ColumnDefault("1")
|
@ColumnDefault("0")
|
||||||
private JournalType type;
|
private JournalType type;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,12 +10,12 @@ public enum JournalType implements ValueEnum<Integer> {
|
||||||
/**
|
/**
|
||||||
* Public type.
|
* Public type.
|
||||||
*/
|
*/
|
||||||
PUBLIC(1),
|
PUBLIC(0),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intimate type.
|
* Intimate type.
|
||||||
*/
|
*/
|
||||||
INTIMATE(0);
|
INTIMATE(1);
|
||||||
|
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
package run.halo.app.model.enums;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Post type.
|
|
||||||
*
|
|
||||||
* @author johnniang
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public enum PostType implements ValueEnum<Integer> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 普通文章
|
|
||||||
*/
|
|
||||||
POST(0),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义页面
|
|
||||||
*/
|
|
||||||
PAGE(1),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 日志
|
|
||||||
*/
|
|
||||||
JOURNAL(2);
|
|
||||||
|
|
||||||
private final Integer value;
|
|
||||||
|
|
||||||
PostType(Integer value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,6 +11,13 @@ import org.springframework.util.Assert;
|
||||||
*/
|
*/
|
||||||
public interface ValueEnum<T> {
|
public interface ValueEnum<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets enum value.
|
||||||
|
*
|
||||||
|
* @return enum value
|
||||||
|
*/
|
||||||
|
T getValue();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts value to corresponding enum.
|
* Converts value to corresponding enum.
|
||||||
*
|
*
|
||||||
|
@ -20,7 +27,7 @@ public interface ValueEnum<T> {
|
||||||
* @param <E> enum generic
|
* @param <E> enum generic
|
||||||
* @return corresponding enum
|
* @return corresponding enum
|
||||||
*/
|
*/
|
||||||
static <V, E extends ValueEnum<V>> E valueToEnum(Class<E> enumType, V value) {
|
static <V, E extends Enum<E> & ValueEnum<V>> E valueToEnum(Class<E> enumType, V value) {
|
||||||
Assert.notNull(enumType, "enum type must not be null");
|
Assert.notNull(enumType, "enum type must not be null");
|
||||||
Assert.notNull(value, "value must not be null");
|
Assert.notNull(value, "value must not be null");
|
||||||
Assert.isTrue(enumType.isEnum(), "type must be an enum type");
|
Assert.isTrue(enumType.isEnum(), "type must be an enum type");
|
||||||
|
@ -30,12 +37,4 @@ public interface ValueEnum<T> {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new IllegalArgumentException("unknown database value: " + value));
|
.orElseThrow(() -> new IllegalArgumentException("unknown database value: " + value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets enum value.
|
|
||||||
*
|
|
||||||
* @return enum value
|
|
||||||
*/
|
|
||||||
T getValue();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
package run.halo.app.model.enums.converter;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Objects;
|
|
||||||
import javax.persistence.AttributeConverter;
|
|
||||||
import run.halo.app.model.enums.ValueEnum;
|
|
||||||
import run.halo.app.utils.ReflectionUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract converter.
|
|
||||||
*
|
|
||||||
* @param <E> enum generic
|
|
||||||
* @param <V> value generic
|
|
||||||
* @author johnniang
|
|
||||||
* @date 12/6/18
|
|
||||||
*/
|
|
||||||
public abstract class AbstractConverter<E extends ValueEnum<V>, V>
|
|
||||||
implements AttributeConverter<E, V> {
|
|
||||||
|
|
||||||
private final Class<E> clazz;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected AbstractConverter() {
|
|
||||||
Type enumType = Objects.requireNonNull(
|
|
||||||
ReflectionUtils
|
|
||||||
.getParameterizedTypeBySuperClass(AbstractConverter.class, this.getClass())
|
|
||||||
).getActualTypeArguments()[0];
|
|
||||||
this.clazz = (Class<E>) enumType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V convertToDatabaseColumn(E attribute) {
|
|
||||||
return attribute == null ? null : attribute.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public E convertToEntityAttribute(V dbData) {
|
|
||||||
return dbData == null ? null : ValueEnum.valueToEnum(clazz, dbData);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package run.halo.app.model.enums.converter;
|
|
||||||
|
|
||||||
import javax.persistence.Converter;
|
|
||||||
import run.halo.app.model.enums.AttachmentType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attachment type converter
|
|
||||||
*
|
|
||||||
* @author johnniang
|
|
||||||
* @date 3/27/19
|
|
||||||
*/
|
|
||||||
@Converter(autoApply = true)
|
|
||||||
public class AttachmentTypeConverter extends AbstractConverter<AttachmentType, Integer> {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package run.halo.app.model.enums.converter;
|
|
||||||
|
|
||||||
import javax.persistence.Converter;
|
|
||||||
import run.halo.app.model.enums.CommentStatus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PostComment status converter.
|
|
||||||
*
|
|
||||||
* @author johnniang
|
|
||||||
* @date 3/27/19
|
|
||||||
*/
|
|
||||||
@Converter(autoApply = true)
|
|
||||||
public class CommentStatusConverter extends AbstractConverter<CommentStatus, Integer> {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package run.halo.app.model.enums.converter;
|
|
||||||
|
|
||||||
import javax.persistence.Converter;
|
|
||||||
import run.halo.app.model.enums.DataType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data type converter.
|
|
||||||
*
|
|
||||||
* @author johnniang
|
|
||||||
* @date 4/10/19
|
|
||||||
*/
|
|
||||||
@Converter(autoApply = true)
|
|
||||||
public class DataTypeConverter extends AbstractConverter<DataType, Integer> {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package run.halo.app.model.enums.converter;
|
|
||||||
|
|
||||||
import javax.persistence.Converter;
|
|
||||||
import run.halo.app.model.enums.LogType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log type converter.
|
|
||||||
*
|
|
||||||
* @author johnniang
|
|
||||||
* @date 3/27/19
|
|
||||||
*/
|
|
||||||
@Converter(autoApply = true)
|
|
||||||
public class LogTypeConverter extends AbstractConverter<LogType, Integer> {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package run.halo.app.model.enums.converter;
|
|
||||||
|
|
||||||
import javax.persistence.Converter;
|
|
||||||
import run.halo.app.model.enums.PostStatus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PostStatus converter.
|
|
||||||
*
|
|
||||||
* @author johnniang
|
|
||||||
* @date 3/27/19
|
|
||||||
*/
|
|
||||||
@Converter(autoApply = true)
|
|
||||||
public class PostStatusConverter extends AbstractConverter<PostStatus, Integer> {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package run.halo.app.model.enums.converter;
|
|
||||||
|
|
||||||
import javax.persistence.Converter;
|
|
||||||
import run.halo.app.model.enums.PostType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PostType converter.
|
|
||||||
*
|
|
||||||
* @author johnniang
|
|
||||||
* @date 3/27/19
|
|
||||||
*/
|
|
||||||
@Converter(autoApply = true)
|
|
||||||
@Deprecated
|
|
||||||
public class PostTypeConverter extends AbstractConverter<PostType, Integer> {
|
|
||||||
|
|
||||||
}
|
|
|
@ -281,7 +281,8 @@ public interface OptionService extends CrudService<Option, Integer> {
|
||||||
* @return an optional value enum value
|
* @return an optional value enum value
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
<V, E extends ValueEnum<V>> Optional<E> getValueEnumByProperty(@NonNull PropertyEnum property,
|
<V, E extends Enum<E> & ValueEnum<V>> Optional<E> getValueEnumByProperty(
|
||||||
|
@NonNull PropertyEnum property,
|
||||||
@NonNull Class<V> valueType, @NonNull Class<E> enumType);
|
@NonNull Class<V> valueType, @NonNull Class<E> enumType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -296,7 +297,8 @@ public interface OptionService extends CrudService<Option, Integer> {
|
||||||
* @return value enum value or null if the default value is null
|
* @return value enum value or null if the default value is null
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
<V, E extends ValueEnum<V>> E getValueEnumByPropertyOrDefault(@NonNull PropertyEnum property,
|
<V, E extends Enum<E> & ValueEnum<V>> E getValueEnumByPropertyOrDefault(
|
||||||
|
@NonNull PropertyEnum property,
|
||||||
@NonNull Class<V> valueType, @NonNull Class<E> enumType, @Nullable E defaultValue);
|
@NonNull Class<V> valueType, @NonNull Class<E> enumType, @Nullable E defaultValue);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import run.halo.app.cache.AbstractStringCacheStore;
|
import run.halo.app.cache.AbstractStringCacheStore;
|
||||||
import run.halo.app.config.properties.HaloProperties;
|
|
||||||
import run.halo.app.event.options.OptionUpdatedEvent;
|
import run.halo.app.event.options.OptionUpdatedEvent;
|
||||||
import run.halo.app.exception.MissingPropertyException;
|
import run.halo.app.exception.MissingPropertyException;
|
||||||
import run.halo.app.model.dto.OptionDTO;
|
import run.halo.app.model.dto.OptionDTO;
|
||||||
|
@ -379,14 +378,16 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V, E extends ValueEnum<V>> Optional<E> getValueEnumByProperty(PropertyEnum property,
|
public <V, E extends Enum<E> & ValueEnum<V>> Optional<E> getValueEnumByProperty(
|
||||||
|
PropertyEnum property,
|
||||||
Class<V> valueType, Class<E> enumType) {
|
Class<V> valueType, Class<E> enumType) {
|
||||||
return getByProperty(property).map(value -> ValueEnum
|
return getByProperty(property).map(value -> ValueEnum
|
||||||
.valueToEnum(enumType, PropertyEnum.convertTo(value.toString(), valueType)));
|
.valueToEnum(enumType, PropertyEnum.convertTo(value.toString(), valueType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V, E extends ValueEnum<V>> E getValueEnumByPropertyOrDefault(PropertyEnum property,
|
public <V, E extends Enum<E> & ValueEnum<V>> E getValueEnumByPropertyOrDefault(
|
||||||
|
PropertyEnum property,
|
||||||
Class<V> valueType, Class<E> enumType, E defaultValue) {
|
Class<V> valueType, Class<E> enumType, E defaultValue) {
|
||||||
return getValueEnumByProperty(property, valueType, enumType).orElse(defaultValue);
|
return getValueEnumByProperty(property, valueType, enumType).orElse(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package run.halo.app.attributeconverter;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import run.halo.app.config.attributeconverter.AttributeConverterAutoGenerateConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute converter apply result test.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
@DataJpaTest
|
||||||
|
@Import(AttributeConverterAutoGenerateConfiguration.class)
|
||||||
|
class AttributeConverterApplyTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
EntityManager entityManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
CityRepository cityRepository;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAutoAppliedForAttributeConverter() {
|
||||||
|
final var city = new City();
|
||||||
|
city.setName("ChengDu");
|
||||||
|
city.setLevel(CityLevel.CITY);
|
||||||
|
cityRepository.save(city);
|
||||||
|
|
||||||
|
final var cityId = city.getId();
|
||||||
|
|
||||||
|
Query nativeQuery =
|
||||||
|
entityManager.createNativeQuery("select level from city where id = " + cityId);
|
||||||
|
Object level = nativeQuery.getSingleResult();
|
||||||
|
Assertions.assertEquals(CityLevel.CITY.getValue(), level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
static class Application {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package run.halo.app.attributeconverter;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* City entity.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
class City {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private CityLevel level;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package run.halo.app.attributeconverter;
|
||||||
|
|
||||||
|
import run.halo.app.model.enums.ValueEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* City level.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
enum CityLevel implements ValueEnum<Integer> {
|
||||||
|
|
||||||
|
PROVINCE(123),
|
||||||
|
|
||||||
|
CITY(456),
|
||||||
|
|
||||||
|
DISTRICT(789);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
CityLevel(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package run.halo.app.attributeconverter;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* City operation repository.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
*/
|
||||||
|
interface CityRepository extends JpaRepository<City, Integer> {
|
||||||
|
}
|
Loading…
Reference in New Issue