zoukankan      html  css  js  c++  java
  • 2020/07/03 初始mybatis

    title: 玩转spring-boot-mybatis
    date: 2019-03-11 19:36:57
    type: "mybatis"
    categories: mybatis #分类名
    tags: mybatis#

    作为持久层的ORM框架,目前在国内主流之一就是MyBatis,学会用它,用好它肯定是必备的功课

    我会主要从下面几个方面入整理本篇博客

    1. 快速搭建快发环境
    2. 常见的注解
    3. 怎么玩?

    一. 快速搭建开发环境#

    小插曲,添加测试模块的时候,引入junit模块和spring-boot-text-starter模块有先顺序,不然ide会报错...

    坐标

    Copy
      <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>
    
    • 另外插一嘴---mysql连接的版本适配

    我现在用的云主机docker上的官方版mysql,版本比较新,因此我的调整版本到 8以上,不然会报错说什么

    Copy
    com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could
    

    配置文件:

    • 主要是配置数据库的连接,如果我们使用的是通用mapper,Mybatis可以做到零配置
    Copy
    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
    

    启动类

    • 在这里告诉通用mapper我们的mapper包路径
    Copy
    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

    • 让我们自己的Mapper继承通用mapper,泛型是实体类名
    • 给它实体类名,就相当与告诉它了我们的表里的字段,不让他怎么会知道如何动态生成sql ?
    Copy
    import tk.mybatis.mapper.common.Mapper;
    public interface MyMapper extends Mapper<MyBrand> {}
    

    ok,到现在环境就搭建好了


    二. 常见的注解#

    • 这里的注解主要是作用在实体类上的注解,当我们撸起袖子写代码的时候,被给它难住喽,尴尬

    情景1: 对于实体类

    标记他对应那张表

    Copy
    import javax.persistence.Table;
    @Table(name = "tb_brand")
    

    情景2: 对于主键

    大多数情况主键一般都叫id,bigint类型,没什么超级特殊的情况我们都希望他可以自己增长,下面两个注解都可以做到这件事,但是前提表id属性必须设置成 autoincreament , 不然报错说,id不能不写

    • 注解1:
    Copy
    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) 
    
    • 注解2:

    直接使用Mybatis的原生注解

    Copy
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    

    情景3:

    我们都知道springMVC会把前端发送过来的json转化为我们的javaBean,因此我们的javaBean里面的属性名和前端发送的那个对象的属性名要意义对应的,这是规范!

    设想,加入前端一顿收集数据,把商品的品牌信息和库存信息都发送过来了,只有关于品牌的javabean,数据接受不全怎么办? 没关系,下面的注解可以搞定

    • @Transient (意味短暂的)告诉mapper 他不是PO(持久层对象,Persistence Object)的属性,而且,springMvc还会把信息关于库存的信息,封装进去
    Copy
    @Transient
    

    情景4:

    我们的pojo属性名,和数据库中的关键字重名怎么办?

    @Column() 可以解决 数据库中的字段为数据库的关键字的问题

    Copy
    @Column(name="`numeric`")  //
    private Boolean numeric;   //是否是数字类型
    

    情景5 :

    数据表中tingint(1) 对应的javaBean的属性咋写?

    Copy
    tingint(1) // 它是boolean的同义词
    

    情景5:

    关于VO , 如何选择性的返回给前端javaBean的属性?就比如说我们查询有没有这个用户,总不至于把密码,私人信息一块返回给前端吧?

    Copy
    import com.fasterxml.jackson.annotation.JsonIgnore;
    @JsonIgnore
    

    它是jackson的注解

    Copy
      <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    

    三. 怎么玩?#

     下面就是介绍常用的mapper的API

    一. CRUD#

    • 两种新增
    Copy
    //会有选择性的新增, 智能判断 brand里面有没有空的字段,有值,用这个值,没有值而使用数据库的默认值
    insertSelective(brand) 
    
    // 假如前端提交过来的 brand里面的数据是全的,用词方法
    brandMapper.insert(brand);
    
    • 简单查询
    Copy
    /**
     * 根据实体中的属性值进行查询,查询条件使用等号
     * @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);
    
    • 简单删除
    Copy
    /**
     * 根据主键字段进行删除,方法参数必须包含完整的主键属性
     * @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

    Copy
    /**
     * 根据主键更新属性不为null的值
     * @param record
     * @return
     */
    @UpdateProvider(type = BaseUpdateProvider.class, method = "dynamicSQL")
    int updateByPrimaryKeySelective(T record);
    

    看到这个方法有没有觉得很爽? 它根据主键,修改不为null的属性,意思就是说,我们把不需要修改的地方设置为null就好了
    ,这样看,前面需要我们一顿整,整啥呢? 结合实际的业务逻辑,把前端给交过来的数据最新的数据放到我们的bean中直接修改,把不需要修改的属性设置为null,有属性可能需要直接删除,有的属性可能要去别的表中查询(别忘了加上事务 @Transactional )

    Copy
    /**
     * 根据主键更新实体全部字段,null值会被更新
     * @param record
     * @return
     */
    @UpdateProvider(type = BaseUpdateProvider.class, method = "dynamicSQL")
    int updateByPrimaryKey(T record);
    

    这个方法和上面的神似

    二 .分页,过滤(模糊查询),搜索#

    API

    Copy
    mapper.selectByExample(example)
    

    参照下面原生的sql.拼接查询条件

    Copy
     select * from 表名
            where name like '% X %' or  letter== 'x'
            order by  id desc
    

    逻辑

    Copy
    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);
    }
    

    三 .自定义sql#

    Mybatis只能针对单表为我们生成sql,如果我们的需求是跨表操作,比如说涉及到两张表,我们就得去mapper里面自己写sql

    原生sql

    Copy
    select * from tb_brand b
    inner join tb_category_brand cb on b.id=cb.brand_id  -- 去笛卡尔积
    where cb.category_id= ? ;
    

    mapper

    ?用#{id} 取代

    Copy
    @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);
    

    四 .拓展包--批量操作#

    • 批量查询,批量删除

    注意他的包啊!

    Copy
    import tk.mybatis.mapper.additional.idlist.IdListMapper;
    public interface Mapper extends Mapper<Category> , IdListMapper<Category,Long> {}
    
    Copy
    /**
     * 根据主键字符串进行查询,类中只有存在一个带有@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);
    
    
    • 批量插入:
    Copy
    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 它才会被扫描到生效

    Copy
    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> {
    }
    
  • 相关阅读:
    [Leetcode]-Palindrome Number
    timesten升级
    C++的for循环细节,必看!
    web desktop在线演示
    定制流程
    西服定制 服装在线定制 GIMIWEAR高级定制
    Roseonly:互联网轻奢品牌之路-搜狐IT
    妊娠纹_百度百科
    ARPU_百度百科
    工商管理硕士(MBA)-北大国际MBA
  • 原文地址:https://www.cnblogs.com/pengyuan192/p/13229735.html
Copyright © 2011-2022 走看看