Mybatis-plus笔记整理

导入依赖

<!--MP-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.1</version>
</dependency>
<!-- 代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
<!-- 代码生成器模板引擎-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

雪花算法

数据库的扩展方式主要包括:业务分库,主从复制,数据库分表

数据库分表

​ 如果业务持续发展,同一业务的单表数据也会达到数据库服务器的处理瓶颈;例如淘宝几亿用户数据,因此需要对单表数据进行拆分。

垂直分表

​ 列也比较多,查询量比较大的时候,通常把经常被查询的字段 和 数据量比较大的字段,拆分到不同的表中,比如age,sex 主要是查询使用,nickname昵称字段和描述字段主要用于展示使用而且本身还比较长,可以将后面两个字段独立到另一个表中去,这样查询的age 和 sex时 能带来一定的性能提升,相当于对表垂直切了一刀,把非主要查询的这两个字段独立出去。也就是把大字段或一般查询用不到的字段分离到另外一张表中。user user_info 用 id进行关联,user id字段自增长,user_info id字段不设策略,user的id是啥 我就是啥

主键一对一关联

水平分表

适合表行数特别大的表,数据量特别大的时候,也没有明确查询字段之类的需求,关键还要看表的访问性能,一些比较复杂的表,可能超过1000万就要分表了,按数据分表,多少万条数据在一个表上,多少行数据在一个表上,相当于横着切一刀 分出去。水平分表相比垂直分表会引入更多的复杂性。

复杂性体现:主键自增问题 如果分2张表,按照主键范围存在不同的表上,比如1~1千万,1千万零一~2千万,分别在两张表上,如果满了 扩展容易,再加即可,实现了动态扩展,各表内数据量有多有少,现在一般都是负载均衡,会导致服务器访问不均衡。如果解决均衡问题,可以将表的主键起始id分表设为1、 2、 3 步长为3,这样就均衡了,但是扩展又会出问题,如果扩展又会出现数据迁移问题,重新分配布局。两种方式扩展都可能会遇到之前的表里面有数据被删除,然后后面的数据主键自增已经占用了新的主键,扩展的id并不是理论上应该存的id,而是之前的表可能已经用掉了。会不安全

解决办法:

Hash:取模运算,放到哪一个表中,要求初始表数量确定,表数量太多维护比较麻烦,太少又可能导致单表性能出现问题,表分布会比较均匀,但是扩展会很麻烦,所有数据都要重分布,uuid不能和mysql的聚簇索引一起用,不利于mysql查询优化

雪花算法snow flake(分布式id占19个符号位):mybatis-plus中默认的主键策略就是雪花算法

分布式主键生成算法,它能够保证不同表的主键不重复性,以及相同表的主键的有序性

核心思想:

长度64bit一个Long型,分四个部分:

1.第一个位置,符号位,为0,表示为正数,一般都不会用负数

2.第二个位置,时间戳位,41bit,存储的是差值,约定于69.73年,这样生成的时间都是不一样的

3.第三个位置,5bit是数据中心(相当于机房代码),5bit机器ID(表示数据中心存储数据的机器,可以部署在1024个节点,相当于1024台机器),

4.第四个位置,12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),支持并发量很大


mybatis-plus 默认识别 名字叫“id”的字段,自动进行雪花算法插入数据库id,如果数据库是uid实体类也是uid字段, 或者其他则识别不了,无法进行默认的雪花算法插入主键到数据库,会报错。所以此时需要在实体类的字段上加@TableId 告诉mybatis-plus这个字段是id 字段(数据库中是uid);

@TableName(value="t_user")
public class User {
    //默认雪花算法  也可以设置
   // @TableId(type= IdType.ASSIGN_ID) private String id; 也可以
    @TableId
    private Long uid;

或者,数据库名字叫uid,实体类叫做id,也无法完成映射,所以需要加value属性,如同@TableName注解

 @TableName(value="t_user")
public class User {
  //@TableId(value = "uid",type = IdType.AUTO) 主键自增策略
    @TableId(value = "uid")
    private Long id;
    private String name;
    private int age;
    private String email;xxxxxxxxxx @TableId(value = "uid")private Long id;private String name;private int age;private String email;
​

业务中id为空,数据库中也没有设置id;id是主键,不能为空,所以在插入的时候就会报错。如果所有的表都要求主键自增,可以在配置文件中设置

#设置全局主键自增
mybatis-plus.global-config.db-config.id-type=auto

新增两个字段 ,如果类中的列名为驼峰命名

private LocalDateTime createTime;
private LocalDateTime updateTime;
//在数据库中字段为:create_time   update_time mybatis-plus 会自动把驼峰进行转换

如果数据库中的字段名跟实体类不一致,需要用@TableField(value="")

@TableId(value = "uid",type = IdType.AUTO)
private Long id;
@TableField(value = "username")
private String name;
private int age;
private String email;

数据库字段可进行自动填充,创建和更新时间 可以用CURRENT_TIMESTAMP 系统自动管理,更新字段,再勾上默认根据当前时间戳进行更新,这样管理就不需要每次业务里面涉及。也可以在业务层进行设计,自动填充功能,需要在实体类加注解来标识。

@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) //插入或更新的时候自动填充
private LocalDateTime updateTime;
//但是填充什么呢?需要定义实现自动填充功能 实现元对象处理器接口
@Slf4j
@Component
public class MyMeatObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("insert自动填充....");
          //插入数据的时候 识别到注解  @TableField(fill = FieldFill.INSERT) 就会进入这个方法来执行
         this.strictInsertFill(metaObject,"createTime", LocalDateTime.class, LocalDateTime.now());
         this.strictUpdateFill(metaObject,"updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("update自动填充。。。");
        //更新的时候
        this.strictUpdateFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());
    }
}
​

//判断当前对象自动填充属性是否包含 当前属性
boolean hasAuthor = metaObject.hasSetter("author");
//如果 有setter的方法 有这个author字段 进行自动填充author
if(hasAuthor) {
    log.info("insert author属性");
    this.strictInsertFill(metaObject, "author", String.class, "石头");
}

年龄也自动填充

@TableField(fill = FieldFill.INSERT)
    private Integer age;
log.info("age 填充为18");
 //对age进行自动填充 当没传age的时候填充18进去 
Object age = this.getFieldValByName("age", metaObject);
//如果业务层赋值了就不用去填充,如果没有赋值去进行填充
if(age==null){
  this.strictInsertFill(metaObject, "age", Integer.class, 18);
        }
​

@TableLogic 逻辑删除

物理删除:真实删除

逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录使用场景,可以进行数据恢复

数据库中此字段 一般默认类型tinyint类型,默认1为已删除,0未删除

在数据库中创建逻辑删除状态列 同时实体字段一般前面不要加is有些框架识别可能出问题

    //private Integer deleted;
    //逻辑删除字段   0表示false 1表述true
    @TableLogic
    @TableField(value = "is_deleted")
    private Boolean deleted;

执行记录 需要数据库中此字段 值为0

Preparing: UPDATE t_user SET is_deleted=1 WHERE uid=? AND is_deleted=0 ==> Parameters: 1499759447969390597(Long)<== Updates: 0

//自定义 1为未删除 -1为已删除 需要去配置
@TableLogic
@TableField(value = "is_deleted")
private Integer deleted;
mybatis-plus.global-config.db-config.logic-delete-field=deleted
mybatis-plus.global-config.db-config.logic-delete-value=-1
mybatis-plus.global-config.db-config.logic-not-delete-value=1

分页插件

1.添加配置类 config包 可以将配置全部写在这个包内

spring

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
    <!-- 其他属性 略 -->
    <property name="configuration" ref="configuration"/>
    <property name="plugins">
        <array>
            <ref bean="mybatisPlusInterceptor"/>
        </array>
    </property>
</bean>
​
<bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
    <!-- 需配置该值为false,避免1或2级缓存可能出现问题,该属性会在旧插件移除后一同移除 -->
    <property name="useDeprecatedExecutor" value="false"/>
</bean>
​
<bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
    <property name="interceptors">
        <list>
            <ref bean="paginationInnerInterceptor"/>
        </list>
    </property>
</bean>
​
<bean id="paginationInnerInterceptor" class="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor">
    <!-- 对于单一数据库类型来说,都建议配置该值,避免每次分页都去抓取数据库类型 -->
    <constructor-arg name="dbType" value="H2"/>
</bean>

spring-boot

//可将包扫描写在这个类里面 也可以写在启动类上,对持久层的扫描
@Configuration//表示这是一个配置类,应用程序启动的时候会被自动读取并以配置的形式读取
@MapperScan("mybatisplus.mapper")//可以将持久层所有的配置集中在这里管理 全写在这个配置类中
public class MybatisPConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false
     * 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    //所有的插件都是以拦截器的形式存在
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //先创建拦截器(插件)管理器
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件 将分页(这里是mysql)添加到一个内置的拦截器管理器中( 将分页插件的拦截器对象创建出来,然后用此方法配置到 这个管理器之中)
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        //最后将这个interceptor对象,作为一个bean对象返回
        return interceptor;
    }

//    @Bean
//    public ConfigurationCustomizer configurationCustomizer() {
//        return configuration -> configuration.setUseDeprecatedExecutor(false);
//    }
}

针对mysql的分页配置类 写好了。进行测试

@Slf4j
@SpringBootTest
public class IntercepterTest {
    @Resource
    private UserMapper userMapper;
    @Resource
    private ProductMapper productMapper;
    @Test
    public void test1(){
        //新建分页参数对象 查询第几页,每页5条记录
        Page<User> userPage = new Page<>(2, 5);
        //这两个对象是一个对象 参数为页对象 及qureyMapper条件构造器 先传null
        //Page<User> userPage1 = userMapper.selectPage(userPage, null);
        //System.out.println("确认是否是一个对象"+(userPage==userPage1)); true
        //优化为
        userMapper.selectPage(userPage, null);
        //当前页码下的所有记录
        List<User> users = userPage.getRecords();
        users.forEach(System.out::println);
        //总数
        long total = userPage.getTotal();
        System.out.println("总数+"+total);
        //有没有下一页
        boolean bn = userPage.hasNext();
        System.out.println("下一页??"+bn);
        //有没有上一页
        boolean bp = userPage.hasPrevious();
        System.out.println("上一页?"+bp);

    }

在xml中如何使用配置分页?

//@Repository
public interface UserMapper extends BaseMapper<User> {
    //自定义一个方法 具体的xml实现在resources下面的mapper文件中
    //扩展mapper
    List<User> selectAllByName(String name);
    //第一个参数为分页对象  第二个为查询条件  根据年龄来查询用户 并分页展示
    IPage<User> selectPageVo(IPage<?> page, Integer age);
 <select id="selectPageVo" resultType="mybatisplus.entity.User">
        select <include refid="Base_Colum_List"/>
        from t_user where age > #{age}
    </select>
</mapper>

xml中不需要去进行 分页,只需要写条件即可,我们传递了page对象进去,MP会自动的去读取参数中的配置对象,且会作为整个查询语句的后缀,然后执行sql

@Test
public void selectByAge(){
    Page<User> userPage = new Page<>(2,6);
    IPage<User> userIPage = userMapp      er.selectPageVo(userPage, 18);
    List<User> users = userIPage.getRecords();
    users.forEach(System.out::println);
}

执行语句为:自动追加

Total: 1==> Preparing: select uid,name,age,email from t_user where age > ? LIMIT ?,?==> Parameters: 18(Integer), 6(Long), 6(Long)<== Columns: uid, name, age, email<== Row: 1500133241527472131, 建国2, 22, jiangu2o@qq.com<== Row: 1500133241527472134, 建国更新, 100, jiangu2o@qq.com<== Row: 1500133241527472139, 观海200, 200, jianguo5@qq.com

但是实体类里面是 id 所以这里自定义的方法 的xml 需要改

<mapper namespace="mybatisplus.mapper.UserMapper">
<!--sql片段-->
    <sql id="Base_Colum_List">
        uid as id,name,age,email,is_deleted as deleted,create_time as createTime,update_time as updateTime
    </sql>
    <select id="selectAllByName" resultType="mybatisplus.entity.User">
        select <include refid="Base_Colum_List"/>
        from t_user
        where name = #{name}
    </select>
    <select id="selectPageVo" resultType="mybatisplus.entity.User">
        select <include refid="Base_Colum_List"/>
        from t_user where age > #{age}
    </select>
​

==> Preparing: select uid as id,name,age,email,is_deleted as deleted,create_time as createTime,update_time as updateTime from t_user where age > ? LIMIT ?,?==> Parameters: 18(Integer), 6(Long), 6(Long)<== Columns: id, name, age, email, deleted, createTime, updateTime<== Row: 1500133241527472131, 建国2, 22, jiangu2o@qq.com, 0, 2022-03-06 14:38:17, 2022-03-06 14:38:17Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4e1459ea]User(id=1500133241527472131, name=建国2, age=22, email=jiangu2o@qq.com, createTime=2022-03-06T14:38:17, updateTime=2022-03-06T14:38:17, deleted=false)


MP的乐观锁应用

A和B都要对数据进行修改操作,但是取出的数据都是未更新过的数据,同时修改,存入就会存在数据覆盖的情况,出现错误数据。比如两个人同时修改某一商品的价格。一个增加一个减少,造成并发冲突,并发也不高但是造成数据不一致问题。可以通过乐观锁来解决

SELECT id,name,version FROM product WHERE id=1;

查询的时候就需要将 控制版本的version取出来 更新时,对version进行控制,每次有人修改的时候都需要提前判断,符合条件才能进行修改。每修改一次 版本号会加一

//告诉mp这 就是版本号
    @Version
    private Integer version;
​
​
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    //添加分页插件
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    //添加乐观锁插件
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

Preparing: SELECT id,name,price,version FROM product WHERE id=?==> Parameters: 1(Long)<== Columns: id, name, price, version<== Row: 1, 笔记本, 150, 3

UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?==> Parameters: 笔记本(String), 200(Integer), 4(Integer), 1(Long), 3(Integer)

修改后 将版本号为3的记录 版本号更改为4 那么同时另外一条修改语句 则无法修改

=> Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?==> Parameters: 笔记本(String), 120(Integer), 4(Integer), 1(Long), 3(Integer)<== Updates: 0

Wrapper条件构造

Wrapper → AbstractWrapper

AbstractLambdaWrapper(抽象类) → LambdaUpdateWrapper 和 LambdaQueryWrapper

UpdateWrapper QueryWrapper

/**
 * 查询名字中包含a
 * 年龄大于10且小于20 email不为空的用户
 */
@Test
public  void test(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
    //column对应数据库表的列名 而不是属性名
    queryWrapper.like("name","a");
    //左侧为%
    queryWrapper.likeLeft("name","k");
    //gt为>  ge为≥  lt为<  le为≤
    queryWrapper.gt("age",10)
            .lt("age",20)
            .isNotNull("email");
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
  
  //between 大于等于 小于等于
        queryWrapper.between("age",0,20);

Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND name LIKE ? AND age > ? AND age < ? AND email IS NOT NULL)==> Parameters: %a%(String), %k(String), 10(Integer), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499409859102179330, jack, 18, jianguo@qq.com, null, null, 0

case函数

按照姓名 分国籍 操作
SELECT
    ( CASE NAME WHEN 'jack' THEN '美国' WHEN '花花' THEN '日本' WHEN '建国' THEN '中国' ELSE '韩国' END ) country ,
    sum(age) 总年龄 
FROM
    `t_user` GROUP BY(
    CASE NAME WHEN 'jack' THEN '美国' WHEN '花花' THEN '日本' WHEN '建国' THEN '中国' ELSE '韩国' END
    );
/**
 * 按年龄降序查询用户,如果年龄相同则按id升序排序
 */
@Test
public void test2() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    //组装排序条件
    queryWrapper.orderByDesc("age").orderByAsc("uid");
    List<User> list = userMapper.selectList(queryWrapper);
    list.forEach(System.out::println);
}

Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,uid ASC==> Parameters: <== Columns: id, name, age, email, create_time, update_time, deleted

/**
 * 删除email为空的用户
 */
@Test
public void test3(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.isNull("email");
    int res = userMapper.delete(queryWrapper);
    System.out.println("删除的记录数:"+res);
   //之前配置了逻辑删除,所以只是把is_deleted更新为1
}

Preparing: UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)

/**
 * 查询名字中包含花 且(年龄小于12或email为空的用户),并将这些用户的年龄设置为18,设置为user@haha.com
 */
@Test
public void test4(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("name","花")
            .and(i->i.lt("age",18).or().isNull("email"));
    User user = new User();
    user.setAge(18);
    user.setEmail("user@haha.com");
    int res = userMapper.update(user, queryWrapper);
    System.out.println("更新的条数为:"+res);

Preparing: UPDATE t_user SET age=?, email=?, update_time=? WHERE is_deleted=0 AND (name LIKE ? AND (age < ? OR email IS NULL))==> Parameters: 18(Integer), user@haha.com(String), 2022-03-14T23:07:38.807(LocalDateTime), %花%(String), 18(Integer)<== Updates: 5

查询所有用户的用户名和年龄 其他的不要

//查询用户名和年龄 其他的不要
@Test
public void test5(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.select("name","age");
    //其他属性 不会返回 返回的是map泛型
  //select语句通常会和selectMaps一起出现
    List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
    maps.forEach(System.out::println);
}

==> Preparing: SELECT name,age FROM t_user WHERE is_deleted=0==> Parameters: <== Columns: name, age<== Row: Jack, 20<== Row: 正阳, 20<== Row: Billie, 24

/**
 * 使用子查询
 * 查询id不大于3的所有用户的id列表
 */
@Test
public void test6(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    //如果子查询由用户输入 容易sql注入 
    queryWrapper.inSql("uid","select uid from t_user where uid<=3");
    // 可以这么写 queryWrapper.le("uid",3);
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}

SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (uid IN (select uid from t_user where uid<=3))==> Parameters: <== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 2, Jack, 20, test2@baomidou.com, null, null, 0<== Total: 1

UpdateWrapper

/**
     * 查询名字中包含花 且(年龄小于等于18或email不为空的用户),并将这些用户的年龄设置为16,设置为user@updateWrapper.com
     */
    @Test
    public void test7(){
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("age",16)
                .set("email","user@updateWrapper.com")
                .like("name","花")
                .and(i-> i.le("age",18).or().isNotNull("email"));
        //如果有自动填充功能,必须要把user对象传进去,否则 无法实现,比如updateTime
        //不需要 可以传null
        User user = new User();
        int res = userMapper.update(user, updateWrapper);
        System.out.println("更新的条数为:"+res);
    }

Preparing: UPDATE t_user SET age=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age <= ? OR email IS NOT NULL))==> Parameters: 16(Integer), user@updateWrapper.com(String), %花%(String), 18(Integer)<== Updates: 5

==> Preparing: UPDATE t_user SET update_time=?, age=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age <= ? OR email IS NOT NULL))==> Parameters: 2022-03-14T23:33:21.205(LocalDateTime), 16(Integer), user@updateWrapper.com(String), %花%(String), 18(Integer)<== Updates: 5

condition 动态组装查询条件

/**
 * 查询名字中含有 n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选择的
 */
@Test
public void test8() {
    String username = "花";
    Integer ageBegin =10;
    Integer ageEnd = 20;
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    if (StringUtils.isNotBlank(username)) {
        queryWrapper.like("name", username);
    }
    if(ageBegin!=null){
    queryWrapper.ge("age",ageBegin);
    }
    if(ageEnd !=null) {
        queryWrapper.le("age", ageEnd);
    }
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}
//利用重载的方法
 @Test
    public void test8() {
        String username = "花";
        Integer ageBegin = null;
        Integer ageEnd = 20;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(username), "name", username)
                .ge(ageBegin != null, "age", ageBegin)
                .le(ageEnd != null, "age", ageEnd);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
​
    }

Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age >= ? AND age <= ?)==> Parameters: %花%(String), 10(Integer), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499759447893893121, 花花0, 16, user@updateWrapper.com, null, 2022-03-14 23:33:21, 0

SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)==> Parameters: %花%(String), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499759447893893121, 花花0, 16, user@updateWrapper.com, null, 2022-03-14 23:33:21, 0<== Row: 1499759447969390594, 花花1, 16, user@updateWrapper.com, null, 2022-03-14 23:33:21, 0<== Row: 1499759447969390595, 花花2, 16, user@updateWrapper.com, null, 2022-03-14 23:33:21, 0

lambdaQueryWrapper

/**
 * 查询名字中含有 n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选择的
 */
@Test
public void test9() {
    String username = "花";
    Integer ageBegin = null;
    Integer ageEnd = 20;
    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.like(StringUtils.isNotBlank(username), User::getName, username)
            .ge(ageBegin != null, User::getAge, ageBegin)
            .le(ageEnd != null, User::getAge, ageEnd);
    List<User> users = userMapper.selectList(lambdaQueryWrapper);
    users.forEach(System.out::println);
}

lambdaUpdateWrapper

/**
 * 查询名字中包含花 且(年龄小于等于18或email不为空的用户),并将这些用户的年龄设置为16,设置为user@updateWrapper.com
 */
@Test
public void test10() {
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.set(User::getAge, 16)
            .set(User::getEmail, "user@updateWrapper.com")
            .like(User::getName, "花")
            .and(i -> i.le(User::getAge, 18).or().isNotNull(User::getEmail));
    //如果有自动填充功能,必须要把user对象传进去,否则 无法实现,比如updateTime
    //不需要 可以传null
    User user = new User();
    int res = userMapper.update(user, updateWrapper);
    System.out.println("更新的条数为:" + res);
}

其实就是把条件组装里的字符串形式 替换为Lambda替换,避免编译的时候没有发现的不必要的错误。


Gennerator代码生成器

public class CodeGenerator {
    @Test
    public void genCode(){
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
​
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("eric");
       //去掉Service接口的首字母I 不加 接口名字前面会有I %s是占位符 实体名字
        gc.setServiceName("%sService");
        //生成后是否打开资源管理器
        gc.setOpen(false);
        //主键策略
        gc.setIdType(IdType.AUTO);
        //开启swagger2模式
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);
​
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);
​
        // 包配置
        PackageConfig pc = new PackageConfig();
//        pc.setModuleName();
        pc.setParent("com.xie.srb.core");
        //此对象与数据库表结构一一对应,通过dao层向上传输数据源对象
        pc.setEntity("pojo.entity");
//        pc.setService();
//        pc.setXml();
        mpg.setPackageInfo(pc);
​
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //数据库表映射到实体的命名策略 数据库表名称 下划线变驼峰
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //数据库表字段映射到实体的命名策略 列名转驼峰
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
​
       // strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        strategy.setEntityLombokModel(true);
        //逻辑删除字段
        strategy.setLogicDeleteFieldName("is_deleted");
        //去掉布尔值的is_前缀(确保tinyint(1) 实体类中最好不要用is做前缀 容易出问题 变成deleted 会加字段映射@TableField
        strategy.setEntityBooleanColumnRemoveIsPrefix(true);
        //restful风格控制器 一般都会返回json 有@ResponseBody  会直接用@RestController
        strategy.setRestControllerStyle(true);
        mpg.setStrategy(strategy);
        //执行
        mpg.execute();
​
    }
}

核心模块的yml配置

erver:
  port: 8110
spring:
  profiles:
    active: dev
  application:
    # 以后要注册到微服务中 起一个 服务名
    name: service-core
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf8
    username: root
    password: root
mybatis-plus:
  configuration:
    #日志输出
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:com/xie/srb/core/mapper/xml/*.xml

创建主类

package com.xie.srb.core;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
​
@SpringBootApplication
@ComponentScan("com.xie.srb")
public class ServiceCoreApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceCoreApplication.class,args);
    }
}
​

配置MP分页

package com.xie.srb.core.config;
/**
 * 配置分页
 */
@Configuration
@MapperScan("com.xie.srb.core.mapper")
//事务处理
@EnableTransactionManagement 
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //分页
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return  interceptor;
    }
}
<build>
    <resources>
        <!-- xml放在java目录-->
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <!-- 指定资源的位置(xml放在resources下可以不用指定 -->
        <resource>
            <directory>src/main/java/com/xie/srb/core/mapper/xml</directory>
        </resource>
    </resources>
</build>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laughing_Xie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值