mybatisPlus学习

3,049 阅读11分钟

序言

mybatis-plus:3.1.2

springboot:2.1.6RELEASE

jdk 12.0.1

部分代码使用了jdk9以上的新特性,jdk9的optional类,jdk9的stream,jdk8函数式接口,jdk8的λ表达式

实体类在数据库的映射

对于基本数据类型的建议

尽量使用包装类,因为包装类没有初始值,方便定位空值传递和某些参数回写

对于实体类的映射

对于实体类的驼峰命名法,会自动映射为数据库的下划线命名法,可以设置 全局策略配置变量,

注意:全局策略变量设置后就不需要自行写注解

默认是开启自动映射的,所以建议数据库表名使用下划线命名法

对于自动映射举个例子

实体类中有个变量 private String lastName,则映射为数据库的last_name

//该注解添加在Id上(准确来说是添加在主键上)
@TableId(value = "id",type = IdType.AUTO)//指定自增策略
//该注解添加在成员变量上,若没有开启驼峰命名,或者表中列名不符合驼峰规则,可通过该注解指定数据库表中的列名(value),exist标明数据表中有没有对应列,因为使用通用mapper时传入参数时一个对象而非对象的各项数据。
@TableField(value = "last_name",exist = true)

实际curd应用

对于基础的增删改查mapper只要继承 BaseMapper<>

对于返回值为Integer的方法,其返回值为该次操作所影响的数据条数

service只要继承IService<>就可以调用写好的方法

insert()方法

参数为对应实体类

返回值为插入成功记录数

1主键回写

mybatis-plus会将主键值自动回写到实体类中,这种情况需要id属性为Integer这种包装类,否则不会回写,且需要确定主键策略,否则会抛出 java.lang.IllegalArgumentException异常

2对于sql语句生成

如果对应实体类的某一属性为空则对应sql中属性不会出现,因为messageBroad类中还存在id,time等属性但是由于未能赋值,所以sql语句中不存在空值属性

      MessageBroad m=new MessageBroad("这是一个关于sql语句回显", "dreamlike");
        messageBroadMapper.insert(m);
// ==>INSERT INTO message_broad ( messages, name ) VALUES ( ?, ? ) 
//==> Parameters: 这是一个关于sql语句回显(String), dreamlike(String)
///<==    Updates: 1
insertAllcolumn()方法

显然3.*版本已经移除,从官网文档上找不到这个方法,我也找不到(3.1.2),据尚硅谷视频讲,就是sql语句包含所有未注明@TableField(value = "last_name",exist = flase)的属性,sql语句中值为空

其余*Allcolumn方法一致

updateById()方法

参数为对应实体类

返回值为修改成功记录数

     MessageBroad m=new MessageBroad("这是一个关于updateById的测试", "dreamlike");
     m.setId(88);
     messageBroadMapper.updateById(m);
//==>  Preparing: UPDATE message_broad SET messages=?, name=? WHERE id=? 
//==> Parameters: 这是一个关于updateById的测试(String), dreamlike(String), //88(Integer)
//<==    Updates: 1

如果存在空值,则对应列不更新

        MessageBroad m=new MessageBroad("这是一个关于updateById的测试2", null);
        m.setId(88);
        messageBroadMapper.updateById(m);
//==>  Preparing: UPDATE message_broad SET messages=? WHERE id=? 
//==> Parameters: 这是一个关于updateById的测试2(String), 88(Integer)
//<==    Updates: 1

update()方法

参数为对应实体类

返回值为修改成功记录数

会根据第二个参数指定的方式将除主键外的属性全部更新为与第一个参数属性相一致

MessageBroad m=new MessageBroad("这是一个关于update的测试2", "LMY");
messageBroadMapper.update(m,new UpdateWrapper<MessageBroad().eq("name","dreamlike"));
//==>  Preparing: UPDATE message_broad SET messages=?, name=? WHERE name = ? 
//==> Parameters: 这是一个关于update的测试2(String), LMY(String), dreamlike(String)
//<==    Updates: 7
deleteById()方法

参数为int/Integer

返回值为删除成功记录数

messageBroadMapper.deleteById(88)
//==>  Preparing: DELETE FROM message_broad WHERE id=? 
//参数是否存在这个id不会产生异常
deleteByMap()方法

参数为Map<String,Object>

返回值为删除成功记录数

//与delete**(@Param Wrapper<>)方法基本一致
//将queryWrapper.eq()的键值对改为了map集合的键值对
//键为String类型为表字段,值为要查询的值
HashMap<String, Object> map = new HashMap<>();
        map.put("name", "sad");
        map.put("password", "asd");
        loginUserMapper.deleteByMap(map);
// Preparing: DELETE FROM login_user WHERE password = ? AND name = ? 
////若map的键在表中不存在则会抛出
//java.sql.SQLSyntaxErrorException异常
//也就是说键不能错,值错了只是会误差或者不删
//这个键一定要和表的列名一致,不是和实体类一致
deleteBatchlds()方法

参数Collection<? extends Serializable>

返回值为删除成功记录数

loginUserMapper.selectBatchIds(Arrays.asList(1,2,100,10));
//Preparing: SELECT Id,name,password,role FROM //login_user WHERE Id IN ( ? , ? , ? , ? ) 
////如果传入的是个空集合则抛出
//org.springframework.jdbc.BadSqlGrammarException异
//常
//如果id不存在也没什么效果
delete()方法

参数为 Wrapper接口

返回值为删除成功记录总数

messageBroadMapper.delete(new QueryWrapper<MessageBroad>().eq("name", "test1"));
//DELETE FROM message_broad WHERE name = ? 
selectById()方法

参数为int/Integer

返回值为对应接口泛型的一个对象

messageBroadMapper.selectById(88)
//==>  Preparing: SELECT id,messages,name,time FROM message_broad WHERE id=? 
//==> Parameters: 88(Integer)
//<==    Columns: id, messages, name, time
//<==        Row: 88, <<BLOB>>, LMY, 2019-07-27 06:10:53
//<==      Total: 1
Optional.ofNullable(loginUserMapper.selectById(7)).ifPresentOrElse(System.out::println, ()-> System.out.println("不存在"));
//如果不存在就会返回一个空指针
selectOne()方法

参数为Wrapper<>接口,一般使用QueryWrapper类

返回值为对应接口泛型的一个对象

loginUserMapper.selectOne(new QueryWrapper<LoginUser>().eq("name", "sad"))
//==>  Preparing: SELECT Id,name,password,role FROM login_user WHERE name = ? 
//==> Parameters: sad(String)
//<==    Columns: Id, name, password, role
//<==        Row: 0, sad, asd, asd
//<==      Total: 1
//查不到就是返回空指针
//如果对应有多个结构则会抛出org.mybatis.spring.MyBatisSystemException异常
//描述: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 7
selectCount()方法

参数为Wrapper<>接口,一般使用QueryWrapper类

返回值为Integer

 Optional
 .ofNullable(messageBroadMapper.selectCount(
              new QueryWrapper<MessageBroad().eq("name", "LY") ))
 .ifPresentOrElse(System.out::println, ()-> System.out.println("不存在"));
//==>  Preparing: SELECT COUNT( 1 ) FROM message_broad WHERE name = ? 
//==> Parameters: LY(String)
//<==    Columns: COUNT( 1 )
//<==        Row: 0
//<==      Total: 1
//这个不存在空指针的事情,有多少返回多少
selectBatchIds()方法

参数为Collection<? extends Serializable>接口

返回值为一个list集合,其泛型与接口泛型相一致

ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
loginUserMapper.selectBatchIds(arrayList);
//==>  Preparing: SELECT Id,name,password,role FROM login_user WHERE Id IN ( ? , ? ) 
//==> Parameters: 1(Integer), 2(Integer)
//<==    Columns: Id, name, password, role
//<==        Row: 1, admin, 123456, asd
//<==      Total: 1
//Id=2的不存在,所以查出了一条
//如果Id查不到,也不会抛出空指针异常
//如果传入的是个空集合则抛出org.springframework.jdbc.BadSqlGrammarException异常
//建议集合传入前先看大小
   ArrayList<Integer> list = new ArrayList<>();
System.out.println(list.size() == 0 ? "no" :loginUserMapper.selectBatchIds(list));
selectByMap()方法

参数为 Map<String, Object>接口

返回值为一个list集合,其泛型与接口泛型相一致

//与select**(@Param Wrapper<>)方法基本一致
//将queryWrapper.eq()的键值对改为了map集合的键值对
//键为String类型为表字段,值为要查询的值
HashMap<String, Object> map = new HashMap<>();
map.put("name", "sad");
map.put("role", "asd");
System.out.println(loginUserMapper.selectByMap(map));
//==>  Preparing: SELECT Id,name,password,role FROM login_user WHERE role = ? //AND name = ? 
//==> Parameters: asd(String), sad(String)
//<==    Columns: Id, name, password, role
//<==        Row: 0, sad, asd, asd
//<==      Total: 1
//若map的键在表中不存在则会抛出java.sql.SQLSyntaxErrorException异常
//这个键一定要和表的列名一致,不是和实体类一致
//举个例子
//实体类中 private String lastName;
//表中为last_name
//则必须为map.put("last_name","***");("***"为任意内容)
selectList()方法

参数为Wrapper<>接口

返回值为一个list集合,其泛型与接口泛型相一致

messageBroadMapper.selectList(new QueryWrapper<MessageBroad>().eq("name", "LY"))
//Preparing: SELECT id,messages,name,time FROM message_broad WHERE name = ? 
selectMaps()方法

参数为Wrapper<>接口

返回值为List<Map<String, Object>>

messageBroadMapper.selectMaps(new QueryWrapper<MessageBroad>().eq("name", "LMY"));
 // Preparing: SELECT id,messages,name,time FROM message_broad WHERE name = ? 

对于返回来说,list每一个节点都是一个map储存一条查询到的信息,列名作为键,实际值作为值

{name=LMY, messages=这是一个关于update的测试2, id=81, time=2019-07-25 16:42:50.0}
{name=LMY, messages=这是一个关于upda, id=82, time=2019-07-25 16:43:51.0}
{name=LMY, messages=这是一, id=84, time=2019-07-27 05:38:29.0}
{name=LMY, messages=这, id=85, time=2019-07-27 05:44:11.0}
{name=LMY, messages=这是一个关于upd888757, id=86, time=2019-07-27 05:56:24.0}
//这就是五个数据及其内部map键值对
selectObjs()方法

参数为Wrapper<>接口

返回值为List

messageBroadMapper.selectObjs(new QueryWrapper<MessageBroad>().eq("name", "LMY"));
// Preparing: SELECT id,messages,name,time FROM message_broad WHERE name = ? 

这个方法说实在的实用性不高,因为list是由第一个字段组成的list,我一般第一个字段都是Id

selectPage()方法

参数依次为IPage<>接口,Wrapper<>接口

其中Wrapper<>参数可为null

返回值为一个Ipage类(mp自带的类),其泛型与接口泛型相一致

这个方法用于分页,而分页分为内存分页和物理分页

内存分页就是全部拿出再分页,物理分页只取出部分数据(一个简单的分辨方法就是sql语句有没有limit)

第一个参数往往使用实现了IPage接口的Page<>类,其第一个参数为目前所在页,第二个参数为每页数据最大条数,第二个参数为null则查询全部,不为空则是按照该参数进行条件查询

对于内存分页

loginUserMapper.selectPage(new Page<>(1, 3), null).getRecords();
//==>  Preparing: SELECT Id,name,password,role FROM login_user 
//很显然是查询全部到内存然后再分页

对于物理分页本质上的实现是使用拦截器去修改sql语句所以要配置拦截器

//这个配置类我放在**/src/java/**/config/MybatisplusConfig.java
@Configuration
@EnableTransactionManagement
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisplusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        PaginationInterceptor page=new PaginationInterceptor();
        page.setDialectType("mysql");//配置mysql方言
        return page;
    }
}

接下来就是调用该方法获得Ipage对象后,调用getRecords获得list对象,如果不够则返回的list不满,超过总量。比如我只有29条数据,但是我请求第七页每页五条超过总数据量所以返回的list的size为0,page页数也做了合理化处理负数则默认为1,每页个数为负数则查询全部

 loginUserMapper.selectPage(new Page<>(1, 3), null);
//Preparing: SELECT Id,name,password,role FROM login_user LIMIT ?,? 
//这个是查询name叫“admin”的数据,并按照id降序排序,这个是通过sql语句执行的
// SELECT id,messages,name,time FROM message_broad WHERE name = ? ORDER BY id DESC LIMIT ?,? 
IPage<MessageBroad> iPage = messageBroadMapper.selectPage(new Page<MessageBroad>(1, 5), new QueryWrapper<MessageBroad>().eq("name", "admin").orderByDesc("id"));
iPage.getRecords().forEach(System.out::println);
selectMapsPage()方法

参数依次为IPage<>接口,Wrapper<>接口

其中Wrapper<>参数可为null

返回值为一个Ipage<Map<String,Object>>类(mp自带的类)

下面代码环境是已经配置好了config

 messageBroadMapper.selectMapsPage(new Page<MessageBroad>(1,2), new QueryWrapper<MessageBroad>().eq("name", "LMY"))
//  Preparing: SELECT COUNT(1) FROM message_broad WHERE name = ? 

其实这个就是分页和selectMaps的统合

对于自定义sql操作
messageBroadMapper.selectList(new QueryWrapper<MessageBroad>().eq("name","admin"))
eq("name", "老王")--->name = '老王'  等于
ne("name", "老王")--->name <> '老王'  不等于
gt("age", 18)--->age > 18    大于
ge("age", 18)--->age >= 18  大于等于
lt("age", 18)--->age < 18 小于
le("age", 18)--->age <= 18 小于等于
between("age", 18, 30)--->age between 18 and 30 介于之间 对于mysql中的between and他是一个包含边界的
notBetween("age", 18, 30)--->age not between 18 and 30 和上面相反 mysql不包含边界
like("name", "王")--->name like '%王%'   同理not
likeLeft("name", "王")--->name like '%王'  同理right
当然也支持注解形式
@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
//所有列名错的都会抛出java.sql.SQLSyntaxErrorException异
//常

以上内容自行查阅官网文档

springboot全局策略变量的设置

部分properties设置
#主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
mybatis-plus.global-config.id-type=3
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
mybatis-plus.global-config.field-strategy=2
#驼峰下划线转换
mybatis-plus.global-config.db-column-underline=true
#sql语句执行打印
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

官方yml设置
mybatis-plus:
  # 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
  # 如果是放在resource目录 classpath:/mapper/*Mapper.xml
  mapper-locations: classpath:/mapper/*Mapper.xml
  #实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.yourpackage.*.entity
  global-config:
    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
    id-type: 3
    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
    field-strategy: 2
    #驼峰下划线转换
    db-column-underline: true
    #mp2.3+ 全局表前缀 mp_
    #table-prefix: mp_
    #刷新mapper 调试神器
    #refresh-mapper: true
    #数据库大写下划线转换
    #capital-mode: true
    # Sequence序列接口实现类配置
    key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
    #逻辑删除配置(下面3个配置)
    logic-delete-value: 1
    logic-not-delete-value: 0
    sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
    #自定义填充策略接口实现
    meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
  configuration:
    #配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId) 
    map-underscore-to-camel-case: true
    cache-enabled: false
    #配置JdbcTypeForNull, oracle数据库必须配置
    jdbc-type-for-null: 'null' 

抛出空指针:

1要使用包装类

2要提供全参构造器

java.lang.IllegalArgumentException:

此时需要提供主键自增策略