基于mybatis-plus的多语言扩展

概览

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

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.用户查询


总结

可运行源码

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MyBatis-Plus是一个基于MyBatis的增强工具,在MyBatis的基础上提供了更加便捷的CRUD操作、分页查询以及代码生成等功能。下面是MyBatis-Plus源码下载的步骤和方法: 1. 打开MyBatis-Plus的官方网站:https://baomidou.com/ 2. 在网站的首页上方菜单栏找到“Github”选项,点击进入MyBatis-Plus的Github页面。 3. 在Github页面上,可以看到MyBatis-Plus的所有开源代码。点击页面右上方的“Code”按钮,然后选择“Download ZIP”选项进行下载。 4. 下载完成后,将下载的ZIP文件解压到指定的文件夹中。 此外,如果你更希望直接通过Maven等构建工具来引入MyBatis-Plus,也可以在项目的pom.xml文件中添加MyBatis-Plus的依赖,然后通过构建工具自动下载源码。 总之,获取MyBatis-Plus源码最简便的方法是通过其官方网站或Github页面下载。这样就能够获得最新版本的MyBatis-Plus源码,方便进行二次开发或查看源码实现细节。 ### 回答2: 要下载MyBatis-Plus源码,可以按照以下步骤进行操作。 1. 打开MyBatis-Plus的GitHub仓库页面。在浏览器中输入"https://github.com/baomidou/mybatis-plus",进入仓库页面。 2. 在仓库页面上,可以看到一个绿色的按钮,上面标有"Code"。点击这个按钮,会出现一个下拉菜单。 3. 在下拉菜单中,选择"Download ZIP"。点击后会自动下载一个ZIP压缩文件到本地电脑。 4. 解压下载的ZIP压缩文件,可以得到MyBatis-Plus的源代码。 此外,也可以通过其他方式获取MyBatis-Plus的源码,比如使用Git命令克隆仓库。你可以在命令行中执行以下命令:git clone https://github.com/baomidou/mybatis-plus.git。这样可以将整个仓库克隆到你的本地电脑上。 下载MyBatis-Plus源码后,你就可以在本地进行修改和扩展。可以使用Java开发工具(如IntelliJ IDEA、Eclipse等)导入源码工程,然后进行编译和运行,进行源码的阅读和修改,以满足你的需求。 希望上述回答对你有所帮助。如有其他问题,请随时提问。 ### 回答3: Mybatis-Plus是一个优秀的Mybatis增强工具,可以在数据库操作中提供更多的便利和增强的功能。如果想要下载Mybatis-Plus的源码,可以按照以下步骤进行操作: 第一步,打开Mybatis-Plus的官方Github仓库,找到源码的下载链接。可以通过搜索引擎输入"mybatis-plus github"来找到官方仓库。 第二步,进入官方仓库后,可以点击 "Clone or download" 按钮,然后选择 "Download ZIP" 选项进行下载。这样就可以将整个Mybatis-Plus源码以zip压缩方式下载到本地。 第三步,下载完成后,可以将zip文件解压到本地的任意目录。 第四步,进入解压后的文件夹,可以看到Mybatis-Plus的源码文件和项目结构。 至此,你已经成功下载了Mybatis-Plus的源码。可以在本地对源码进行查看、学习和调试。 值得注意的是,Mybatis-Plus的源码是基于Java语言编写的,所以需要确保本地环境已经安装了Java开发工具。另外,Mybatis-Plus的源码是一个完整的项目,包含了各种功能模块和依赖库,所以无需额外的配置即可进行使用。 通过下载Mybatis-Plus的源码,可以深入了解其内部实现原理、自定义扩展功能,同时也能更好地理解和使用Mybatis-Plus提供的各种便利和增强功能,对于深入学习和使用Mybatis-Plus都非常有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值