tddl相关
https://www.programcreek.com/java-api-examples/?code=loye168/tddl5/tddl5-master/tddl-group
https://github.com/ninqing/hermes/wiki/Tddl_Rule
前言
本例是用apply_no为分表分库键
mybatis中:
- #{ }是预编译处理,MyBatis在处理#{ }时,它会将sql中的#{ }替换为?,然后调用PreparedStatement的set方法来赋值;
- ${ }是字符串替换, MyBatis在处理${ }时,它会将sql中的${ }替换为变量的值。
mapper.xml文件中的方法先被mybatis先处理: # 转为占位符 ?, $ 则是直接注入值, 再被TDDL来处理。
经过验证: 一定要是“APPLY_NO=${applyNo}”这样的写法!
int insertBatch(@Param("applyNo") String applyNo, @Param("list")List<DecisionExecNode> list);
<insert id="insertBatch" parameterType="java.util.List" >
/*+TDDL({type:'condition',params:[{"paramtype":"long","expr":["APPLY_NO=${applyNo}"]}],vtab:'decision_exec_node'})*/
insert into decision_exec_node (
id,
apply_no,
case_no,
product_code
)
values
<foreach collection="list" item="item" separator=",">
( #{item.id,jdbcType=BIGINT},
#{item.applyNo,jdbcType=VARCHAR},
#{item.caseNo,jdbcType=VARCHAR},
#{item.productCode,jdbcType=VARCHAR} )
</foreach>
</insert>
- expr: 参数表达式,本例写的是TDDL分表键
- vtab: 表名
- /*+tddl_group({groupIndex:0,failRetry:true})*/ 可以强制指定数据库。 groupIndex: 标识同一个group下的多个dataSource(读&写库等)
在tddl-client 3.3.2.4 中的 SimpleHintParser 最末端有个测试用例, 对于使用格式在此类中处理!
- SimpleHintParser: 在TPreparedStatementImp路由获取真实的TGroupDataSource之前执行 (选择Group)
- GroupHintParser: 在TGroupPreparedStatement获取真实Connection之前执行 (选择Group下强制指定库druid)
在网上找到一些关于SimpleHintParser的使用事例: https://www.programcreek.com/java-api-examples/?api=com.taobao.tddl.optimizer.parse.hint.SimpleHintParser。没试过每一个用例,权当解决问题的一种思路。
验证过程
具体可以关注SimpleHintParser.convertHint2RouteCondition方法中的入参StartInfo对象的属性sql和sqlParam 在不同hint写法下构建tddlHint是有区别的。会取第一个param来注入
分表规则:
以下的方法是: SimpleHintParser.decodeComparative
错误1: "expr":["APPLY_NO=#applyNo#"]
这种会直接按字符串“#applyNo#”进行路由分表,导致数据分表不一致!!
错误2: "expr":["APPLY_NO=?"]
这种会直接按取第一个占位符指定的参数值来填充。 但比较奇怪的是拿不到传入的“applyNo”, 只会拿到“List”对象里的第一个param 填充(applyNo像被丢了一样,推测是因为在mapper文件方法体内并没有对其进行直接使用,mybatis解析时会过滤多余的属性值), 导致最后因填充参数不足插入失败
错误3:"expr":["APPLY_NO=#{applyNo,jdbcType=VARCHAR}"]
这种本来应该是最接近真相的方式,这种方式可以读取到额外传入的“applyNo” 的属性值, 但比较奇怪的是在SimpleHintParser处理时调用的HintParserHelper.extractHint方法会判定这个参数是否是“setString”, 会给真实值前后加入了单引号! 这个导致VirtualTableRuleMatcher.findMatchedRule的入参map值与正常处理情景下的不一致! 最终导致会在WrappedGroovyRule执行eval方法路由的时候异常!
最终得到的对象是:
但正常情况下应该是:
异常时的入参却是:
那能不能直接传递hashCode进去呢? 最后验证发现然并软。。分表规则是先转成String再hash, 还是不行。。
正确方法:“APPLY_NO=${applyNo}”
根据推测: mybatis对于“#”会转为占位符,那直接在拼接sql的时候拼入分库分表键值,跳过SimpleHintParser中对String类型参数的额外补单引号动作!
这下正常了:
GroovyRule
相关源码
SimpleHintParser
在TPreparedStatementImp执行executeQuery(其他方法类似)时,通过buildSqlExecutionContextUsePipeline来获取真实的group信息。
GroupHintParser
我们来看TGroupPreparedStatement执行的executeQuery(其他方法类似):会通过TConnection获取真实的TGroupConnection。
此时将通过GroupHintParser来处理sql获取指定的GroupIndex
这个GroupIndex用在AbstractDBSelector的tryExecute方法中选择当前Group下匹配的DataSourceHolder
RuleLePipelineFactory
处理链路handler