mybatis使用经验小结

编程实验

72人已加入

描述

  一、多数据源问题

  主要思路是把dataSource、sqlSesstionFactory、MapperScannerConfigurer在配置中区分开,各Mapper对应的包名、类名区分开

  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 《/property>

  40 《/property>

  41 《/property>

  42

  43

  44

  45

  46 《/property>

  47 《/property>

  48 《/property>

  49

  50

  51

  52 《/property>

  53

  54

  55

  56

  57

  58

  59

  60

  61

  62

  上面的配置,一个连h2的a数据库,一个连h2的b数据库,至于事务管理器,大家可参考这个思路,建二个,各管各的。

  项目中mapper接口及映射文件均用包名区分开,如下图:

  mybatis

  二、如何使用Map做为参数及动态条件生成

  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做为参数,动态传入查询条件,及List参数生成in(。..)条件

  java端代码示例:

  1 PrintLayoutMapper mapper = context.getBean(PrintLayoutMapper.class);

  2

  3 Map map = new HashMap《String, Object>();

  4 map.put(“userName”, “ADMIN”);

  5 map.put(“awbType”, “CARGOLABEL_MU”);

  6 map.put(“recId”, 1);

  7

  8 List ids = new ArrayList《Integer>();

  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 select(Map《String, Object> map);

  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 ( ? , ? , ? )

  三、兼容不同的数据库

  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这二种不同的数据库,来获取序列的下一个值

  四、加强版的分支、选择判断

  1

  这也是官方文档上的示例,因为。..并没对应的标签,所以要达到。..。.. 的效果,得借助组合使用。

  五、避免Where 空条件的尴尬

  1

  如果state参数为空时,最终生成SQL语句为

  1 SELECT * FROM BLOG

  2 WHERE

  执行会出错,当然,你可以在where 后加一个1=1,改成

  1

  但是这个做法不太“环保”(毕竟引入了一个垃圾条件),其实只要改成《where>。..《/where>即可

  1

  六、$与#的区别

  1 select * from T_PRINT_LAYOUT where D_RECID = ${recId}

  最后生成的SQL为:

  1 select * from T_PRINT_LAYOUT where D_RECID = 1

  即:直接将参数值替换到了原来${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参数

  七、大量数据的批量insert

  大量数据(条数>10000)做insert时,如果按常规方式,每条insert into table(。..) values(。..);来提交,速度巨慢。改善性能的思路是多条insert批量提交。

  oracle环境中,有一种批量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

  中间的部分非常有规律,可以用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中一次执行的sql语句长度是有限制的,如果最后拼出来的sql字符串过长,会导致执行失败,所以java端还要做一个分段处理,参考下面的处理:

  1 List data = new ArrayList《SharkFlt>();

  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 tempList = new ArrayList《SharkFlt>(a);

  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(”已经插入“ + (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这几行是关键,这一段逻辑会经常使用,为了重用,可以封装一下:

  1 /**

  2 * 批量提交数据

  3 * @param sqlSessionFactory

  4 * @param mybatisSQLId SQL语句在Mapper XML文件中的ID

  5 * @param commitCountEveryTime 每次提交的记录数

  6 * @param list 要提交的数据列表

  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环境中,多条语句提交的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环境下,我大概测试了一下:

  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数据),但是我们前面谈到的批量提交操作,都是写代码手动提交的,不需要spring管理,所以配置中需要将某些方法排除,可以约定self开头的方法,由开发者自己管理事务,不需要spring代为管理,上面的配置要改成:

  1

  2

  3

  4

  通过 and !execution(。..) 将self开头的方法排除就可以了,前面的批量操作代码写到selfXXX方法中。

  关于批量提交,还有一种情况:父子表的批量插入。思路还是一样的,但是SQL的写法有点区别,原理参考下面的语句(Oracle环境)

  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 details;

  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 getDetails() {

  17 return details;

  18 }

  19

  20 public void setDetails(List《TDetail> details) {

  21 this.details = details;

  22 }

  23 }

mybatis

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分