fhs-framework springboot mybatisテーブル関連クエリーの問題を解決するキー・ソリューション-翻訳サービス

20712 ワード

概要
開発では、成績表にstudent_があるなど、よく遭遇します.id、もし私が成績リストを表示するには学生の名前が必要で、一般的な書き方はjoinを使うことです.今、ほとんどのORMフレームワークは表の関連サポートに友好的ではありません.だから、私たちは自分でSQLを書いて実現しなければならないことが多いです.
翻訳サービスとは、idにより、対応するtitle/nameフィールドを翻訳するVOにロードするフロントエンド表示のための技術である.
1 FHSが提供する翻訳サービスはどのように使用しますか?
a翻訳データソースを定義する.
        
@Service
@DataSource("base_business")
@AutoTrans(namespace = BaseTransConstant.ORG, fields = "name", useRedis = true, defaultAlias = "org")
public class UcenterMsOrganizationServiceImpl extends BaseServiceImpl implements UcenterMsOrganizationService {

以上の定義により、組織機構の表、対外開放組織機構名というフィールドが他の業務に使用することが分かる.
bタグ業務pojo orgidフィールドはこの翻訳データソースを使用する.
          
/**
     *     
     */
    @NotNull(message = "         null", groups = {Update.class, Delete.class})
    @Length(message = "            32", groups = {Add.class, Update.class}, max = 32)
    @Column(name = "organization_id")
    @Trans(type = TransType.AUTO_TRANS,key = BaseTransConstant.ORG )
    private String organizationId;

c transServiceのtrans関連メソッドを呼び出せばよい.
         
 public V d2v(D d) {
        try {
            if (d == null) {
                return null;
            }
            V vo = voClass.newInstance();
            BeanUtils.copyProperties(d, vo);
            transService.transOne(vo);
            return vo;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

 public List dos2vos(List dos) {
        List vos = ListUtils.copyListToList(dos, this.getVOClass());
        transService.transMore(vos);
        return vos;
    }

c VOではgetTransMapメソッドでmapを返す必要があり、翻訳サービスは翻訳の結果をこのmapに置く.
d翻訳後の結果例:
    
 {
            "between": {},
            "createTime": "2019-03-25 00:00:00",
            "createUser": "1",
            "dataPermissin": {},
            "dataPermissions": "{\"parkIds\":\"0d601eb23f0e11e99571d02788407b5e\"}",
            "groupCode": null,
            "inFilter": {},
            "isDelete": null,
            "isEnable": 1,
            "methods": null,
            "organizationId": "001",
            "pkey": 1,
            "remark": "12123",
            "roleId": 1,
            "roleName": "1231",
            "state": null,
            "transMap": {
                "orgName": "  ",
                "isEnableName": "  ",
                "createUserUserName": "admin22"
            },
            "updateTime": "2019-03-29 00:00:00",
            "updateUser": "62b5870c510c4e9da3f72460001c42fa"
        },

2翻訳サービスの実装
A注記の定義
注釈を自動的に翻訳し、1つのサービスをデータソースの注釈として、サービス実装クラスの上に使用します.例えば、上のORGservice
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE})
public @interface AutoTrans
{
    /**
     *      
     * @return
     */
    String namespace();

    /**
     *     
     * @return
     */
    String[] fields();

    /**
     *         
     * @return     true    false  
     */
    boolean useCache() default true;

    /**
     *     redis    
     * @return   false
     */
    boolean useRedis() default false;

    /**
     *      
     * @return
     */
    String defaultAlias() default "";
}

フィールド翻訳注記は、fhsがauto(自動翻訳)、wordbook(辞書)を提供するなど、このフィールドがどの翻訳を使用するかを定義します.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface Trans {

    /**
     *       ,   wordbook    
     * @return   
     */
    String type();

    /**
     *                 type wordbook  key sex  
     * @return
     */
    String key() default "";
}

pojoにマークする翻訳タイプの集合で、私がどのタイプの翻訳を使用したかを表します.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE})
public @interface TransTypes 
{
    /**
     *          
     * @return
     */
    String[] types();
}

B翻訳サービスクラスの定義
主に翻訳の種類によってどのITransTypeServiceの実装クラスを呼び出していくつかのフィールドを翻訳するかを決定する.

@Service("transService")
public class TransService {
    /**
     * key type  val   type service
     */
    private static Map transTypeServiceMap = new HashMap();

    /**
     *     trans  
     *
     * @param type               
     * @param transTypeService    trans    
     */
    public static void registerTransType(String type, ITransTypeService transTypeService) {
        transTypeServiceMap.put(type, transTypeService);
    }

    /**
     *       
     *
     * @param obj        
     */
    public void transOne(VO obj) {
        if (obj == null) {
            return;
        }
        ClassInfo info = ClassManager.getClassInfoByName(obj.getClass());
        String[] transTypes = info.getTransTypes();
        if (transTypes == null) {
            return;
        }
        List transFieldList = null;
        for (String type : transTypes) {
            transFieldList = info.getTransField(type);
            if (transFieldList == null || transFieldList.size() == 0) {
                continue;
            }
            transTypeServiceMap.get(type).transOne(obj, transFieldList);
        }
    }

    /**
     *        
     *
     * @param objList          
     * @param objList          
     */
    public void transMore(List extends VO> objList) {
        if (objList == null || objList.size() == 0) {
            return;
        }
        Object object = objList.get(0);
        ClassInfo info = ClassManager.getClassInfoByName(object.getClass());
        String[] transTypes = info.getTransTypes();
        if (transTypes == null) {
            return;
        }
        List transFieldList = null;
        for (String type : transTypes) {
            transFieldList = info.getTransField(type);
            if (transFieldList == null || transFieldList.size() == 0) {
                continue;
            }
            transTypeServiceMap.get(type).transMore(objList, transFieldList);
        }
    }

}

D自動翻訳実現
Springbootの起動に成功すると、サービスパッケージをスキャンし、autotransに注釈マークされたサービスを解析し、autotransの内容を解析し、データベースのデータをメモリとredisにキャッシュし、業務が翻訳する必要がある場合、idに基づいてキャッシュデータを取り出し、transmapに格納.
 
    

/**
 *          Autotrans    autoTransAble    
 *
 * @Description:       
 * @Author: Wanglei
 * @Date: Created in 10:14 2019/10/15
 */
@Data
@Service
public class AutoTransService implements ITransTypeService, InitializingBean, ApplicationListener {

    public static final Logger LOGGER = LoggerFactory.getLogger(AutoTransService.class);


    /**
     * service    
     */
    @Value("${fhs.autotrans.package:com.*.*.service.impl}")
    private String[] packageNames;

    /**
     *       map
     */
    private Map> cacheMap = new HashMap<>();


    /**
     *        :    
     */
    @CreateCache(expire = 1800, name = "trans:cache:", cacheType = CacheType.REMOTE)
    private Cache> transCache;

    /**
     *     
     */
    private Map baseServiceMap = new HashMap<>();

    /**
     *   
     */
    private Map transSettMap = new HashMap<>();

    /**
     *          ,    cache 
     */
    private ThreadLocal>> threadLocalCache = new ThreadLocal<>();

    /**
     *       map
     */
    private Map transFieldSettMap = new HashMap<>();

    @Override
    public void transOne(VO obj, List toTransList) {
        Trans tempTrans = null;
        for (Field tempField : toTransList) {
            TransFieldSett transFieldSett = transFieldSettMap.containsKey(tempField) ? transFieldSettMap.get(tempField) : new TransFieldSett(tempField);
            tempTrans = transFieldSett.getTrans();
            String namespace = transFieldSett.getNamespace();
            String alias = transFieldSett.getAlias();
            if (transSettMap.containsKey(namespace) && CheckUtils.isNullOrEmpty(alias)) {
                alias = transSettMap.get(namespace).defaultAlias();
            }
            String pkey = ConverterUtils.toString(ReflectUtils.getValue(obj, tempField.getName()));
            if (StringUtils.isEmpty(pkey)) {
                continue;
            }
            Map transCache = null;
            //        
            pkey = pkey.replace("[", "").replace("]", "");
            if (pkey.contains(",")) {
                String[] pkeys = pkey.split(",");
                transCache = new HashMap<>();
                Map tempTransCache = null;
                for (String tempPkey : pkeys) {
                    tempTransCache = getTempTransCacheMap(namespace, ConverterUtils.toInteger(tempPkey));
                    if (tempTransCache == null) {
                        LOGGER.error("auto trans     :" + namespace + "_" + tempPkey);
                        continue;
                    }
                    //           name age 2   
                    for (String key : tempTransCache.keySet()) {
                        transCache.put(key, transCache.containsKey(key) ? transCache.get(key) + "," + tempTransCache.get(key) : tempTransCache.get(key));
                    }
                }
            } else {
                transCache = getTempTransCacheMap(namespace, pkey);
                if (transCache == null) {
                    LOGGER.error("auto trans     :" + namespace + "_" + pkey);
                    continue;
                }
            }
            if (!CheckUtils.isNullOrEmpty(alias)) {
                Map tempMap = new HashMap<>();
                Set keys = transCache.keySet();
                for (String key : keys) {
                    tempMap.put(alias + key.substring(0, 1).toUpperCase() + key.substring(1), transCache.get(key));
                }
                transCache = tempMap;
            }
            Map transMap = obj.getTransMap();
            Set keys = transCache.keySet();
            for (String key : keys) {
                if (CheckUtils.isNullOrEmpty(transMap.get(key))) {
                    transMap.put(key, transCache.get(key));
                }
            }
        }
    }

    @Override
    public void transMore(List extends VO> objList, List toTransList) {
        threadLocalCache.set(new HashMap<>());
        //           ,             ,  db      ,              
        for (Field tempField : toTransList) {
            tempField.setAccessible(true);
            Trans tempTrans = tempField.getAnnotation(Trans.class);
            String namespace = tempTrans.key();
            //     good#student          goodStuName goodStuAge  customer#customer  customerName
            if (namespace.contains("#")) {
                namespace = namespace.substring(0, namespace.indexOf("#"));
            }
            if (!this.baseServiceMap.containsKey(namespace)) {
                LOGGER.warn("namesapce   service    autotrans:" + namespace);
                continue;
            }
            AutoTrans autoTransSett = this.transSettMap.get(namespace);
            if (autoTransSett.useCache()) {
                continue;
            }
            Set ids = new HashSet<>();
            objList.forEach(obj -> {
                try {
                    ids.add(tempField.get(obj));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
            List dbDatas = baseServiceMap.get(namespace).findByIds(new ArrayList<>(ids));
            for (VO vo : dbDatas) {
                threadLocalCache.get().put(namespace + "_" + vo.getPkey(), createTempTransCacheMap(vo, autoTransSett));
            }
        }
        objList.forEach(obj -> {
            this.transOne(obj, toTransList);
        });
        threadLocalCache.set(null);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        TransService.registerTransType("auto", this);
        TransMessageListener.regTransRefresher("auto", this::refreshCache);
    }


    public void init(ApplicationReadyEvent applicationReadyEvent) {
        //spring         ,       。
        Set> entitySet = scan(AutoTrans.class, packageNames);
        //     class,     @autowareYLM     
        if (entitySet != null) {
            for (Class> entity : entitySet) {
                //     
                Object baseService = SpringContextUtil.getBeanByClass(entity);
                if (!(baseService instanceof AutoTransAble)) {
                    LOGGER.warn("AutoTrans       AutoTransAble   ,    :" + baseService.getClass());
                    continue;
                }
                AutoTrans autoTransSett = entity.getAnnotation(AutoTrans.class);
                this.baseServiceMap.put(autoTransSett.namespace(), (AutoTransAble) baseService);
                this.transSettMap.put(autoTransSett.namespace(), autoTransSett);
            }
        }
        refreshCache(new HashMap<>());
    }

    /**
     *     
     *
     * @param messageMap   
     */
    public void refreshCache(Map messageMap) {
        //       namespace    ,       
        String namespace = messageMap.get("namespace") != null ?
                messageMap.get("namespace").toString() : null;
        if (namespace == null) {
            Set namespaceSet = this.transSettMap.keySet();
            namespaceSet.forEach(temp -> {
                refreshOneNamespace(temp);
            });
        } else {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                LOGGER.error("      :", e);
            }
            refreshOneNamespace(namespace);
        }
    }

    /**
     *     namespace       
     *
     * @param namespace namespace
     */
    public void refreshOneNamespace(String namespace) {
        LOGGER.info("    auto-trans  :" + namespace);
        if (!this.transSettMap.containsKey(namespace)) {
            LOGGER.info("          namespace:" + namespace);
            return;
        }

        List vos = this.baseServiceMap.get(namespace).select();
        if (vos == null || vos.isEmpty()) {
            return;
        }
        Object pkeyVal = null;
        String fielVal = null;
        Map tempCacheTransMap = null;
        VO po = null;
        AutoTrans autoTrans = this.transSettMap.get(namespace);
        //          
        if (!autoTrans.useCache()) {
            return;
        }
        for (int i = 0; i < vos.size(); i++) {
            po = vos.get(i);
            pkeyVal = po.getPkey();
            cacheMap.put(namespace + "_" + pkeyVal, createTempTransCacheMap(po, autoTrans));
            if(autoTrans.useCache()){
                this.transCache.put(namespace + "_" + pkeyVal, createTempTransCacheMap(po, autoTrans));
            }
        }
        LOGGER.info("  auto-trans    :" + namespace);
    }

    /**
     *         map
     *
     * @param po        po
     * @param autoTrans   
     * @return
     */
    private Map createTempTransCacheMap(Object po, AutoTrans autoTrans) {
        String fielVal = null;
        Map tempCacheTransMap = new LinkedHashMap<>();
        for (String field : autoTrans.fields()) {
            fielVal = ConverterUtils.toString(ReflectUtils.getValue(po, field));
            tempCacheTransMap.put(field, fielVal);
        }
        return tempCacheTransMap;
    }

    /**
     *          
     *
     * @param namespace namespace
     * @param pkey        
     * @return   
     */
    private Map getTempTransCacheMap(String namespace, Object pkey) {
        AutoTrans autoTrans = this.transSettMap.get(namespace);
        //        ,        
        if (cacheMap.containsKey(namespace + "_" + pkey)) {
            return cacheMap.get(namespace + "_" + pkey);
        }
        //      ,               ,   redis    
        else if (autoTrans == null) {
            Map redisCacheResult = this.transCache.get(namespace + "_" + pkey);
            //        
            if (redisCacheResult != null) {
                return redisCacheResult;
            }
            //redis       map
            return new HashMap<>();
        } else {
            if (autoTrans == null) {
                LOGGER.warn("namespace   service    autotrans    :" + namespace);
                return  new HashMap<>();
            }
            //        ,          ,     map,          
            if (autoTrans.useCache()) {
                return new HashMap<>();
            }
            if (this.threadLocalCache.get() == null) {
                VO vo = this.baseServiceMap.get(namespace).selectById(pkey);
                return createTempTransCacheMap(vo, autoTrans);
            }
            return this.threadLocalCache.get().get(namespace + "_" + pkey);
        }
    }

    /**
     *      key
     *
     * @param namespace namespace
     * @param pkeyVal     
     * @return
     */
    public String transKey(String namespace, String pkeyVal) {
        Map tempCacheTransMap = cacheMap.get(namespace + "_" + pkeyVal);
        if (tempCacheTransMap == null) {
            LOGGER.error("auto trans     :" + namespace + "_" + pkeyVal);
        } else {
            for (String key : tempCacheTransMap.keySet()) {
                return tempCacheTransMap.get(key);
            }
        }
        return null;
    }

    /**
     *     
     *
     * @param annotationClass   
     * @param packageNames     
     * @return       
     */
    public static Set> scan(Class extends Annotation> annotationClass, String[] packageNames) {
        TypeFilter entityFilter = AnnotationTypeFilterBuilder.build(annotationClass);
        SpringClassScanner entityScanner = new SpringClassScanner.Builder().typeFilter(entityFilter).build();
        for (String packageName : packageNames) {
            entityScanner.getScanPackages().add(packageName);
        }
        Set> entitySet = null;
        try {
            entitySet = entityScanner.scan();
        } catch (ClassNotFoundException | IOException e) {
            LOGGER.error("     ", e);
            // log or throw runTimeExp
            throw new RuntimeException(e);
        }
        return entitySet;
    }

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        init(applicationReadyEvent);
    }

}

/**
 *          
 */
@Data
class TransFieldSett {
    /**
     * trans  
     */
    private Trans trans;
    /**
     *     
     */
    private String namespace;
    /**
     *   
     */
    String alias;

    public TransFieldSett(Field transField) {
        transField.setAccessible(true);
        trans = transField.getAnnotation(Trans.class);
        namespace = trans.key();
        //     good#student          goodStuName goodStuAge  customer#customer  customerName
        if (namespace.contains("#")) {
            alias = namespace.substring(namespace.indexOf("#") + 1);
            namespace = namespace.substring(0, namespace.indexOf("#"));
        }
    }
}

すべてのソースアドレスhttps://gitee.com/fhs-opensource/fhs-framework/tree/v2.x/fhs_extends/fhs_trans
オープンソースプロジェクトのアドレス:https://gitee.com/fhs-opensource/fhs-framework
fhs framework qq群:976278956