tddl 支持批量插入的方法

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中:

  1. #{  }是预编译处理,MyBatis在处理#{  }时,它会将sql中的#{  }替换为?,然后调用PreparedStatement的set方法来赋值;
  2. ${  }是字符串替换, 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(读&写库等)
    d472d250fb84cbcf8cfc8740427fda31daa.jpg

在tddl-client  3.3.2.4 中的 SimpleHintParser 最末端有个测试用例, 对于使用格式在此类中处理! 

007e50919d6f538732fe5d8b3b6b3c92153.jpg

  • 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来注入

分表规则: 
8aa92e25895964ecdd6ecb81452ffeec16e.jpg
以下的方法是:  SimpleHintParser.decodeComparative

错误1: "expr":["APPLY_NO=#applyNo#"] 

这种会直接按字符串“#applyNo#”进行路由分表,导致数据分表不一致!!
4e2e1c626920b8f840f196f34680d4fb2b1.jpg

错误2: "expr":["APPLY_NO=?"]

这种会直接按取第一个占位符指定的参数值来填充。 但比较奇怪的是拿不到传入的“applyNo”, 只会拿到“List”对象里的第一个param 填充(applyNo像被丢了一样,推测是因为在mapper文件方法体内并没有对其进行直接使用,mybatis解析时会过滤多余的属性值), 导致最后因填充参数不足插入失败

7c6897a7ab52dc3fbe2e86ef1264bd87573.jpg

错误3:"expr":["APPLY_NO=#{applyNo,jdbcType=VARCHAR}"]  

这种本来应该是最接近真相的方式,这种方式可以读取到额外传入的“applyNo” 的属性值, 但比较奇怪的是在SimpleHintParser处理时调用的HintParserHelper.extractHint方法会判定这个参数是否是“setString”, 会给真实值前后加入了单引号! 这个导致VirtualTableRuleMatcher.findMatchedRule的入参map值与正常处理情景下的不一致! 最终导致会在WrappedGroovyRule执行eval方法路由的时候异常!

0b88e4d80f4b6c507767d65cfe415956f7b.jpg
最终得到的对象是:
d0ad7a6b8a227cf857796b31ac11a5eb807.jpg

但正常情况下应该是:
dd4d0bf650ffa1c00a4fdfc8850375ba123.jpg

异常时的入参却是:

c0f44ff5ea937e3023a496d6670b2351b0c.jpg

那能不能直接传递hashCode进去呢? 最后验证发现然并软。。分表规则是先转成String再hash, 还是不行。。

正确方法:APPLY_NO=${applyNo}

根据推测: mybatis对于“#”会转为占位符,那直接在拼接sql的时候拼入分库分表键值,跳过SimpleHintParser中对String类型参数的额外补单引号动作!

6e779eca1e1dff8bda5f01233bd500d027f.jpg

2e0a8226be527f95c2b3293238e06d9cc11.jpg

这下正常了:
GroovyRule

ae94101ce235ab575b48e5c56be64a0c0db.jpg

相关源码

SimpleHintParser

TPreparedStatementImp执行executeQuery(其他方法类似)时,通过buildSqlExecutionContextUsePipeline来获取真实的group信息。
3984a461dda5c167edb77e9ece33a7430eb.jpg
705f50c9ec107dba6e9e2b0b06407e9fcc1.jpg

GroupHintParser

我们来看TGroupPreparedStatement执行的executeQuery(其他方法类似):会通过TConnection获取真实的TGroupConnection。6281fcb1ef63f97c5696b602e8ab81dcb11.jpg

此时将通过GroupHintParser来处理sql获取指定的GroupIndex

b75c291d96ef14ba4eae91e93371fa75e5c.jpg

这个GroupIndex用在AbstractDBSelector的tryExecute方法中选择当前Group下匹配的DataSourceHolder

1ad52bad15830dbc7d173a1785e4a9b884a.jpg
f3d0fc4987d53e9bd8c032b8011847db626.jpg

RuleLePipelineFactory

处理链路handler

d0346d2bc3b7241565e73b9e6994e7fe229.jpg

 

转载于:https://my.oschina.net/u/3434392/blog/3025684

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值