概览
对于表中字段,需要实现多语言的方案探讨:
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.用户查询
总结
可运行源码