mybatisplus总结

发布时间 2023-06-14 20:10:06作者: zhangyukun

mybatisplus 和 springboot 的集成

导入依赖

        <!--spring-boot-web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

        <!--mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

配置文件

## web ##
server:
  servlet:
    context-path: /
  port: 80

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://192.168.100.66:3306/hl?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=Hongkong&useSSL=false&allowPublicKeyRetrieval=true
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  mapper-locations: mapper/*.xml   #定义xml文件的位置
  type-aliases-package: com.lomi.entity  #定义实体文件的包

dao从层

public interface GoodsMapper extends BaseMapper<Goods> {

}

GoodsService层

public interface GoodsService  extends IService<Goods> {

	void add(Goods goods);


}

GoodsServiceImpl

@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements GoodsService  {

	@Resource
	GoodsExMapper goodsExMapper;

	@Override
	@Transactional
	public void add(Goods goods) {
		getBaseMapper().insert(goods);
	}


}
/**
 * 定义dao文件的的包
 */
@MapperScan("com.lomi.mapper") 
@SpringBootApplication
public class MybatisplusApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisplusApplication.class, args);
    }

}

GoodsMapper.xml可以不存在

用法和建议

mybatisPlus对dao层和Service都有一些默认封装,可以选择只用dao层,也可以选择dao和service层一起使用。

service的批量操作效率比拼装 foreach方式更高

mybatisPlus对dao层的封装

增删改查,分页,列表,查询数量,查询是否存在等接口

	@Override
	public void t1() {
		LambdaQueryWrapper<Goods> qw = Wrappers.lambdaQuery();
		Goods goods = Goods.randomGoods();
		Page page = new Page(1,4);


		//查询
		goodsExMapper.selectById(1668801996867579904L);
		//多个会抛出异常
		try{
			goodsExMapper.selectOne(qw);
		}catch (Exception e){
			log.warn("不止一个");
		}

		boolean exists = goodsExMapper.exists(qw);
		log.warn("是否存在{}", exists );

		Long count = goodsExMapper.selectCount(qw);
		log.warn("数量{}", count );

		List<Goods> list = goodsExMapper.selectList(qw);
		log.warn("不分页结果{}", JSONUtil.toJsonStr( list ));


		IPage selectPage = goodsExMapper.selectPage(page, qw);
		log.warn("分页结果{}", JSONUtil.toJsonStr( selectPage ));



		//添加
		goodsExMapper.insert(goods);


		//更新
		goodsExMapper.updateById( goods );
		log.warn("完成更新");


		//批量
		//批量查询(用的in,真批量,建议使用)
		List<Goods> list1 = goodsExMapper.selectBatchIds(Arrays.asList(1668805297105350656L, 1668805300435628032L));
		log.warn("批量查询{}",list1);

		//批量删除(用的in,真批量,建议使用)
		int i = goodsExMapper.deleteBatchIds(Arrays.asList(1668805297105350656L, 1668805300435628032L));
		log.warn("批量删除{}",i);


		//删除
		goodsExMapper.deleteById(1L);
		log.warn("删除Id是1的数据");
		//条件删除
		//goodsExMapper.delete(qw);
	}

mybatisPlus对service的封装

对比dao层的封装,提供里更多批量接口,提供了saveOrUpdate之类的接口,后面会体现提供的批量接口效率都很高

	@Override
	public void t1() {
		t2();
		add();
		get();
		up();
		delete();
		batch();
	}

	@Override
	@Transactional
	public void batchAdd(List<Goods> list) {
		saveBatch(list);
	}

	/**
	 * 获取当前的mapper
	 */
	private void t2() {
		GoodsMapper goodsMapper = getBaseMapper();
		log.debug( "goodsMapper:" + goodsMapper );
	}


	/**
	 * 添加
	 */
	private void add() {
		add( Goods.randomGoods() );
		save(Goods.randomGoods());
	}

	/**
	 * 查询
	 */
	private void get() {
		//用过Id查询
		Goods goods = getById(id);

		LambdaQueryWrapper<Goods> qw = Wrappers.lambdaQuery();
		qw.gt( Goods::getId,1L);
		//查询单个,多个会抛出异常
		//getOne(qw);

		//查询单个不抛出异常,得到第一个
		Goods one = getOne(qw, false);
		log.debug( "查询单个:" + one );


		//获取任意对象
		HashMap<String, Object> obj = (HashMap<String, Object>) getObj(qw, item -> {
			//item.get
			return BeanUtil.beanToMap( item );
		});
		log.debug( "查询对象封装:" + obj );


		//查询数量
		count();
		long count = count(qw);
		log.debug( "查询数量:" + count );


		//不分页查询列表
		list();
		list(qw);


		//分页查询1
		Page page = new Page(1,10);
		IPage pageList1 = page(page, qw);
		log.debug( "分页查询1:" + pageList1 );

		//分页查询2
		List<Goods> pageList2 = goodsExMapper.pageList(page);
		log.debug( "分页查询2:" + pageList2.getClass() );

	}

	Long id = 1668822727982657536L;

	/**
	 * 修改
	 */
	private void up() {
		Goods goods = getById(id);
		goods.setName("改");

		//添加或者更新
		saveOrUpdate(goods);


		//通过Id更新
		updateById(  goods );

		//查询条件更新
		LambdaUpdateWrapper<Goods> uw = Wrappers.lambdaUpdate(Goods.randomGoods());
		uw.eq( Goods::getId,1L);
		uw.set(Goods::getName,"uw名字");
		update(uw);

		//查询条件更新2
		LambdaQueryWrapper<Goods> qw = Wrappers.lambdaQuery();
		qw.eq( Goods::getId,1L);
		update(goods,qw);


	}


	/**
	 * 删除
	 */
	private void delete() {
		removeById(1L);
	}



	/**
	 * 批量接口
	 */
	private void batch() {
		//批量查询(这个用的底层的in批量查询)
		listByIds(Arrays.asList( 1L ) );


		//批量插入(1000条数据时,约比sql上拼装foreach块60倍)
		saveBatch(Arrays.asList( Goods.randomGoods(),Goods.randomGoods()  ));
		log.warn("批量插入----------------");


		//批量添加或者更新
		saveOrUpdateBatch(Arrays.asList( Goods.randomGoods(),Goods.randomGoods()  ));
		log.warn("批量插入或者更新----------------");

		//批量删除
		removeByIds( Arrays.asList( 1L,2L ) );
		log.warn("批量删除----------------");


		//批量更新
		updateBatchById( Arrays.asList( Goods.randomGoods(),Goods.randomGoods()  ) );
		log.warn("批量更新----------------");


		updateBatchById( Arrays.asList( Goods.randomGoods(),Goods.randomGoods()  ),1000 );
		log.warn("批量更新2----------------");
	}

mybatisplus不同版本的分页

不同版本启用分页插件
    /**
     * mybatisPlus 3.5以后 用这种用法
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }



    /*
     *
     * mybatis 3.3-3.4 这样用
     *
     * */
   /* @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setDbType(DbType.MYSQL);
        return paginationInterceptor;
    }*/


    /*
    *
    * mybatis 3.2以前使用这种用法
    *
    * */
    /*@Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor().setDialectType(DbType.MYSQL.getDb());
    }*/

使用分页的常见两种方式
		//直接使用mybatisplus的selectPage接口
		Page page = new Page(1,4);
		IPage selectPage = goodsExMapper.selectPage(page, qw);
		log.warn("分页结果{},{}", JSONUtil.toJsonStr( selectPage ),page.getTotal());

		//请求参数中带有Page对象就会分页
		Page page2 = new Page(1,4);
		List<Goods> list2 = goodsExMapper.pageList(page2);
		log.warn("分页结果2{},{}", JSONUtil.toJsonStr( list2 ),page2.getTotal());

批量service层批量接口和在sql上拼接foreach效率差异

结论service层批量插入约是sql上凭借foreach的60倍

foreach方式
    <insert id="addBatch">
        insert into goods (id,
        name,
        stock,
        des,
        des2,
        data,
        create_date)
        values
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.id,jdbcType=BIGINT},
            #{item.name,jdbcType=VARCHAR},
            #{item.stock,jdbcType=INTEGER},
            #{item.des,jdbcType=VARCHAR},
            #{item.des2,jdbcType=VARCHAR},
            #{item.data,jdbcType=LONGVARCHAR},
            #{item.createDate,jdbcType=TIMESTAMP})
        </foreach>
    </insert>

	@Override
	@Transactional
	public void batchAdd(List<Goods> list) {
		goodsExMapper.addBatch(list);
	}
使用mybatisplus service封装的方式
	@Override
	@Transactional
	public void batchAdd(List<Goods> list) {
		saveBatch(list);
	}

记录运行时间的测试代码
	/**
	 * 批量
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value="t3", method= {RequestMethod.GET})
	public String t3() throws Exception{

		List<Goods> list1 = new ArrayList<>();
		List<Goods> list2 = new ArrayList<>();

		Stream.generate(Goods::randomGoods ).limit(1000).forEach( item-> list1.add( item ) );
		Stream.generate(Goods::randomGoods ).limit(1000).forEach( item-> list2.add( item ) );



		Long time1 = System.currentTimeMillis();
		//foreach方式
		iGoodsService.batchAdd(list1);
		Long time2 = System.currentTimeMillis();

		//mybatisplus封装的批量插入
		goodsService.batchAdd(list2);
		Long time3 = System.currentTimeMillis();

		log.warn("dao层子写批量:{}", time2-time1);
		log.warn("mybatisplus——service封装的批量:{}",time3-time2 );


		return "OK";
	}
三次运行的结果分别是
2023-06-14 14:28:23.992  WARN 17156 --- [p-nio-80-exec-2] c.lomi.controller.MyBatisPlusController  : dao层子写批量:13187
2023-06-14 14:28:23.993  WARN 17156 --- [p-nio-80-exec-2] c.lomi.controller.MyBatisPlusController  : mybatisplus——service封装的批量:211



2023-06-14 14:29:00.470  WARN 17156 --- [p-nio-80-exec-9] c.lomi.controller.MyBatisPlusController  : dao层子写批量:12562
2023-06-14 14:29:00.470  WARN 17156 --- [p-nio-80-exec-9] c.lomi.controller.MyBatisPlusController  : mybatisplus——service封装的批量:183



2023-06-14 14:29:25.417  WARN 17156 --- [p-nio-80-exec-5] c.lomi.controller.MyBatisPlusController  : dao层子写批量:13079
2023-06-14 14:29:25.417  WARN 17156 --- [p-nio-80-exec-5] c.lomi.controller.MyBatisPlusController  : mybatisplus——service封装的批量:168


mybatisplus乐观锁

    /**
     * mybatisPlus 3.5以后 用这种用法
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        //乐观锁插件
        interceptor.addInnerInterceptor( new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

在实体上标记version

@Version
private Integer version;

然后再更新的时候就会带上 version的条件,如果version是null,那么乐观锁不会生效

	@Override
	public void updatebyVersion() {
		Goods goods = getById(1668873830610841600L);
		goods.setName("改1");

		log.warn("打印1,{},{}",goods.getVersion(),goods.getName());
		boolean rt1 = updateById( goods );
		log.warn("打印2,{},{}",goods.getVersion(),goods.getName());
		log.warn("更新结果{}",rt1);


		goods.setName("改2");

		log.warn("打印3,{},{}",goods.getVersion(),goods.getName());
		boolean rt2 = updateById(goods);
		log.warn("打印4,{},{}",goods.getVersion(),goods.getName());
		log.warn("更新结果{}",rt2);


		//删除的时候不会带上乐观锁
		removeById(goods);
	}

注解方式的sql

这不是mybatisplus的,是mybatis的语法,并且个人不推荐用注解形式的sql,xml 格式的sql 比 注解方式的sql 可读性搞很多,尽量写到xml里面去,对齐格式,方便阅读,个人建议优先使用 xml方式的sql,可以少量使用QueryWrapper,UpdateWrapper这种面向对象的方式,namedsql方式建议少使用

	//使用#{}获取参数
	@Select("select * from goods where id = #{id}")
	Goods querById_nameSql(@Param("id") Long id);


	@Update("update goods set name = #{param.name} where id = #{param.id}")
	int update_namedsql(@Param("param") Goods param);

	//参数有 Page对象就会分页
	@Select("select * from goods")
	List<Goods> queryPage_namedsql(@Param("name") String name,Page page);


	/**
	 * ${ew.sqlSelect} :查询字段的部分的sql,来自接口 Query 的getSqlSelect方法,查询语句才能用
	 * ${ew.sqlSet} set 部分的sql ,来自接口 Update的getSqlSet方法
	 * ${ew.sqlSegment} where后面的 sql ,来自接口 ISqlSegment的getSqlSegment方法
	 * Constants.WRAPPER里面的参数都是可以通过 ${}方式取到
	 */
	@Update("update goods set ${ew.sqlSet}   where ${ew.sqlSegment}")
	int updateParam_namedsql(@Param(Constants.WRAPPER) Wrapper<Goods> wp, Page page);

mybatisQueryWrapper or方式的查询

	/**
	 * 默认都说and
	 * 使用  and 或者 or 等于建立了一个子条件,同一层有多个条件会添加括号
	 */
	@Override
	public void orQuery() {

		//WHERE (name = ? OR name = ?)
		LambdaQueryWrapper<Goods> query1 = Wrappers.lambdaQuery();
		query1.eq( Goods::getName,"name1" );
		LambdaQueryWrapper<Goods> query12 = query1.or();
		query12.eq(Goods::getName,"name2");
		List<Goods> list = list(query1);


		// WHERE (name = ? AND (id = ? AND des = ? OR id = ? AND des = ?))
		//外面的条件
		LambdaQueryWrapper<Goods> query2 = Wrappers.lambdaQuery();
		query2.eq( Goods::getName,"name1" );
		LambdaQueryWrapper<Goods> and = query2.and(qw->{
			//子条件1
			qw.eq( Goods::getId,1 );
			qw.eq( Goods::getDes,"des1" );

			//子条件2
			LambdaQueryWrapper<Goods> qwor = qw.or();
			qwor.eq(  Goods::getId,2 );
			qwor.eq( Goods::getDes,"des2" );


		});
		List<Goods> list2 = list(query2);



		// WHERE (name = ? AND ((id = ? AND des = ?) OR (id = ? AND des = ?)))
		//外面的条件
		LambdaQueryWrapper<Goods> query3 = Wrappers.lambdaQuery();
		query3.eq( Goods::getName,"name1" );
		LambdaQueryWrapper<Goods> and3 = query3.and(qw->{
			//子条件1
			qw.and(item->{
				item.eq( Goods::getId,1 );
				item.eq( Goods::getDes,"des1" );
			});

			//子条件2
			LambdaQueryWrapper<Goods> qwor = qw.or();
			qwor.or(item->{
				item.eq(  Goods::getId,2 );
				item.eq( Goods::getDes,"des2" );
			});

		});
		List<Goods> list3 = list(query3);

	}