一、多數(shù)據(jù)源問題
主要思路是把dataSource、sqlSesstionFactory、MapperScannerConfigurer在配置中區(qū)分開,各Mapper對應的包名、類名區(qū)分開
1 <?xml version=“1.0” encoding=“UTF-8”?>
2
3 xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:aop=“http://www.springframework.org/schema/aop”
4 xmlns:tx=“http://www.springframework.org/schema/tx” xmlns:jdbc=“http://www.springframework.org/schema/jdbc”
5 xmlns:context=“http://www.springframework.org/schema/context”
6 xsi:schemaLocation=“
7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
8 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
9 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
10 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
11 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd”
12 default-autowire=“byName”>
13
14
15 destroy-method=“dispose”>
16
17
18
19
20
21
22
23
24
25
26
27 destroy-method=“dispose”>
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
上面的配置,一個連h2的a數(shù)據(jù)庫,一個連h2的b數(shù)據(jù)庫,至于事務管理器,大家可參考這個思路,建二個,各管各的。
項目中mapper接口及映射文件均用包名區(qū)分開,如下圖:
二、如何使用Map做為參數(shù)及動態(tài)條件生成
1
2
3
4
5
6 。..
7
8
9
10
11 D_RECID, D_USER_NAME, D_NAME, D_TYPE, 。..
12
13
14
14-31演示了如何使用Map做為參數(shù),動態(tài)傳入查詢條件,及List參數(shù)生成in(。..)條件
java端代碼示例:
1 PrintLayoutMapper mapper = context.getBean(PrintLayoutMapper.class);
2
3 Map
4 map.put(“userName”, “ADMIN”);
5 map.put(“awbType”, “CARGOLABEL_MU”);
6 map.put(“recId”, 1);
7
8 List
9 ids.add(0, 1);
10 ids.add(0, 2);
11 ids.add(0, 3);
12
13 map.put(“ids”, ids);
14
15 List<?> list = mapper.select(map);
其實PrintLayoutMapper接口的定義為:
1 public interface PrintLayoutMapper {
2 。..
3
4 List
5 }
最終生成的SQL語句為:
1 select D_RECID, D_USER_NAME, D_NAME, D_TYPE, 。.. from T_PRINT_LAYOUT where D_USER_NAME = ? and D_TYPE = ? and D_RECID = ? or D_RECID in ( ? , ? , ? )
三、兼容不同的數(shù)據(jù)庫
1
2
3
4 select seq_users.nextval from dual
5
6
7 select nextval for seq_users from sysibm.sysdummy1“
8
9
10 insert into users values (#{id}, #{name})
11
這是官方文檔上的示例,演示了如何兼容oracle與db2這二種不同的數(shù)據(jù)庫,來獲取序列的下一個值
四、加強版的分支、選擇判斷
1
這也是官方文檔上的示例,因為
五、避免Where 空條件的尷尬
1
2 SELECT * FROM BLOG
3 WHERE
4
5 state = #{state}
6
7
如果state參數(shù)為空時,最終生成SQL語句為
1 SELECT * FROM BLOG
2 WHERE
執(zhí)行會出錯,當然,你可以在where 后加一個1=1,改成
1
2 SELECT * FROM BLOG
3 WHERE 1=1
4
5 and state = #{state}
6
7
但是這個做法不太“環(huán)?!保ó吘挂肓艘粋€垃圾條件),其實只要改成《where>。..《/where>即可
1
2 SELECT * FROM BLOG
3
4
5 and state = #{state}
6
7
8
六、$與#的區(qū)別
1 select * from T_PRINT_LAYOUT where D_RECID = ${recId}
最后生成的SQL為:
1 select * from T_PRINT_LAYOUT where D_RECID = 1
即:直接將參數(shù)值替換到了原來${recId}的位置,相當于硬拼SQL
1 select * from T_PRINT_LAYOUT where D_RECID = #{recid,jdbcType=DECIMAL}
最后生成的SQL為:
1 select * from T_PRINT_LAYOUT where D_RECID = ?
即:#{。..}被識別為一個SQL參數(shù)
七、大量數(shù)據(jù)的批量insert
大量數(shù)據(jù)(條數(shù)>10000)做insert時,如果按常規(guī)方式,每條insert into table(。..) values(。..);來提交,速度巨慢。改善性能的思路是多條insert批量提交。
oracle環(huán)境中,有一種批量insert的小技巧,原理是 insert into 。.. select from 。..,套在mybatis上,變形為:
1 INSERT INTO T_TEST
2 (ID, COL_A, COL_B)
3 SELECT SEQ_TEST.NEXTVAL, A.*
4 FROM (
5 SELECT ‘A1’, ‘B1’ FROM DUAL
6 UNION ALL SELECT ‘A2’, ‘B2’ FROM DUAL
7 UNION ALL SELECT ‘A3’, ‘B3’ FROM DUAL
8 UNION ALL SELECT ‘A4’, ‘B4’ FROM DUAL
9 UNION ALL SELECT ‘A5’, ‘B5’ FROM DUAL
10 UNION ALL SELECT ‘A6’, ‘B6’ FROM DUAL
11 ) A
中間的部分非常有規(guī)律,可以用foreach標簽生成,參考下面的片段:
1
2
3 select SEQ_CTAS_SHARK_FLT.nextval as recId from dual
4
5 insert into CTAS_SHARK_FLT (《include refid=”Base_Column_List“/>) SELECT SEQ_TEST.NEXTVAL, A.*
6 FROM (
7
8 select #{item.awbType,jdbcType=VARCHAR}, #{item.awbPre,jdbcType=VARCHAR},。.. from dual
9
10 ) A
11
即使這樣,也不能直接run,oracle中一次執(zhí)行的sql語句長度是有限制的,如果最后拼出來的sql字符串過長,會導致執(zhí)行失敗,所以java端還要做一個分段處理,參考下面的處理:
1 List
2 for (TSharkFlt f : sharkFlts) {
3 data.add(getSharkFlt(f));
4 }
5
6 System.out.println(data.size());
7
8 long beginTime = System.currentTimeMillis();
9 System.out.println(”開始插入。..“);
10 SqlSessionFactory sqlSessionFactory = ctx.getBean(SqlSessionFactory.class);
11 SqlSession session = null;
12 try {
13 session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
14 int a = 2000;//每次提交2000條
15 int loop = (int) Math.ceil(data.size() / (double) a);
16
17 List
18 int start, stop;
19 for (int i = 0; i 《 loop; i++) {
20 tempList.clear();
21 start = i * a;
22 stop = Math.min(i * a + a - 1, data.size() - 1);
23 System.out.println(”range:“ + start + ” - “ + stop);
24 for (int j = start; j 《= stop; j++) {
25 tempList.add(data.get(j));
26 }
27 session.insert(”ctas.importer.writer.mybatis.mappper.SharkFltMapper.insertBatch2“, tempList);
28 session.commit();
29 session.clearCache();
30 System.out.println(”已經(jīng)插入“ + (stop + 1) + ” 條“);
31 }
32 } catch (Exception e) {
33 e.printStackTrace();
34 session.rollback();
35 } finally {
36 if (session != null) {
37 session.close();
38 }
39 }
40 long endTime = System.currentTimeMillis();
41 System.out.println(”插入完成,耗時 “ + (endTime - beginTime) + ” 毫秒!“);
13,27-29這幾行是關鍵,這一段邏輯會經(jīng)常使用,為了重用,可以封裝一下:
1 /**
2 * 批量提交數(shù)據(jù)
3 * @param sqlSessionFactory
4 * @param mybatisSQLId SQL語句在Mapper XML文件中的ID
5 * @param commitCountEveryTime 每次提交的記錄數(shù)
6 * @param list 要提交的數(shù)據(jù)列表
7 * @param logger 日志記錄器
8 */
9 private 《T> void batchCommit(SqlSessionFactory sqlSessionFactory, String mybatisSQLId, int commitCountEveryTime, List《T> list, Logger logger) {
10 SqlSession session = null;
11 try {
12 session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
13 int commitCount = (int) Math.ceil(list.size() / (double) commitCountEveryTime);
14 List《T> tempList = new ArrayList《T>(commitCountEveryTime);
15 int start, stop;
16 Long startTime = System.currentTimeMillis();
17 for (int i = 0; i 《 commitCount; i++) {
18 tempList.clear();
19 start = i * commitCountEveryTime;
20 stop = Math.min(i * commitCountEveryTime + commitCountEveryTime - 1, list.size() - 1);
21 for (int j = start; j 《= stop; j++) {
22 tempList.add(list.get(j));
23 }
24 session.insert(mybatisSQLId, tempList);
25 session.commit();
26 session.clearCache();
27 }
28 Long endTime = System.currentTimeMillis();
29 logger.debug(”batchCommit耗時:“ + (endTime - startTime) + ”毫秒“);
30 } catch (Exception e) {
31 logger.error(”batchCommit error!“, e);
32 e.printStackTrace();
33 session.rollback();
34 } finally {
35 if (session != null) {
36 session.close();
37 }
38 }
39 }
對應的,如果是批量update,也是類似的思路,只不過要注意一點:oracle環(huán)境中,多條語句提交的sql語句為
begin
update xxx set xxx =xxx ;
update xxx set xxx =xxx;
end;
用mytais拼的時候,參考下面的寫法:
1
2
3 update xxx set x=#{item.x,jdbcType=VARCHAR} where x =#{item.x,jdbcType=VARCHAR};
4
5
關于批量提交的性能,Oracle環(huán)境下,我大概測試了一下:
insert into 。.. select xxx
union all select yyy
union all select zzz;
最快,其次是
begin
insert into 。.. values 。..;
insert into 。.. values 。..;
end;
當然最慢是逐條insert提交,最后談下Spring與mybatis集成后,AOP事務管理 對 批量提交的影響 ,通常情況下,我們會這樣配置AOP事務管理:
1
2
3
4
5
6
7
8
9
10
11
這樣,ctas.service(及子包)下的所有方法都被攔截,而且只有do開頭的方法,具有可寫的事務(即:能insert/update/delete記錄),而其它方法是只讀事務(即:只能select數(shù)據(jù)),但是我們前面談到的批量提交操作,都是寫代碼手動提交的,不需要spring管理,所以配置中需要將某些方法排除,可以約定self開頭的方法,由開發(fā)者自己管理事務,不需要spring代為管理,上面的配置要改成:
1
2
3
4
通過 and !execution(。..) 將self開頭的方法排除就可以了,前面的批量操作代碼寫到selfXXX方法中。
關于批量提交,還有一種情況:父子表的批量插入。思路還是一樣的,但是SQL的寫法有點區(qū)別,原理參考下面的語句(Oracle環(huán)境)
1 DECLARE
2 BASE_ID INTEGER;
3 DETAIL_ID INTEGER;
4 BEGIN
5 --第1組記錄
6 SELECT SEQ_T_BASE.NEXTVAL INTO BASE_ID FROM DUAL;
7 INSERT INTO T_BASE (ID, FEE) VALUES (BASE_ID, ?);
8
9 SELECT SEQ_T_DETAIL.NEXTVAL INTO DETAIL_ID FROM DUAL;
10 INSERT INTO T_DETAIL (ID, BASE_ID, FEE) VALUES (DETAIL_ID, BASE_ID, ?);
11 SELECT SEQ_T_DETAIL.NEXTVAL INTO DETAIL_ID FROM DUAL;
12 INSERT INTO T_DETAIL (ID, BASE_ID, FEE) VALUES (DETAIL_ID, BASE_ID, ?);
13
14 --第2組記錄
15 SELECT SEQ_T_BASE.NEXTVAL INTO BASE_ID FROM DUAL;
16 INSERT INTO T_BASE (ID, FEE) VALUES (BASE_ID, ?);
17
18 SELECT SEQ_T_DETAIL.NEXTVAL INTO DETAIL_ID FROM DUAL;
19 INSERT INTO T_DETAIL (ID, BASE_ID, FEE) VALUES (DETAIL_ID, BASE_ID, ?);
20 SELECT SEQ_T_DETAIL.NEXTVAL INTO DETAIL_ID FROM DUAL;
21 INSERT INTO T_DETAIL (ID, BASE_ID, FEE) VALUES (DETAIL_ID, BASE_ID, ?);
22
23 --。..
24 END;
xml映射文件中的寫法:
1
2 DECLARE
3 base_id INTEGER ;
4 detail_id INTEGER ;
5
6 select seq_t_base.nextval into base_id from dual;
7 insert into t_base(id, fee) values(base_id, #{item.baseEntity.fee,jdbcType=DECIMAL});
8
9 select seq_t_detail.nextval into detail_id from dual;
10 insert into t_detail(id, base_id, fee) values(detail_id,base_id,#{detail.fee,jdbcType=DECIMAL});
11
12
13
List中的Dto定義
1 public class BaseDetailDto {
2
3 private TBase baseEntity;
4
5 private List
6
7 public TBase getBaseEntity() {
8 return baseEntity;
9 }
10
11 public void setBaseEntity(TBase baseEntity) {
12 this.baseEntity = baseEntity;
13 }
14
15
16 public List
17 return details;
18 }
19
20 public void setDetails(List《TDetail> details) {
21 this.details = details;
22 }
23 }
評論
查看更多