在MyBatis-Plus中,自定义注入器功能提供了一种方式,可以在MyBatis的核心操作流程中无缝集成个人的业务逻辑。这包括在数据库的查询、更新或删除操作的前后注入特定的代码。通过这种机制,MyBatis-Plus的功能得以灵活扩展,从而更好地满足各种复杂多变的业务需求。 在将MyBatis-Plus与Doris数据库对接的过程中,实现动态的行转列查询逻辑,以适应更加丰富和高效的数据处理方式。
自定义注入器的应用场景 自定义注入器可以应用于多种场景,比如:
数据审计:在执行数据库操作前后记录日志,用于追踪数据的变更历史。
数据脱敏:在查询敏感信息前对数据进行脱敏处理,保护用户隐私。
性能监控:监控数据库操作的性能,帮助优化系统性能。
自定义查询方法 :当标准的CRUD操作无法满足复杂的查询需求时,可以通过SQL注入器添加自定义的查询方法。
复杂数据处理 :在需要进行复杂的数据处理,如多表联结、子查询、聚合函数等时,SQL注入器可以帮助生成相应的SQL语句。
性能优化 :通过自定义SQL语句,可以针对特定的查询场景进行性能优化。
数据权限控制 :在需要根据用户权限动态生成SQL语句时,SQL注入器可以用来实现数据权限的控制。
遗留系统迁移 :在将遗留系统迁移到MyBatis-Plus时,可能需要保留原有的SQL语句结构,SQL注入器可以帮助实现这一过渡。
自定义全局方法攻略 1. 定义SQL 首先,你需要定义自定义方法的SQL语句。这通常在继承了AbstractMethod的类中完成,例如DorisGenericQuery。
1 2 3 4 5 6 7 8 public class DorisGenericQuery extends AbstractMethod { @Override public MappedStatement injectMappedStatement (Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { String sql = getSQLString(tableInfo); SqlSource sqlSource = languageDriver.createSqlSource(this .configuration, sql, modelClass); return this .addSelectMappedStatementForOther(mapperClass,"BigDataSelect" , sqlSource, Map.class); } }
这里注入的是 返回一个List<Map<String,Object>>的方法。
SQL注意用<script> .... </script>包含需要执行的语句
官方分页selectMapPage 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <script > <if test ="ew != null and ew.sqlFirst != null" > ${ew.sqlFirst} </if > SELECT <choose > <when test ="ew != null and ew.sqlSelect != null" > ${ew.sqlSelect} </when > <otherwise > p_id_card,tag_code,tag_id,out_date,tag_value,p_id,tag_name,tag_time</otherwise > </choose > FROM tbl_p_tags <if test ="ew != null" > <where > <if test ="ew.entity != null" > <if test ="ew.entity.pIdCard != null" > p_id_card=#{ew.entity.pIdCard}</if > <if test ="ew.entity['tagCode'] != null" > AND tag_code=#{ew.entity.tagCode}</if > <if test ="ew.entity['tagId'] != null" > AND tag_id=#{ew.entity.tagId}</if > <if test ="ew.entity['outDate'] != null" > AND out_date=#{ew.entity.outDate}</if > <if test ="ew.entity['tagValue'] != null" > AND tag_value=#{ew.entity.tagValue}</if > <if test ="ew.entity['pId'] != null" > AND p_id=#{ew.entity.pId}</if > <if test ="ew.entity['tagName'] != null" > AND tag_name=#{ew.entity.tagName}</if > <if test ="ew.entity['tagTime'] != null" > AND tag_time=#{ew.entity.tagTime}</if > </if > <if test ="ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfWhere" > <if test ="ew.nonEmptyOfEntity and ew.nonEmptyOfNormal" > AND</if > ${ew.sqlSegment}</if > </where > <if test ="ew.sqlSegment != null and ew.sqlSegment != '' and ew.emptyOfWhere" > ${ew.sqlSegment} </if > </if > <if test ="ew != null and ew.sqlComment != null" > ${ew.sqlComment} </if > </script >
2. 注册自定义方法 接下来,你需要创建一个类来继承DefaultSqlInjector,并重写getMethodList方法来注册你的自定义方法。
1 2 3 4 5 6 7 8 9 10 @Component public class GenericQuerySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList (Class<?> mapperClass, TableInfo tableInfo) { List<AbstractMethod> methodList = super .getMethodList(mapperClass,tableInfo); methodList.add(new DorisGenericQuery ()); methodList.add(new DorisGenericPageQuery ()); return methodList; } }
3. 定义BaseMapper 1 2 3 4 public interface BigDataQueryBaseMapper <T> extends BaseMapper <T> { List<Map<String, Object>> BigDataSelect (@Param("pt") T po) ; <P extends IPage <Map<String, Object>>> P BigDataSelectPage (P page, @Param("pt") T po) ; }
这里我用我基于实体来做分析处理,没有传入查询参数,如果需要传入加入 @Param(Constants.WRAPPER) Wrapper<T> queryWrapper
4. 配置SqlInjector 使用@Component注入 在DefaultSqlInjector类上添加@Component
在 aplication.properties 中配置 1 mybatis-plus.global-config.sql-injector=com.example.MyLogicSqlInjector
在 application.yml 中配置 1 2 3 mybatis-plus: global-config: sql-injector: com.example.MyLogicSqlInjector
在组装动态属性返回时报错
Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Error SQL: select p_id_card,
map[?] AS ? from (select p_id_card,map_agg(tag_code,tag_value) AS map from tbl_p_tags where tag_id in ( ? , ? , ? , ?
) group by p_id_card) t at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-5.3.24.jar:5.3.24] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.24.jar:5.3.24] at javax.servlet.ht
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public MappedStatement injectMappedStatement (Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { StringBuilder selectStr = new StringBuilder ("<script> select " ); selectStr.append(tableInfo.getKeySqlSelect()).append("," ); StringBuilder sqlScript = new StringBuilder ("map[#{item.code}] AS #{item.code}" ); String selectRules = SqlScriptUtils.convertForeach(sqlScript.toString(), "rules" , null , "item" , "," ); selectStr.append(selectRules); StringBuilder fromStr = new StringBuilder (" from (select " ); fromStr.append(tableInfo.getKeySqlSelect()).append("," ); fromStr.append("map_agg(tag_code,tag_value) AS map " ).append(NEWLINE); fromStr.append("from " ).append(tableInfo.getTableName()).append(NEWLINE);; fromStr.append("where " ).append("tag_id in (" ); String subRuleS = SqlScriptUtils.convertForeach("#{subItem.tagId}" , "item.selectedTags" , (String)null , "subItem" , "," ); String rules = SqlScriptUtils.convertForeach(subRuleS, "rules" , null , "item" , "," ); fromStr.append(rules).append(")" ).append(NEWLINE) .append("group by " ) .append(tableInfo.getKeySqlSelect()).append(") t" ) .append("</script>" ); String sql = selectStr.append(fromStr).toString(); System.out.println(selectStr.append(fromStr)); SqlSource sqlSource = languageDriver.createSqlSource(this .configuration, sql, modelClass); return this .addSelectMappedStatementForOther(mapperClass,"BigDataSelectPage" , sqlSource, Map.class); }
1 StringBuilder sqlScript = new StringBuilder ("map[#{item.code}] AS #{item.code}" );
中使用别名会报错如上
改成${}后正常
1 StringBuilder sqlScript = new StringBuilder ("map[#{item.code}] AS ${item.code}" );
在MyBatis中,使用#{}语法是为了防止SQL注入并进行预编译处理参数。然而,在映射文件中直接将#{}用作动态Map的键或别名的表达方式是不正确的。这是因为MyBatis期望#{}内部直接引用一个参数的名称,而不是作为动态字符串处理。