某日,同事說批量插入失敗,異常如下
2018-03-07 20:54:58,262 ERROR [][CardOperationVerifyServiceImpl.java:95] : 添加申請記錄出現異常
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter '__frch_item_0' not found. Available parameters are [list, param1]
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:76)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:399)
at com.sun.proxy.$Proxy17.insert(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:253)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:52)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
at com.sun.proxy.$Proxy29.batchCardOperationRecord(Unknown Source)
at com.shitu.cloudCard.service.impl.CardOperationVerifyServiceImpl.applyMoneyUp(CardOperationVerifyServiceImpl.java:91)
at com.shitu.service.CardOperationVerifyTest.applyMoneyUpTest(CardOperationVerifyTest.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.apache.ibatis.binding.BindingException: Parameter '__frch_item_0' not found. Available parameters are [list, param1]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:168)
at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:45)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)
at org.apache.ibatis.reflection.MetaObject.metaObjectForProperty(MetaObject.java:145)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:115)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:79)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:85)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:63)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:77)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:48)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:115)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.ibatis.plugin.Invocation.proceed(Invocation.java:49)
at com.shitu.cloudCard.interceptor.SqlInterceptor.intercept(SqlInterceptor.java:137)
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
at com.sun.proxy.$Proxy40.update(Unknown Source)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:170)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:157)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:386)
... 35 more
這是mybatis的常見異常,隨即查詢xml里的sql語句,多次測試,無問題。困惑。。。
將批量插入改成單筆插入,插入數據庫成功。
懷疑是mycat的問題,搜索之。
SELECT:
跨分片(實體庫)的交叉查詢
跨節點的聯合查詢 (如用戶庫的表和平臺庫的表做聯合查詢)
INSERT:
插入的字段不包含分片字段 (如插入tbl_user_base_info表,沒有提供user_id列)
插入的分片字段找不到對應分片
復制插入Insert into…select…
多行插入insert into tab_a(c1,c2) values(v1,v2),(v11,v21)…
UPDATE:
更新的列包含分片列
多表更新update a, b set a.nation=’China’, b.pwd=’123456’ where a.id=b.id
復雜更新update a, b set a.nation=’China’ where a.id=b.id; 但支持子查詢方式update a set a.nation=’China’ where id in (select id from b);
DELETE:
復雜刪除 delete a from a join b on a.id=b.id;
支持子查詢方式delete from a where a.id in (select id from b), 但表不能起別名
其它:
Call procedure() MyCat未支持存儲過程定義, 因而不允許調用存儲過程,但可通過注解來調用各個分片上的存儲過程
Select func(); 不支持這種方式直接調用自定義函數,但支持select id, func() from employee,只需employee所在的所有分片上存在這個函數。MySql自帶函數可隨意使用。
注意事項:
Order by字段必須出現在select中(MyCat先將結果取出,然后排序)
Group by務必使用標準語法select count(1),type from tab_a group by type;
MyCat的一些自帶函數sum,min,max等可以正確使用,但多分片執行的avg有bug,執行的結果是錯誤的謹慎使用子查詢,外層查詢沒有分片查詢條件,則會在所有分片上執行(子查詢內外層的表一樣較為特殊)
隨即將mybatis中的#改成$,問題解決。
動態 SQL 是 mybatis 的強大特性之一,也是它優于其他 ORM 框架的一個重要原因。mybatis 在對 sql 語句進行預編譯之前,會對 sql 進行動態解析,解析為一個 BoundSql 對象,也是在此處對動態 SQL 進行處理的。在動態 SQL 解析階段, #{ } 和 ${ } 會有不同的表現
select * from user where name = #{name};
#{} 在動態解析的時候, 會解析成一個參數標記符。就是解析之后的語句是:
select * from user where name = ?;
那么我們使用 ${}的時候
select * from user where name = ${name};
${}在動態解析的時候,會將我們傳入的參數當做String字符串填充到我們的語句中,就會變成下面的語句
select * from user where name = "dato";
預編譯之前的 SQL 語句已經不包含變量了,完全已經是常量數據了。相當于我們普通沒有變量的sql了。
綜上所得, ${ } 變量的替換階段是在動態 SQL 解析階段,而 #{ }變量的替換是在 DBMS 中。
這是 #{} 和 ${} 我們能看到的主要的區別,除此之外,還有以下區別:
#方式能夠很大程度防止sql注入。
$方式無法防止Sql注入。
$方式一般用于傳入數據庫對象,例如傳入表名.
一般能用#的就別用$.