当前位置:网站首页>An integrated framework for caching, loading, refreshing and mapping data dictionaries

An integrated framework for caching, loading, refreshing and mapping data dictionaries

2022-06-09 07:40:00 Don't ask me what I dislike

Preface

In the process of business development , I always deal with dictionaries . for instance : Gender , type , Status, etc , Can be summed up as the scope of the dictionary .

The composition of a dictionary is divided into : Dictionary type Dictionary data .

among Dictionary data Belonging to a class Dictionary type . Can pass Dictionary type obtain Dictionary data . As mentioned at the beginning , Gender is the dictionary type ,1- male ;0- Woman ; Just for dictionary data . among “1” and “0” Is the code value ,“ male ” and “ Woman ” Is the display value . Store code values in the database , Display values on the page .

The purpose of the dictionary :

  1. Front end drop-down box
  2. List display , You need to convert code values to display values
  3. Logical condition judgment ( Both front and rear ends need )
  4. Data range limits

ad locum , For the time being, I will classify the sources of dictionaries into three categories : enumeration Dictionary table Business data .

  • enumeration : Use enumeration , It is convenient to make logical judgment and data range limitation in the code .
  • Dictionary table : Use one or more dictionary tables , Unified management of dictionary data in the project .( May repeat with enum )
  • Business data : In some cases , We need to use business data as a dictionary .( data source , Data sheets, etc )

Text

According to the purpose in the preface , The dictionary framework requires the following functions :

  1. Cache dictionary types and dictionary data
  2. Easy to load real-time business dictionary data
  3. According to the dictionary type and dictionary data code value , Mapping to display values

So the dictionary framework is also designed according to the above three points :

  1. cache ( Application startup cache )
  2. load ( The application starts loading )
  3. Reload ( Dynamic dictionary changes , Modify cache data )
  4. serialize ( mapping )

Engineering structure drawing

image.png

One 、 Load and cache

To find a dictionary quickly , Or used for mapping . The easiest way , Cache data . Cache data into memory , Or in other middleware , Improve efficiency .

Design

image.png

The external operations of cached data are uniformly placed in DictDataCache Class .

In my design , Because there are three sources of data in the dictionary , So the cache is also placed in three different locations . It uses Hutool Caching tools in tools , And I have implemented a custom cache class NoClearCache

Custom persistent cache NoClearCache

package com.cah.project.core.dict.cache;
import cn.hutool.cache.impl.AbstractCache;
import java.util.HashMap;
/** *  Function description :  No clean cache  <br/> */
public class NoClearCache<K, V> extends AbstractCache<K, V> {
    private static final long serialVersionUID = 1L;
    public NoClearCache() {
        cacheMap = new HashMap<>();
    }
    @Override
    protected int pruneCache() {
        return 0;
    }
}
 Copy code 

By three handler Deposit . And use Enumerate factories To manage DictLoadEnum. If you are interested, you can check your own “ Strategy enumeration ” special column .

Dictionary load enumeration DictLoadEnum

package com.cah.project.core.dict.enums;
import cn.hutool.extra.spring.SpringUtil;
import com.cah.project.core.dict.annotation.DictType;
import com.cah.project.core.dict.handler.BusinessDictHandler;
import com.cah.project.core.dict.handler.DictDataHandler;
import com.cah.project.core.dict.handler.DictEnumHandler;
import com.cah.project.core.dict.handler.IDictHandler;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.stream.Stream;
/** *  Function description :  Dictionary load enumeration  <br/> */
@Getter
@AllArgsConstructor
@DictType(typeCode= "LOAD_TYPE", typeName = " Dictionary load type ")
public enum DictLoadEnum implements IDictEnum {
    /**  enumeration ( Permanent cache ) */
    ENUM("1", " enumeration ", DictEnumHandler.class),
    /**  Dictionary table ( fifo )[ If it conflicts with enumeration , The enumeration will be overwritten ] */
    DICT_TABLE("2", " Dictionary table ", DictDataHandler.class),
    /**  Business data ( Frequency of use ) */
    BUSINESS_DATA("3", " Business data ", BusinessDictHandler.class),
    ;
    private final String type;
    private final String msg;
    private final Class<? extends IDictHandler> handler;

    public static DictLoadEnum indexOf(String type) {
        return Stream.of(values()).filter(d -> d.getType().equals(type)).findFirst().orElse(ENUM);
    }

    public IDictHandler getDictHandler() {
        return SpringUtil.getBean(getHandler());
    }

    @Override
    public String getCode() {
        return getType();
    }

    /** *  Function description :  Start loading  <br/> */
    public static void start() {
        for(DictLoadEnum dle : DictLoadEnum.values()) {
            dle.getDictHandler().loadDictCache();
        }
    }

}
 Copy code 

Three handler A unified interface is implemented IDictHandler, And inherited the abstract class AbstractDictHandler, stay AbstractDictHandler in , Defines the cache properties .

protected Cache<String, List<DictDataOptions>> cacheData;
 Copy code 

Every implementation , Create different cache objects by yourself , Different caching strategies can be implemented . Such as :

  • Enumeration cache , Itself is code , So you can set the permanent cache NoClearCache, No size ;
  • Dictionary table cache , have access to “ FIFO cache ” FIFOCache, And set the size to 10000 individual ;
  • Business data caching , have access to “ At least use caching ” LFUCache, And set the size to 10000 individual ;

Interface IDictHandler

loadDictCache() Method to load the cache .

reloadDictCache() Reload cache . Must cooperate with Event Events use

getDictDataOptions() Get dictionary data .

package com.cah.project.core.dict.handler;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.event.DictEvent;
import org.springframework.context.ApplicationListener;
import java.util.List;
/*** *  Function description :  Dictionary loading interface  <br/> */
public interface IDictHandler extends ApplicationListener<DictEvent> {
    /** *  Function description :  Load cache  <br/> */
    void loadDictCache();
    /** *  Function description :  Reload cache  <br/> */
    void reloadDictCache(String typeCode);
    /** *  Function description :  By dictionary type , Get dictionary data  <br/> * * @param typeCode  Dictionary type  * @return "java.util.List<com.cah.project.module.standard.domain.vo.out.DictDataOptions>" */
    List<DictDataOptions> getDictDataOptions(String typeCode);
}
 Copy code 

abstract class AbstractDictHandler

package com.cah.project.core.dict.handler;
import cn.hutool.cache.Cache;
import cn.hutool.core.collection.ListUtil;
import com.cah.project.core.dict.cache.DictDataCache;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.event.DictEvent;
import java.util.List;
/** *  Function description :  Dictionary processing , abstract class  <br/> */
public abstract class AbstractDictHandler implements IDictHandler {
    /*** *  Function description :  Cache object  <br/> */
    protected Cache<String, List<DictDataOptions>> cacheData;
    public AbstractDictHandler() {}
    @Override
    public void onApplicationEvent(DictEvent event) {
        reloadDictCache(event.getTypeCode());
    }
    /** *  Function description :  Add dictionary type  <br/> * * @param typeCode  Dictionary type  * @param typeName  Dictionary type name  */
    protected void addDictType(String typeCode, String typeName) {
        DictDataCache.addType(typeCode, typeName);
    }
    /*** *  Function description :  Batch add dictionary data cache  <br/> * * @param typeCode  Dictionary type  * @param dataList  Dictionary data list  */
    protected void put(String typeCode, List<DictDataOptions> dataList) {
        if(cacheData.get(typeCode) == null) {
            cacheData.put(typeCode, ListUtil.list(false));
        }
        cacheData.get(typeCode).addAll(dataList);
    }
    /** *  Function description :  Add dictionary data cache for a single entry  <br/> * * @param typeCode  Dictionary type  * @param dataValue  The dictionary is worth  * @param dataLabel  Dictionary labels  */
    protected void put(String typeCode, String dataValue, String dataLabel) {
        if(cacheData.get(typeCode) == null) {
            cacheData.put(typeCode, ListUtil.list(false));
        }
        cacheData.get(typeCode).add(DictDataOptions.builder().typeCode(typeCode).dataValue(dataValue).dataLabel(dataLabel).build());
    }
    /** *  Function description :  Remove the cache  <br/> * * @param typeCode  Dictionary type  */
    protected void remove(String typeCode) {
        cacheData.remove(typeCode);
    }
}
 Copy code 

1、 enumeration

In the code , How to automatically and easily add enumerations to the cache ? I defined an annotation DictType , There's also an interface IDictEnum. Through these two classes , You can use reflection , Add the enumeration class to the enumeration cache . For specific use, please refer to the above DictLoadEnum class .

Enumerate dictionary annotations DictType

package com.cah.project.core.dict.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** *  Function description :  Dictionary type annotation  <br/> */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DictType {
    /**  Dictionary type  */
    String typeCode();
    /**  Dictionary name  */
    String typeName();
}
 Copy code 

Enumeration Dictionary interface IDictEnum

package com.cah.project.core.dict.enums;
/** *  Function description :  Dictionary enumeration interface  <br/> */
public interface IDictEnum {
    /** *  Function description :  Code value  <br/> */
    String getCode();
    /** *  Function description :  Display value  <br/> */
    String getMsg();
}
 Copy code 

The implementation is as follows : Scan through packages and interfaces , Cycle to judge whether it meets the requirements . In the pass condition , get data , Add to cache .

Enumerate dictionary handlers DictEnumHandler

package com.cah.project.core.dict.handler;
import cn.hutool.core.util.ClassUtil;
import com.cah.project.core.dict.annotation.DictType;
import com.cah.project.core.dict.cache.NoClearCache;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.enums.IDictEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
@Slf4j
@Component
public class DictEnumHandler extends AbstractDictHandler {
    //  By configuring , Implement package scanning 
    @Value("${dict.enum.package:com.cah.project}")
    private String packageStr;

    public DictEnumHandler() {
        cacheData = new NoClearCache<>();
    }
    @Override
    public List<DictDataOptions> getDictDataOptions(String typeCode) {
        return cacheData.get(typeCode);
    }
    @Override
    public void loadDictCache() {
        try {
            //  If there is no data , To register 
            Set<Class<?>> classes = ClassUtil.scanPackageBySuper(packageStr, IDictEnum.class);
            for (Class<?> aClass : classes) {
                if (ClassUtil.isEnum(aClass)) {
                    DictType dictType = aClass.getAnnotation(DictType.class);
                    if (dictType == null) {
                        throw new RuntimeException(" enumeration :" + aClass.getName() + "  Does not add @DictType annotation .");
                    }
                    Method codeMethod = ClassUtil.getDeclaredMethod(aClass, "getCode");
                    Method msgMethod = ClassUtil.getDeclaredMethod(aClass, "getMsg");
                    if (codeMethod == null || msgMethod == null) {
                        continue;
                    }
                    // obtain enum All instances of 
                    Object[] objList = aClass.getEnumConstants();
                    for (Object obj : objList) {
                        put(dictType.typeCode(), (String) codeMethod.invoke(obj), (String) msgMethod.invoke(obj));
                    }
                    //  Add dictionary type 
                    addDictType(dictType.typeCode(), dictType.typeName());
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            log.error(" Enumerate cache load exceptions :{}", e.getMessage());
        }
    }
    @Override
    public void reloadDictCache(String typeCode) {
        //  Due to the memory , No need to reload 
    }
}
 Copy code 

2、 Dictionary table

The dictionary table needs to be distributed for loading , So the dictionary table processing interface is redefined IDictDataHandler, And use abstract classes AbstractDictDataHandler The general method , And the default implementation class DefaultDictDataHandler. This ensures the integrity of the framework , Not strongly dependent on the business .

Dictionary table processor DictDataHandler

package com.cah.project.core.dict.handler;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import com.cah.project.core.dict.cache.DictDataCache;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.handler.dictData.IDictDataHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/** *  Function description :  Dictionary table loading  <br/> */
@Component
public class DictDataHandler extends AbstractDictHandler {
    @Autowired
    private DictHandlerAware dictHandlerAware;

    public DictDataHandler() {
        cacheData = CacheUtil.newFIFOCache(10000);
    }
    @Override
    public void loadDictCache() {
        //  Load Dictionary Cache 
        getDictDataHandler().loadDictCache(DictDataCache.getTypeList(), cacheData);
    }
    @Override
    public void reloadDictCache(String typeCode) {
        remove(typeCode);
        List<DictDataOptions> dictDataList = getDictDataHandler().getDictDataOptions(typeCode);
        if(CollUtil.isNotEmpty(dictDataList)) {
            put(typeCode, dictDataList);
        }
    }
    @Override
    public List<DictDataOptions> getDictDataOptions(String typeCode) {
        List<DictDataOptions> dictDataOptions = cacheData.get(typeCode);
        //  If it doesn't exist in the cache , It may be invalid , Query again from the database 
        if(CollUtil.isEmpty(dictDataOptions)) {
            List<DictDataOptions> dictDataList = getDictDataHandler().getDictDataOptions(typeCode);
            if(CollUtil.isEmpty(dictDataList)) {
                put(typeCode, dictDataList);
            } else {
                return ListUtil.empty();
            }
        }
        return cacheData.get(typeCode);
    }
    
    private IDictDataHandler getDictDataHandler() {
        return dictHandlerAware.getDictHandler();
    }
}
 Copy code 

Dictionary table interface IDictDataHandler

package com.cah.project.core.dict.handler.dictData;
import cn.hutool.cache.Cache;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.domain.vo.DictTypeOptions;
import java.util.Collection;
import java.util.List;
import java.util.Set;

public interface IDictDataHandler {

    /** *  Function description :  Get all dictionary types  <br/> * * @return "java.util.List<com.cah.project.module.standard.domain.entity.DictTypeEntity>" */
    Set<DictTypeOptions> getDictTypeList();

    /** *  Function description :  By dictionary type , Get dictionary data  <br/> * * @param typeCode  Dictionary type  * @return "java.util.List<com.cah.project.module.standard.domain.vo.out.DictDataOptions>" */
    List<DictDataOptions> getDictDataOptions(String typeCode);

    /** *  Function description :  By batch dictionary type , Query dictionary data  <br/> * * @param typeCodeList  Dictionary type collection  * @return "java.util.List<com.cah.project.module.standard.domain.vo.out.DictDataOptions>" */
    List<DictDataOptions> getDictDataOptions(Collection<String> typeCodeList);

    /** *  Function description :  Load cache  <br/> * * @param dictTypeList  List of dictionary types  * @param cacheData  Dictionary cache data  */
    void loadDictCache(Collection<DictTypeOptions> dictTypeList, Cache<String, List<DictDataOptions>> cacheData);

}
 Copy code 

Dictionary table Abstract implementation AbstractDictDataHandler

package com.cah.project.core.dict.handler.dictData;
import cn.hutool.cache.Cache;
import cn.hutool.core.collection.ListUtil;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.domain.vo.DictTypeOptions;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/** *  Function description :  Dictionary table loading  <br/> */
public abstract class AbstractDictDataHandler implements IDictDataHandler {
    /**  Define page size per page  */
    protected static final int PAGE_SIZE = 50;
    @Override
    public void loadDictCache(Collection<DictTypeOptions> dictTypeList, Cache<String, List<DictDataOptions>> cacheData) {
        Set<DictTypeOptions> dictTypeOptionsSet = getDictTypeList();
        dictTypeList.addAll(dictTypeOptionsSet);
        List<String> typeCodeList = dictTypeOptionsSet.stream().map(DictTypeOptions::getTypeCode).collect(Collectors.toList());
        int total = typeCodeList.size() / PAGE_SIZE + 1;
        //  Page loading dictionary data 
        for(int i = 0; i < total; i++) {
            List<DictDataOptions> dictDataList = getDictDataOptions(ListUtil.page(i, PAGE_SIZE, typeCodeList));
            for(DictDataOptions ddp : dictDataList) {
                if(cacheData.get(ddp.getTypeCode()) == null) {
                    cacheData.put(ddp.getTypeCode(), ListUtil.list(false));
                }
                cacheData.get(ddp.getTypeCode()).add(ddp);
            }
        }
    }
}
 Copy code 

The dictionary table is implemented by default DefaultDictDataHandler

package com.cah.project.core.dict.handler.dictData;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.domain.vo.DictTypeOptions;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/** *  Function description :  Default implementation  <br/> */
public class DefaultDictDataHandler extends AbstractDictDataHandler {
    public DefaultDictDataHandler() {
        super();
    }
    @Override
    public Set<DictTypeOptions> getDictTypeList() {
        return Collections.emptySet();
    }
    @Override
    public List<DictDataOptions> getDictDataOptions(String typeCode) {
        return Collections.emptyList();
    }
    @Override
    public List<DictDataOptions> getDictDataOptions(Collection<String> typeCodeList) {
        return Collections.emptyList();
    }
}
 Copy code 

Specific business implementation StandardAbstractDictDataHandler

The implementation is placed in a specific business service , Here is an example to show .

package com.cah.project.module.standard.service.handler;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.domain.vo.DictTypeOptions;
import com.cah.project.core.dict.handler.dictData.AbstractDictDataHandler;
import com.cah.project.module.standard.service.IDictDataService;
import com.cah.project.module.standard.service.IDictTypeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Component
public class StandardAbstractDictDataHandler extends AbstractDictDataHandler {
    @Autowired
    private IDictTypeService dictTypeService;
    @Autowired
    private IDictDataService dictDataService;
    @Override
    public Set<DictTypeOptions> getDictTypeList() {
        return dictTypeService.selectAll().stream()
                .map(e -> DictTypeOptions.builder().typeCode(e.getTypeCode()).typeName(e.getTypeName()).build())
                .collect(Collectors.toSet());
    }
    @Override
    public List<DictDataOptions> getDictDataOptions(String typeCode) {
        return dictDataService.selectListByCode(typeCode).stream()
                .map(e -> DictDataOptions.builder().typeCode(e.getTypeCode()).dataLabel(e.getDataLabel()).dataValue(e.getDataValue()).build())
                .collect(Collectors.toList());
    }
    @Override
    public List<DictDataOptions> getDictDataOptions(Collection<String> typeCodeList) {
        return dictDataService.selectListByCodes(typeCodeList).stream()
                .map(e -> DictDataOptions.builder().typeCode(e.getTypeCode()).dataLabel(e.getDataLabel()).dataValue(e.getDataValue()).build())
                .collect(Collectors.toList());
    }
}
 Copy code 

3、 Business data

Dictionary of business data types , Similar to a dictionary table . But it's not exactly the same . Basically a business table , Is a dictionary type and dictionary data . So the design method is different from the dictionary table .

Business Dictionary processing interface IBusinessDictHandler

typeCode() Set the business dictionary type

typeName() Set the business dictionary type description ( It's better to have )

getDictDataList() Get business dictionary data

package com.cah.project.core.dict.handler.business;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import java.util.List;
/** *  Function description :  Business Dictionary processing interface  <br/> */
public interface IBusinessDictHandler {
    /** *  Function description :  Get dictionary type  <br/> */
    String typeCode();
    /** *  Function description :  Dictionary description  <br/> */
    String typeName();
    /** *  Function description :  Get dictionary data  <br/> */
    List<DictDataOptions> getDictDataList();
}
 Copy code 

Business Dictionary processing abstract implementation AbstractBusinessDictHandler

There's nothing to write about , Just let it go

package com.cah.project.core.dict.handler.business;
/** *  Function description :  Business Dictionary processing , Abstract implementation  <br/> */
public abstract class AbstractBusinessDictHandler implements IBusinessDictHandler {
}
 Copy code 

Specific business implementation DataSourceDictHandler

package com.cah.project.module.meta.service.handler;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.handler.business.AbstractBusinessDictHandler;
import com.cah.project.module.meta.domain.entity.DataSourceEntity;
import com.cah.project.module.meta.service.IDataSourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/** *  Function description :  Data source dictionary processing  <br/> */
@Component
public class DataSourceDictHandler extends AbstractBusinessDictHandler {
    @Autowired
    private IDataSourceService dataSourceService;
    @Override
    public String typeCode() {
        return "DATA_SOURCE";
    }
    @Override
    public String typeName() {
        return " data source ";
    }
    @Override
    public List<DictDataOptions> getDictDataList() {
        List<DataSourceEntity> dataSourceList = dataSourceService.selectAll();
        return dataSourceList.stream()
                .map(e -> DictDataOptions.builder().typeCode(typeCode()).dataLabel(e.getName()).dataValue(e.getId()).build())
                .collect(Collectors.toList());
    }
}
 Copy code 

Two 、 Scan and start

1、 Implement class scanning

The dictionary table is also a business data dictionary , Generally, they belong to different business systems . So in this project , Only a default null implementation . As long as the concrete implementation , You can replace the default implementation . The function consists of DictHandlerAware Realization ApplicationContextAware Interface to achieve this .

scanning DictHandlerAware

package com.cah.project.core.dict.handler;
import cn.hutool.core.map.MapUtil;
import com.cah.project.core.dict.handler.business.IBusinessDictHandler;
import com.cah.project.core.dict.handler.dictData.AbstractDictDataHandler;
import com.cah.project.core.dict.handler.dictData.DefaultDictDataHandler;
import com.cah.project.core.dict.handler.dictData.IDictDataHandler;
import lombok.Getter;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/** *  Function description :  adopt spring, Register the business dictionary implementation class to map in , Easy to use  <br/> */
@Getter
@Component
public class DictHandlerAware implements ApplicationContextAware {
    //  The business data dictionary implements a collection (key:  Business Dictionary type ,value: Business Dictionary implementation )
    private final Map<String, IBusinessDictHandler> handlerMap = new HashMap<>();
    //  Set the default implementation 
    private IDictDataHandler dictHandler = new DefaultDictDataHandler();
    /** *  Function description :  Get the application context and get the corresponding interface implementation class  <br/> * * @param applicationContext  Context  */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // Return all corresponding data according to the interface type bean
        Map<String, IBusinessDictHandler> map = applicationContext.getBeansOfType(IBusinessDictHandler.class);
        Set<Map.Entry<String, IBusinessDictHandler>> entries = map.entrySet();
        for(Map.Entry<String, IBusinessDictHandler> entry : entries) {
            this.handlerMap.put(entry.getValue().typeCode(), entry.getValue());
        }
        //  According to the abstract class , Return to the dictionary implementation 
        Map<String, AbstractDictDataHandler> dictMap = applicationContext.getBeansOfType(AbstractDictDataHandler.class);
        if(MapUtil.isNotEmpty(dictMap)) {
            dictHandler = dictMap.entrySet().iterator().next().getValue();
        }
    }
}
 Copy code 

2、 Start loading

By implementing ApplicationRunner Interface , Implement the cache function of loading dictionary .

Start class DictStartRunner

package com.cah.project.core.dict;
import com.cah.project.core.dict.enums.DictLoadEnum;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/** *  Function description :  The dictionary starts loading  <br/> */
@Component
@Order(value = 2)
public class DictStartRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        try {
            DictLoadEnum.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 Copy code 

3、 ... and 、 Refresh

As long as the data in the database , There are changes ( newly added 、 modify 、 Delete ). At this time , You need to modify the corresponding cache .

It's used here ApplicationEvent To process . By customizing DictEvent Pass on Dictionary type typeCode. Then from IDictHandler Inherit ApplicationListener<DictEvent> Listening interface . Abstract processor AbstractDictHandler Default implementation onApplicationEvent(DictEvent event) Method . And redefine abstract methods abstract void reloadDictCache(String typeCode) The logic is written by each concrete implementation class .

Custom events DictEvent

package com.cah.project.core.dict.event;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

/** *  Function description :  Dictionary reload event  <br/> */
@Getter
public class DictEvent extends ApplicationEvent {

    private final String typeCode;

    /** * Create a new {@code ApplicationEvent}. * * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */
    public DictEvent(Object source, String typeCode) {
        super(source);
        this.typeCode = typeCode;
    }
}
 Copy code 

Send event class DictReloadPublishEvent

package com.cah.project.core.dict.event;

import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/** *  Function description :  Dictionary reload event operation  <br/> */
@Component
public class DictReloadPublishEvent {

    @Resource
    private ApplicationContext applicationContext;

    public void publishEvent(String typeCode) {
        DictEvent de = new DictEvent(this, typeCode);
        applicationContext.publishEvent(de);
    }

}
 Copy code 

Refresh events using

@Autowired
private DictReloadPublishEvent dictReloadPublishEvent;

public void test() {
    dictReloadPublishEvent.publishEvent("SEX_TYPE");
}

 Copy code 

Four 、 mapping ( serialize )

Please refer to [Jackson Serialize dictionary field properties ] and [Jackson Serialize dictionary field properties ( upgrade )] The content of . One of the classes has been adjusted . Moved to the project . Not affecting use .

5、 ... and 、 test

1、 Dictionaries Controller

package com.cah.project.module.standard.controller;

import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.dict.domain.vo.DictTypeOptions;
import com.cah.project.core.domain.out.CommonResult;
import com.cah.project.module.standard.service.IDictDataService;
import com.cah.project.module.standard.service.IDictTypeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;
import java.util.List;

/** *  Function description :  Dictionaries  <br/> */
@RestController
@RequestMapping("/dict")
@Api(tags = {" Dictionaries "})
public class DictController {

    @Autowired
    private IDictTypeService dictTypeService;

    @Autowired
    private IDictDataService dictDataService;

    @GetMapping("type/options")
    @ApiOperation(value = " Get the dictionary type drop-down options ")
    public CommonResult<Collection<DictTypeOptions>> selectOptions() {
        return CommonResult.data(dictTypeService.selectOptions());
    }

    @GetMapping("data/options/{typeCode}")
    @ApiOperation(value = " Get dictionary data drop-down options ")
    public CommonResult<List<DictDataOptions>> selectDataOptions(@PathVariable String typeCode) {
        return CommonResult.data(dictDataService.selectDataOptionsByCode(typeCode));
    }

    @GetMapping("data/options/{loadType}/{typeCode}")
    @ApiOperation(value = " Get dictionary data drop-down options , According to the loading parameters , Look for different data ")
    public CommonResult<List<DictDataOptions>> selectDataOptionsByLoadType(@PathVariable String loadType, @PathVariable String typeCode) {
        return CommonResult.data(dictDataService.selectDataOptionsByLoadType(loadType, typeCode));
    }

}
 Copy code 

2、 Dictionaries Service

package com.cah.project.module.standard.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cah.project.core.enums.YesOrNoEnum;
import com.cah.project.module.standard.domain.entity.DictTypeEntity;
import com.cah.project.core.dict.domain.vo.DictTypeOptions;
import com.cah.project.module.standard.mapper.DictTypeMapper;
import com.cah.project.module.standard.service.IDictTypeService;
import com.cah.project.core.dict.cache.DictDataCache;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;

/** *  Function description :  Dictionary type   Service implementation  <br/> */
@Service
public class DictTypeServiceImpl extends ServiceImpl<DictTypeMapper, DictTypeEntity> implements IDictTypeService {

    @Override
    public Collection<DictTypeOptions> selectOptions() {
        //  From the cache 
        return DictDataCache.getTypeList();
    }

    @Override
    public List<DictTypeEntity> selectAll() {
        return lambdaQuery().eq(DictTypeEntity::getEnable, YesOrNoEnum.YES.getCode()).list();
    }

}
 Copy code 
package com.cah.project.module.standard.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cah.project.core.dict.cache.DictDataCache;
import com.cah.project.core.dict.domain.vo.DictDataOptions;
import com.cah.project.core.enums.YesOrNoEnum;
import com.cah.project.module.standard.domain.entity.DictDataEntity;
import com.cah.project.module.standard.mapper.DictDataMapper;
import com.cah.project.module.standard.service.IDictDataService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;

/** *  Function description :  Dictionary data   Service implementation  <br/> */
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired, @Lazy})
public class DictDataServiceImpl extends ServiceImpl<DictDataMapper, DictDataEntity> implements IDictDataService {

    @Override
    public List<DictDataOptions> selectDataOptionsByCode(String dictType) {
        return DictDataCache.get(dictType);
    }

    @Override
    public List<DictDataOptions> selectDataOptionsByLoadType(String loadType, String dictType) {
        return DictDataCache.get(loadType, dictType);
    }

    @Override
    public List<DictDataEntity> selectListByCode(String dictType) {
        return lambdaQuery().eq(DictDataEntity::getTypeCode, dictType)
                .eq(DictDataEntity::getEnable, YesOrNoEnum.YES.getCode()).list();
    }

    @Override
    public List<DictDataEntity> selectListByCodes(Collection<String> dictTypeList) {
        return lambdaQuery().in(DictDataEntity::getTypeCode, dictTypeList)
                .eq(DictDataEntity::getEnable, YesOrNoEnum.YES.getCode()).list();
    }

}
 Copy code 

3、 The interface test

Get a list of dictionary types

image.png

Get dictionary data list - enumeration

image.png

Get dictionary data list - Dictionary table

image.png

Get dictionary data list - Business data

image.png

6、 ... and 、 Code address

[ Project tool code ]

summary

The description is not good , Will improve more . Welcome to discuss . Subsequent optimization contents : Multilevel ( Such as provincial and urban areas , National geographic , Industry category, medium category, small category, etc ) How to implement and cache dictionaries ? By splitting into single layers , Or add multi-level content to be considered .

原网站

版权声明
本文为[Don't ask me what I dislike]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/160/202203021405539843.html