title: 玩转spring-boot-mybatis
date: 2019-03-11 19:36:57
type: "mybatis"
categories: mybatis #分类名
tags: mybatis
---
作为持久层的ORM框架,目前在国内主流之一就是MyBatis,学会用它,用好它肯定是必备的功课
我会主要从下面几个方面入整理本篇博客
小插曲,添加测试模块的时候,引入junit模块和spring-boot-text-starter模块有先顺序,不然ide会报错...
坐标
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/><!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--通用mapper启动器--> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.0.3</version> </dependency> <!--分页助手--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!--测试--> <!--先添加 junit 再添加下面的text stater--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>text</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
我现在用的云主机docker上的官方版mysql,版本比较新,因此我的调整版本到 8 以上,不然会报错说什么
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could
配置文件:
server: port: 8089 spring: application: name: text-mybatis datasource: url: jdbc:mysql://211.159.XXX.XXX:8888/changwu?serverTimezone=UTC&useUnijava=true&characterEncoding=utf-8&useSSL=false username: root password: 2424zcw.. driver-class-name: com.mysql.jdbc.Driver #输出sql mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
启动类
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import tk.mybatis.spring.annotation.MapperScan; @SpringBootApplication @MapperScan("com.changwu.mapper") public class MybatisApp { public static void main(String[] args) { SpringApplication.run(MybatisApp.class); } }
编写Mapper
import tk.mybatis.mapper.common.Mapper; public interface MyMapper extends Mapper<MyBrand> {}
ok,到现在环境就搭建好了
情景1: 对于实体类
标记他对应那张表
import javax.persistence.Table; @Table(name = "tb_brand")
情景2: 对于主键
大多数情况主键一般都叫id,bigint类型,没什么超级特殊的情况我们都希望他可以自己增长,下面两个注解都可以做到这件事,但是前提表id属性必须设置成 autoincreament , 不然报错说,id不能不写
import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; /* * 使用的是jpa的策略生成器 *JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO. * TABLE:使用一个特定的数据库表格来保存主键。 * SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列 * IDENTITY:主键由数据库自动生成(主要是自动增长型) * AUTO:主键由程序控制。 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
直接使用Mybatis的原生注解
@Id @KeySql(useGeneratedKeys = true) private Long id;
情景3:
我们都知道springMVC会把前端发送过来的json转化为我们的javaBean,因此我们的javaBean里面的属性名和前端发送的那个对象的属性名要意义对应的,这是规范!
设想,加入前端一顿收集数据,把商品的品牌信息和库存信息都发送过来了,只有关于品牌的javabean,数据接受不全怎么办? 没关系,下面的注解可以搞定
@Transient
情景4:
我们的pojo属性名,和数据库中的关键字重名怎么办?
@Column() 可以解决 数据库中的字段为数据库的关键字的问题
@Column(name="`numeric`") // private Boolean numeric; //是否是数字类型
情景5 :
数据表中tingint(1) 对应的javaBean的属性咋写?
tingint(1) // 它是boolean的同义词
情景5:
关于VO , 如何选择性的返回给前端javaBean的属性?就比如说我们查询有没有这个用户,总不至于把密码,私人信息一块返回给前端吧?
import com.fasterxml.jackson.annotation.JsonIgnore; @JsonIgnore
它是jackson的注解
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency>
下面就是介绍常用的mapper的API
//会有选择性的新增, 智能判断 brand里面有没有空的字段,有值,用这个值,没有值而使用数据库的默认值 insertSelective(brand) // 假如前端提交过来的 brand里面的数据是全的,用词方法 brandMapper.insert(brand);
/** * 根据实体中的属性值进行查询,查询条件使用等号 * @param record * @return */ @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL") List<T> select(T record); /** * 根据实体中的属性值进行查询,查询条件使用等号 * @param record * @return */ @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL") List<T> select(T record); /** * 查询全部结果 * @return */ @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL") List<T> selectAll(); /** * 根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号 * @param record * @return */ @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL") T selectOne(T record); /** * 根据实体中的属性查询总数,查询条件使用等号 * @param record * @return */ @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL") int selectCount(T record);
/** * 根据主键字段进行删除,方法参数必须包含完整的主键属性 * @param key * @return */ @DeleteProvider(type = BaseDeleteProvider.class, method = "dynamicSQL") int deleteByPrimaryKey(Object key); /** * 根据实体属性作为条件进行删除,查询条件使用等号 * @param record * @return */ @DeleteProvider(type = BaseDeleteProvider.class, method = "dynamicSQL") int delete(T record);
修改主要用到下面这个api
/** * 根据主键更新属性不为null的值 * @param record * @return */ @UpdateProvider(type = BaseUpdateProvider.class, method = "dynamicSQL") int updateByPrimaryKeySelective(T record);
看到这个方法有没有觉得很爽? 它根据主键,修改不为null的属性,意思就是说,我们把不需要修改的地方设置为null就好了
,这样看,前面需要我们一顿整,整啥呢? 结合实际的业务逻辑,把前端给交过来的数据最新的数据放到我们的bean中直接修改,把不需要修改的属性设置为null,有属性可能需要直接删除,有的属性可能要去别的表中查询(别忘了加上事务 @Transactional )
/** * 根据主键更新实体全部字段,null值会被更新 * @param record * @return */ @UpdateProvider(type = BaseUpdateProvider.class, method = "dynamicSQL") int updateByPrimaryKey(T record);
这个方法和上面的神似
API
mapper.selectByExample(example)
参照下面原生的sql.拼接查询条件
select * from 表名 where name like '% X %' or letter== 'x' order by id desc
逻辑
public VO queryBrandByPage(Integer page, String key, Integer rows, String sortBy, Boolean desc) { //分页 -- 使用分页助手,在我们真正查询之前,调用这个下面的方法,开启分页查询,他很智能,会用mybatis的拦截器 // 对接下来要执行查询的sql进行拦截,自动的在其后面拼接 limit语句 PageHelper.startPage(page,rows); //当前页码,每页显示的数目 //过滤 --key (StringUtils用的是comment lang3 下面的) // 过滤条件,key是前端用户传递进来的,可能仅仅是一个 小米, 也可能是 小 --- 模糊查询 /* select * from tb_brand where name like '% X %' or letter== 'x' order by id desc */ Example example = new Example(Brand.class); if(StringUtils.isNotBlank(key)){ //不为空,过滤 example.createCriteria().orLike("name","%"+key+"%").andEqualTo("letter",key.toUpperCase()); // example.createCriteria().orLike("name","%"+key+"%").or } //排序 if(StringUtils.isNotBlank(sortBy)) { //传递进来的排序不为空,设置我们的排序条件 // 上面的 order by 可以帮我们生成, 但是后面的 id desc 需要我们自己写 // String orderByClause = "id desc" ; 写死了 String orderByClause = sortBy + (desc ? " DESC " : " ASC "); //坑坑坑 注意要加上 空格 不然拼接完了 就是 orderBy idASC 而不是orderBy id ASC example.setOrderByClause(orderByClause); } //查询 获取到list ,其实是 当前页的数据 page对象 List<Brand> list = brandMapper.selectByExample(example); if (CollectionUtils.isEmpty(list)){ throw new Exception ; } // 解析List PageInfo<Brand> pageInfo = new PageInfo<>(list); return new PageResult<Brand>(pageInfo.getTotal(),list); }
Mybatis只能针对单表为我们生成sql,如果我们的需求是跨表操作,比如说涉及到两张表,我们就得去mapper里面自己写sql
原生sql
select * from tb_brand b inner join tb_category_brand cb on b.id=cb.brand_id -- 去笛卡尔积 where cb.category_id= ? ;
mapper
@Select("select * from tb_brand b inner join tb_category_brand cb on b.id=cb.brand_id where cb.category_id=#{cid}") List<Brand> queryBrandByCid(@Param("cid") Long cid);
注意他的包啊!
import tk.mybatis.mapper.additional.idlist.IdListMapper; public interface Mapper extends Mapper<Category> , IdListMapper<Category,Long> {}
/** * 根据主键字符串进行查询,类中只有存在一个带有@Id注解的字段 * @param idList * @return */ @SelectProvider(type = IdListProvider.class, method = "dynamicSQL") List<T> selectByIdList(@Param("idList") List<PK> idList); /** * 根据主键字符串进行删除,类中只有存在一个带有@Id注解的字段 * @param idList * @return */ @DeleteProvider(type = IdListProvider.class, method = "dynamicSQL") int deleteByIdList(@Param("idList") List<PK> idList);
import tk.mybatis.mapper.additional.insert.InsertListMapper; public interface BaseMapper<T> extends InsertListMapper<T> {} @RegisterMapper public interface InsertListMapper<T> { @InsertProvider( type = InsertListProvider.class, method = "dynamicSQL" ) int insertList(List<? extends T> var1); }
抽取出一个baseMapper, 添加 @RegisterMapper 它才会被扫描到生效
import tk.mybatis.mapper.additional.idlist.IdListMapper; import tk.mybatis.mapper.additional.insert.InsertListMapper; import tk.mybatis.mapper.annotation.RegisterMapper; import tk.mybatis.mapper.common.Mapper; @RegisterMapper public interface BaseMapper<T> extends IdListMapper<T,Long>,Mapper<T>, InsertListMapper<T> { }