基于mybatis-plus的多语言扩展

news2024/9/26 1:20:36

概览

对于表中字段,需要实现多语言的方案探讨:

1.表中横向扩展多个字段分别存储中文,英文,俄语等语言字段,查询时,根据需要查询的语言,进行查询

2.增加一张多语言表,存储多语言信息,如图,同时基于mybatis-plus扩展插入更新查询等功能,完成代码无侵入的多语言功能


一、组件的开发

1.自定义重写sql注入器ZlxSqlInjector

  @Override
    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);
        if (modelClass != null) {
            String className = mapperClass.toString();
            Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
            if (!mapperRegistryCache.contains(className)) {
                List<AbstractMethod> methodList = this.getMethodList(mapperClass);
                if (CollectionUtils.isNotEmpty(methodList)) {
                    ZlxTableInfo tableInfo = ZlxTableInfoHelper.initTableInfo(builderAssistant, modelClass,className);
                    // 循环注入自定义方法
                    methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
                } else {
                    log.debug(mapperClass.toString() + ", No effective injection method was found.");
                }
                mapperRegistryCache.add(className);
            }
        }
    }
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
            return Stream.of(
                new com.zenglx.multilanguage.method.Insert(),
                new Delete(),
                new DeleteByMap(),
                new DeleteById(),
                new DeleteBatchByIds(),
                new Update(),
                new UpdateById(),
                new com.zenglx.multilanguage.method.SelectById(),
                new com.zenglx.multilanguage.method.SelectBatchByIds(),
                new SelectByMap(),
                new com.zenglx.multilanguage.method.SelectOne(),
                new com.zenglx.multilanguage.method.SelectCount(),
                new SelectMaps(),
                new SelectMapsPage(),
                new SelectObjs(),
                new com.zenglx.multilanguage.method.SelectList(),
                    new com.zenglx.multilanguage.method.SelectPage()
        ).collect(toList());
    }

2.自定义元数据和方法

@Getter
@Setter
public class ZlxTableInfo extends TableInfo {
    private String condition = SqlCondition.EQUAL;
    /**
     * 多语言字段
     */
    private Set<TableFieldInfo> multiLanguageColumns = new HashSet<>();

    /**
     * 实体类 => 主键信息
     */
    private Set<TableFieldInfo> entityClassPkColumns = new HashSet<>();

    /**
     * 实体类 => 全部列
     */
    private Set<TableFieldInfo> entityClassColumns = new HashSet<>();

    /**
     * 是否多语言
     */
    private boolean multiLanguage;

    public ZlxTableInfo(Class<?> entityType) {
        super(entityType);
    }


    public List<String> multiLanguageColumns() {
        return multiLanguageColumns.stream()
                .map(TableFieldInfo::getColumn).collect(Collectors.toList());
    }


    /**
     * 获取所有的查询的 sql 片段
     *
     * @param ignoreLogicDelFiled 是否过滤掉逻辑删除字段
     * @param withId              是否包含 id 项
     * @param prefix              前缀
     * @return sql 脚本片段
     */
    @Override
    public String getAllSqlWhere(boolean ignoreLogicDelFiled, boolean withId, final String prefix) {
        final String newPrefix = prefix == null ? EMPTY : prefix;
        String filedSqlScript = super.getFieldList().stream()
                .filter(i -> {
                    if (ignoreLogicDelFiled) {
                        return !(isWithLogicDelete() && i.isLogicDelete());
                    }
                    return true;
                })
                .map(i -> getSqlWhere(newPrefix,i)).filter(Objects::nonNull).collect(joining(NEWLINE));
        if (!withId || StringUtils.isBlank(super.getKeyProperty())) {
            return filedSqlScript;
        }
        String newKeyProperty = newPrefix + super.getKeyProperty();
        String keySqlScript = super.getKeyColumn() + EQUALS + SqlScriptUtils.safeParam(newKeyProperty);
        return SqlScriptUtils.convertIf(keySqlScript, String.format("%s != null", newKeyProperty), false)
                + NEWLINE + filedSqlScript;
    }

    private String convertIfProperty(String prefix, String property) {
        return StringUtils.isNotBlank(prefix) ? prefix.substring(0, prefix.length() - 1) + "['" + property + "']" : property;
    }

    /**
     * 获取 查询的 sql 片段
     *
     * @param prefix 前缀
     * @return sql 脚本片段
     */
    public String getSqlWhere(final String prefix,TableFieldInfo tableFieldInfo) {
        final String newPrefix = prefix == null ? EMPTY : prefix;
        // 默认:  AND column=#{prefix + el}
        String sqlScript = " AND " + String.format(condition, tableFieldInfo.getColumn(), newPrefix + tableFieldInfo.getEl());
        if(this.getMultiLanguageColumns().contains(tableFieldInfo)) {
            sqlScript = " AND " + String.format(condition, "t."+tableFieldInfo.getColumn(), newPrefix + tableFieldInfo.getEl());
        }
        // 查询的时候只判非空
        return convertIf(sqlScript, convertIfProperty(newPrefix, convertIfProperty(newPrefix, tableFieldInfo.getProperty())), tableFieldInfo);
    }

    /**
     * 转换成 if 标签的脚本片段
     *
     * @param sqlScript     sql 脚本片段
     * @param property      字段名
     * @param tableFieldInfo 验证策略
     * @return if 脚本片段
     */
    private String convertIf(final String sqlScript, final String property, TableFieldInfo tableFieldInfo) {
        if (tableFieldInfo.getWhereStrategy() == FieldStrategy.NEVER) {
            return null;
        }
        if (tableFieldInfo.isPrimitive() || tableFieldInfo.getWhereStrategy() == FieldStrategy.IGNORED) {
            return sqlScript;
        }
        if (tableFieldInfo.getWhereStrategy() == FieldStrategy.NOT_EMPTY && tableFieldInfo.isCharSequence()) {
            return SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and %s != ''", property, property),
                    false);
        }
        return SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", property), false);
    }

}
public class SelectById extends com.baomidou.mybatisplus.core.injector.methods.SelectById {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        ZlxTableInfo zlxTableInfo = (ZlxTableInfo)tableInfo;
        if(zlxTableInfo.isMultiLanguage()) {
            SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
            String scriptSql = "<script>SELECT %s FROM %s WHERE %s \n</script>";
            String sql = String.format(scriptSql, SqlHelper.getAllColumns(tableInfo), SqlHelper.selectFromTableTl(tableInfo),
                    sqlWhereEntityWrapper(true, tableInfo));
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
            return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
        } else {
           return super.injectMappedStatement(mapperClass, modelClass, tableInfo);
        }
    }

    @Override
    protected String sqlWhereEntityWrapper(boolean newLine, TableInfo tableInfo) {
        ZlxTableInfo zlxTableInfo = (ZlxTableInfo)tableInfo;
        if(zlxTableInfo.isMultiLanguage()) {
            StringBuilder sb = new StringBuilder();
            sb.append("b.").append(zlxTableInfo.getKeyColumn())
                    .append("=#{id}");
            return sb.toString();
        } else {
            return super.sqlWhereEntityWrapper(newLine, tableInfo);
        }

    }
}

3.自定义拦截器

 public class ZlxMultiLanguageInterceptor extends PaginationInnerInterceptor {


@Override
    public void beforeUpdate(Executor executor, MappedStatement statement, Object parameter) throws SQLException {
        ZlxTableInfo tableInfo = ZlxTableInfoHelper.getTableByMapper(getMapperClassName(statement.getId()));
        if (tableInfo != null && tableInfo.isMultiLanguage()) {
            Connection connection = executor.getTransaction().getConnection();
            switch (statement.getSqlCommandType()) {
                case INSERT:
                    insertMultiLanguage(tableInfo, parameter, connection);
                    break;
                case UPDATE:
                    List<String> columnNameList = tableInfo.getEntityClassColumns().stream().map(TableFieldInfo::getColumn)
                                    .collect(Collectors.toList());
                    updateMultiLanguage(tableInfo, parameter, connection, new HashSet<>(columnNameList));
                    break;
                case DELETE:
                    proceedDeleteMultiLanguage(tableInfo, parameter, connection);
                    break;
                default:
                    break;
            }
        }
    }

}

二、组件的使用

1.新建表

原表t_user,多语言表t_user_tl

2.引入库

<dependency>
    <groupId>com.zenglx.multilanguage</groupId>
    <artifactId>multilanguage-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

3.demo验证测试

 

@RequestMapping("/v1/demo")
public class DemoController {

    @Autowired
    private UserService userService;

    /**
     * 新增用户
     *
     * @return
     */
    @PostMapping
    public ResponseEntity add(@RequestBody User user) {
        userService.save(user);
        return ResponseEntity.ok("成功");
    }

    /**
     * 用户详情
     *
     * @return
     */
    @GetMapping
    public ResponseEntity<User> get(@RequestParam("id") Long id) {
        return ResponseEntity.ok(userService.getById(id));
    }

    /**
     * 查询用户列表
     *
     * @return
     */
    @GetMapping("/list")
    public ResponseEntity<List<User>> list() {
        return ResponseEntity.ok(userService.list());
    }

    /**
     * 更新用户
     *
     * @return
     */
    @PutMapping
    public ResponseEntity update(@RequestBody User user) {
        userService.update(user,null);
        return ResponseEntity.ok("成功");
    }


    /**
     * 删除用户
     *
     * @return
     */
    @DeleteMapping
    public ResponseEntity delete(@RequestParam("id") Long id) {
        userService.removeById(id);
        return ResponseEntity.ok("成功");
    }


    @GetMapping("/page")
    public ResponseEntity<Page<User>> page(@RequestParam(required = false, value = "name") String name,
                                                         @RequestParam(defaultValue = "1", value = "page") Integer page,
                                                         @RequestParam(defaultValue = "10", value = "pageSize") Integer pageSize) {
        Page<User> param = new Page<>(page,pageSize);
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(StringUtils.hasText(name),User::getName,name);
        Page<User> userPage = userService.page(param,lambdaQueryWrapper);
        return ResponseEntity.ok(userPage);
    }

   a.用户添加

b.用户查询


总结

可运行源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1705760.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RedHat9网络配置设计

目录 一、实验目的 二、实验过程 1、配置新网络接口 2、多网卡配置网络 3、网络接口的绑定&#xff0c;进行远程访问 4、配置网络接口的组合 一、实验目的 本次实验的目的是使用nmcli命令工具配置网络&#xff0c;ens160配置多个网卡&#xff0c;进行网络接口的绑定与组合…

iPhone“已删除”照片被恢复,苹果到底有没有后门?

继微软本周推出的Windows“回忆”功能引发隐私焦虑&#xff0c;遭马斯克和安全大咖们猛烈抨击后&#xff0c;苹果iPhone手机近日也曝出了类似的“记忆门”。 删除十几年的iPhone照片被恢复 近日&#xff0c;有苹果手机用户更新了苹果上周发布的iOS 17.5系统后&#xff0c;意外…

ChatGPT越来越懒,都学会反过来PUA人类了

OpenAI 表示自 11 月 11 日以来&#xff0c;他们就没有更新过模型&#xff0c;模型行为是不可预测的&#xff0c;他们正在研究如何修复。 不知你有没有注意到&#xff0c;最近一段时间&#xff0c;GPT-4 变得有些「懒惰」&#xff0c;现在的它&#xff0c;老是拒绝执行某些任务…

关于亚马逊、速卖通、虾皮、Lazada等平台自养号测评IP的重要性

在自养号测评中&#xff0c;IP的纯净度是一个至关重要的问题&#xff0c;它直接关系到账号的安全性和稳定性如果使用了被平台识别为异常或存在风险的IP地址&#xff0c;那么账号可能会面临被封禁的风险。这将对账号的正常使用和测评过程中造成严重影响。而使用纯净的IP地址&…

oracle准确记录数据提交时间

注意&#xff1a;mysql中的默认值同样记录的是dml操作发出时的时间&#xff0c;并且没有找到mysql中准确记录commit时间的方法。 oracle中数据发生变动时&#xff0c;如何准确记录发生变动时的时间。一般会使用ts字段&#xff0c;该字段使用默认值&#xff0c;default to_char…

【JAVA WEB实用与优化技巧】如何自己封装一个自定义UI的Swagger组件,包含Swagger如何处理JWT无状态鉴权自动TOKEN获取

目录 一、Swagger 简介1. 什么是 Swagger&#xff1f;2. 如何使用 Swagger3. Springboot 中swagger的使用示例1. maven 引入安装2. java配置 二、Swagger UI存在的缺点1.不够方便直观2.请求的参数没有缓存3.不够美观4.如果是JWT 无状态登录&#xff0c;Swagger使用起来就没有那…

简化跨网文件传输摆渡过程,降低IT人员工作量

在当今数字化时代&#xff0c;IT企业面临着日益增长的数据交换需求。随着网络安全威胁的不断演变&#xff0c;网关隔离成为了保护企业内部网络不受外部威胁的重要手段。然而&#xff0c;隔离的同时&#xff0c;企业也需要在不同网络间安全、高效地传输文件&#xff0c;这就催生…

harmony开发遇到的问题

arkt-no-props-by-indexProperty ‘name’ has no initializer and is not definitely assigned in the constructor.arkts-no-any-unknown typescript 中的报错 Property ‘name’ has no initializer and is not definitely assigned in the construc… ArkTs编译常见错误汇…

全网爆火Remini 粘土滤镜风格,我用ComfyUI一键生成了(保姆级教程)!

一、火爆全网的Remini&#xff01; Remini真的火爆了&#xff01;最近大家的朋友应该都被粘土滤镜刷屏了。 小红书上粘土滤镜、粘土特效的帖子动不动就是几百万浏览量&#xff0c;几千赞。 在有些电商平台上还有人接单&#xff0c;帮忙定制remini粘土风格的照片&#xff01; …

Shell编程之正则表达式与文本处理器

一、正则表达式 之前学习了 Shell 脚本的基础用法&#xff0c;已经可以利用条件判断、循环等语句编辑 Shell 脚本。接下来我们将开始介绍一个很重要的概念——正则表达式&#xff08;RegularExpression&#xff0c;RE&#xff09;。 1.1正则表达式概述 下面先来了解一下正则表…

软件产品测试报告模版分享,获取专业测试报告所需时间和费用

软件产品测试是一个系统性的、全面的质量保障过程&#xff0c;旨在发现和修复软件产品中的缺陷和错误&#xff0c;以确保软件的功能完善、性能卓越、安全可靠。 软件产品测试报告是软件测试过程中的重要文档之一&#xff0c;它主要记录了测试人员对软件产品进行测试的结果和结…

LLM 大模型学习必知必会系列(十三):基于SWIFT的VLLM推理加速与部署实战

LLM 大模型学习必知必会系列(十三)&#xff1a;基于SWIFT的VLLM推理加速与部署实战 1.环境准备 GPU设备: A10, 3090, V100, A100均可. #设置pip全局镜像 (加速下载) pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ #安装ms-swift pip install ms-…

[链表]求中间节点、反转链表、回文链表

一、求链表的中间节点 876. 链表的中间结点 - 力扣&#xff08;LeetCode&#xff09; 快慢指针法: 分别定义两个节点的指针(pSlow和pFast)指向链表的第一个节点&#xff0c;然后两个指针一起往后遍历链表&#xff0c;pFast一次移动两个节点&#xff0c;pSlow一次移动一个节点&…

打工人好用的大模型问答,还需要一款可靠的文档解析工具

如果说三四年前&#xff0c;我们对AI的展望还停留在科幻片的话&#xff0c;现在&#xff0c;通向AI智能的路径已经初现端倪。各行各业的朋友们不约而同地嗅到了大模型带来的生产方式变革气息。 LLM宣布了AI时代的正式到来。 2022年11月30日&#xff0c;ChatGPT发布&#xff0…

牛客NC362 字典序排列【中等 DFS Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/de49cf70277048518314fbdcaba9b42c 解题方法 DFS&#xff0c;剪枝Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回…

SaltStack

SaltStack 官方文档 1.简介 作用&#xff1a;批量处理状态管理&#xff08;配置管理&#xff09;事件驱动&#xff08;通过事件触发操作&#xff09;管理私有云/公有云 yum仓库&#xff1a;http://repo.saltstack.com 安装1.master和minionrpm --import https://repo.saltproj…

政府鼓励社会力量建设气膜体育场馆—轻空间

2023年12月1日&#xff0c;国家体育安全总局发布的《关于政协第十四届全国委员会第一次会议第00374号&#xff08;文体宣传类020号&#xff09;提案答复的函》中指出&#xff0c;2016年和2020年国务院发布的文件中均涉及推动气膜场馆建设及完善装配式建筑相关政策。下一步&…

Git学习篇

目录 使用命令导入项目 使用命令导入项目 1. 使用git init 命令初始化一个新的Git仓库。 git init 是 Git 命令&#xff0c;用于初始化一个新的 Git 仓库。当您想要开始跟踪一个新项目的版本控制时&#xff0c;可以运行 git init 命令来初始化一个空的 Git 仓库。 如果出现以下…

项目文件上传宝塔后只有一个文件内容不同且没有报错该如解决?

一、遇到此类问题不要慌先检查文件是否上传正确&#xff1b; 二、检查文件是否可以在浏览器中正常运行&#xff1b; 三、检查文件是否存在某些不明显的报错&#xff1b; 四、检查此页面的是否存在代码错误&#xff1b; 五、标签链接是否错误&#xff1b; 我所遇到的错误是…

白酒:不同产地白酒的风格特点与比较

云仓酒庄豪迈白酒&#xff0c;作为中国白酒的一部分&#xff0c;其风格特点深受产区的影响。不同产地的白酒&#xff0c;由于自然环境、酿造工艺等因素的差异&#xff0c;形成了各自与众不同的风味和特点。下面让云仓酒庄豪迈白酒来比较一下不同产地白酒的风格特点。 首先&…