概览
对于表中字段,需要实现多语言的方案探讨:
1.表中横向扩展多个字段分别存储中文,英文,俄语等语言字段,查询时,根据需要查询的语言,进行查询
2.增加一张多语言表,存储多语言信息,如图,同时基于mybatis-plus扩展插入更新查询等功能,完成代码无侵入的多语言功能
一、组件的开发
1.自定义重写sql注入器ZlxSqlInjector
@Overridepublic 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);}}}@Overridepublic 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 脚本片段*/@Overridepublic 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 {@Overridepublic 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);}}@Overrideprotected 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 {@Overridepublic 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 {@Autowiredprivate UserService userService;/*** 新增用户** @return*/@PostMappingpublic ResponseEntity add(@RequestBody User user) {userService.save(user);return ResponseEntity.ok("成功");}/*** 用户详情** @return*/@GetMappingpublic 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*/@PutMappingpublic ResponseEntity update(@RequestBody User user) {userService.update(user,null);return ResponseEntity.ok("成功");}/*** 删除用户** @return*/@DeleteMappingpublic 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.用户查询
总结
可运行源码