新乡学院22软工毕业实训

本文介绍了如何使用MyBatisPlus进行数据库操作,包括创建数据库表、导入依赖、编写配置文件、实体类定义、Mapper接口以及使用条件构造器进行复杂查询。重点讲解了@TableId、@TableField等注解的使用,以及如何通过LambdaQueryWrapper进行链式查询和条件设置,实现动态查询条件和字段选择。
摘要由CSDN通过智能技术生成

Day1

了解Mybatis Plus

1创建数据库表

CREATE TABLE `tbl_product` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `pname` varchar(50)  COMMENT '商品名称',
  `brand` varchar(100) COMMENT '品牌',
  `price` double COMMENT '单价',
  `num` int COMMENT '库存数量',
  `online` char(1) COMMENT '是否上架',
  `create_time` date COMMENT '添加日期',
  `last_update_time` datetime COMMENT '最后修改日期',
  `version` tinyint DEFAULT '0' COMMENT '版本号',
  `deleted` tinyint DEFAULT '0' COMMENT '是否删除 0 否 1 是',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

/*Data for the table `tbl_user` */

INSERT INTO tbl_product (pname,brand,price,num,online,create_time,last_update_time,version,deleted) VALUES
	 ('HUAWEI P50','华为',8988.0,20,'1','1999-10-26','2021-12-24 14:53:22',1,0),
	 ('小米10S','小米',2699.0,21,'1','2021-06-12','2021-10-06 08:12:16',1,0),
	 ('OPPO K9','OPPO',1999.0,23,'1','1998-03-27','2021-12-06 12:56:36',1,0),
	 ('HUAWEI nova 8','华为',2099.0,20,'0','2021-06-22','2021-12-10 17:26:21',1,0),
	 ('华为 Mate 40','华为',6799.0,25,'1','1996-07-16','2021-10-07 09:32:16',1,0),
	 ('小米 红米Note9','小米',935.0,23,'1','1998-05-01','2021-10-10 22:17:17',1,0),
	 ('OPPO A11s','OPPO',1199.0,23,'0','1998-07-12','2021-11-02 23:22:36',1,0),
	 ('OPPO A93','OPPO',1599.0,24,'1','1997-11-02','2021-08-03 10:36:27',1,0),
	 ('华为畅享 20e ','华为',1199.0,26,'1','1995-12-06','2021-08-03 11:36:27',1,0),
	 ('小米 MIX4','小米',4499.0,22,'1','1996-08-06','2021-10-07 09:32:16',1,0),
	 ('vivo S12','vivo',3699.0,28,'1','1999-12-06','2021-11-02 18:23:22',1,0),
	 ('vivo iQOO 8','vivo',3899.0,30,'0','2000-08-01','2021-12-25 09:56:31',1,0);

2 导入依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->

            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

3 编写配置文件

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/user?useUnicode=true&IntegerEncoding=utf8&useSSL=false&servertimeZone=Asia/Shanghai
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: 123456
    
mybatis-plus:
  # 别名
  type-aliases-package: com.itheima.mp.domain
  # mapper文件位置
  mapper-locations: classpath*:/mapper/**/*.xml
  configuration:
    # 控制台日志打印,便于查看SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4 实体类

@Data
@TableName("tbl_product")
public class Product {
    private Long id;
    private String pname;
    private String brand;
    private Double price;
    private Integer num;
    private Integer online;
    private Date createTime;
    private Date lastUpdateTime;
    private Integer version;
    private Integer deleted;
}

5 mapper extends BaseMapper

@Repository
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}

注解

@TableName
  • 描述:表名注解,标识实体类对应的表,如果表名和类名一致可以省略
  • 使用位置:实体类
@TableName("tbl_product") //绑定表关系
public class Product {

如果每个表都是以固定前缀开头,可以全局配置表前缀

属性设置 > 全局设置: 如果使用了 @TableName 指定表名,则会忽略全局的表前缀

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tbl_ #表前缀
@TableId
  • 描述:主键注解
  • 使用位置:实体类主键字段
//IdType 指定主键类型
@TableId(value = "id",type = IdType.AUTO)
private Long id;

如果大部分表主键都是自增,可以进行全局设置

属性上的优先级 > 全局设置

mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id #主键策略
      table-prefix: tbl_ #表前缀
IdType
描述
AUTO数据库 ID 自增,手动设置ID无效
NONE默认值 无状态,和未添加@TableId注解效果一样,当手动设置主键id值,会按设置的值插入,若未手动设置值,会使用雪花算法成一个Long值作为主键。
INPUTinsert 前自行 set 主键值,(如果没有手动设置ID值,则ID为null,使用数据库自增策略)
ASSIGN_ID分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
如果手动设置了id值,则使用手动设置的id,否则使用雪花算法生成一个Long类型值作为id
ASSIGN_UUID分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法)
注意注解类型要是varchar类型,长度最少32位
//IdType 指定主键类型
@TableId(value = "id",type = IdType.AUTO)
private Long id;
@Test
public void testIdType(){
    Product product = new Product();
    //product.setId(16L); //不手动设置id
    product.setPname("华为P50");
    product.setPrice(8999D);
    productMapper.insert(product);
}
@TableField
  • 描述:字段注解
  • 使用位置:实体类普通字段
属性类型默认值描述
valueString“”数据库字段名,如果同名可以省略
existbooleantrue是否为数据库表字段,如果表中没有该字段必须设置为false,CRUD都不会包含该字段
selectbooleantrue查询时是否查询该字段,如果设置为false,查询时不包含,但是insert、update、delete包含
fillEnumFieldFill.DEFAULT字段自动填充策略,默认不会自动填充值
@TableField(value = "pname",select = false)
private String name;

fill 自动填充

当对应的属性没有值,执行 insert 或 update 操作自动给属性字段填充指定的值

应用场景:设置默认值,例如每次创建用户,后台程序中都需要设置创建时间、是否启用、用户等级…,这些字段就可以使用默认填充,不必每次都手动设置值

  • FieldFill 填充策略
描述
DEFAULT默认不处理
INSERT插入时填充字段
UPDATE更新时填充字段
INSERT_UPDATE插入和更新时填充字段

实现步骤

第一步:指定填充策略

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date lastUpdateTime;

第二步:设置填充值

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        //给对象中的哪个属性赋值,这里的类型必须和属性类型一致
        this.strictInsertFill(metaObject, "createTime", () -> new Date(), Date.class); // 起始版本 3.3.3(推荐)
        this.strictInsertFill(metaObject, "lastUpdateTime", () -> new Date(), Date.class); // 起始版本 3.3.3(推荐)
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictUpdateFill(metaObject, "createTime", () -> new Date(), Date.class); // 起始版本 3.3.3(推荐)
        this.strictUpdateFill(metaObject, "lastUpdateTime", () -> new Date(), Date.class); // 起始版本 3.3.3(推荐)
    }
}

第三步:测试

@Test
public void testFill(){
    Product product = new Product();
    product.setPname("华为P60");
    product.setPrice(9999D);
    //没有设置时间,让MyBatisPlus自动填充值
    productMapper.insert(product);
}

Mapper CRUD接口

BaseMapper

通用 CRUD 封装 BaseMapper 接口, 泛型 T 为任意实体对象

public interface ProductMapper extends BaseMapper<Product> {

}

MyBatisPlus 提供了 Wrapper 条件构造器接口,用于设置条件

  • QueryWrapper LambdaQueryWrapper 设置查询、删除条件
  • UpdateWrapper LambdaUpdateWrapper 设置修改条件

总结:

  1. dao层实现 BaseMapper 接口,可以省略大量单表CRUD代码编写
  2. 如果CRUD需要条件,使用 Wrapper 接口的实现类来设置条件
查询

前面使用BaseMapper接口中的查询所有和根据id查询,开发中很多时候需要设置查询条件和一些复杂查询

设置查询条件

QueryWrapper 实现类提供了大量设置条件的方法

查询方法说明
eq (equals)等于=
ne (not equals)不等与<>
gt (greater than)大于>
ge (greater equals)大于等于>=
lt (less than)小于<
le (less equals)小于等于<=
like模糊查询 LIKE
notLike模糊查询 NOT LIKE
inIN 查询
notInNOT IN 查询
isNullNULL 值查询
isNotNullIS NOT NULL

需求:查询 库存num > 25 并且创建时间 1997-01-01 <= create_time < 2000-01-01 或者 pname以华为开头的产品信息,结果按 create_time 升序排序

select * from tbl_product
	where num > 25
	and create_time between '1997-01-01' and '2000-01-01'
	or pname like '华为%' 
	order by create_time asc 
  • 对应程序如下
@Test
public void testQueryWrapper(){
    /**
         select * from tbl_product
             where num > 25
             and create_time between '1997-01-01' and '2000-01-01'
             or pname like '华为%'
             order by create_time asc
         */
    //设置查询条件 (where 后面部分内容)
    QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
    // num > 25
    queryWrapper.gt("num",25);
    //and create_time between '1997-01-01' and '2000-01-01'
    queryWrapper.between("create_time","1997-01-01","2000-01-01");
    //or
    queryWrapper.or();
    //pname like '华为%'
    queryWrapper.likeRight("pname","华为");
	//order by create_time asc
    queryWrapper.orderByAsc("create_time");
    
    List<Product> list = productMapper.selectList(queryWrapper);
    for (Product p : list) {
        System.out.println(p);
    }
}
查询优化-动态判断条件

问题:前面查询条件都是写死的,没有判断用户是否输入了查询条件

需求: 前台用户可以查询 price > xx 或 pname liek “%xx%” 的数据,此时查询该如何写 ?

查询优化
@Test
public void testParamQuery2(){

    //查询 price > xx 并且 pname like "%xx%"

    //设置查询条件 (where 后面部分内容)
    QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
    //price > xx    condition:满足条件再设置查询条件
    queryWrapper.gt(productVO.getPrice() != null,"price", productVO.getPrice());
    //pname like '%xx%'  condition:满足条件再设置查询条件
    queryWrapper.like(StringUtils.isNotBlank(productVO.getPname()),"pname", productVO.getPname());

    List<Product> list = productMapper.selectList(queryWrapper);
    for (Product p : list) {
        System.out.println(p);
    }
}
查询优化-lambda查询

字段名都是写死了,万一字段名称变更,例如 pname 改为 product_name ,每个带有 pname 条件的代码都需要修改

如果做到一改全改?

@Test
public void testLambdaQuery(){

    //查询 price > xx 并且 pname like "%xx%"

    // 使用lambda()方法进行字段名获取
    //QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
     通过lambda方式获取字段名称
    //queryWrapper.lambda().gt(productVO.getPrice() != null, Product::getPrice, productVO.getPrice());
     通过lambda方式获取字段名称
    //queryWrapper.lambda().like(StringUtils.isNotBlank(productVO.getPname()),Product::getPname, productVO.getPname());

    // 使用 LambdaQueryWrapper方式查询
    LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();
    // 通过lambda方式获取字段名称
    queryWrapper.gt(productVO.getPrice() != null, Product::getPrice, productVO.getPrice());
    // 通过lambda方式获取字段名称
    queryWrapper.like(StringUtils.isNotBlank(productVO.getPname()),Product::getPname, productVO.getPname());

    List<Product> list = productMapper.selectList(queryWrapper);
    for (Product p : list) {
        System.out.println(p);
    }

}

原理:

LambdaQueryWrapper 获取指定方法对应属性上的 @TableField 注解的 value 属性值,即为 字段名称

如果 @TableFile(value=“username”) 修改,所有对应的lambda查询字段都会改变

查询优化-链式编程

queryWrapper对象的方法返回的还是父类AbstractWrapper类型对象,因此可以直接继续调用方法

image-20211225191605460 image-20211225190829038
@Test
public void testChainQuery(){

    // 使用 LambdaQueryWrapper方式查询
    LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();
    // 链式调用,因为方法返回的还是调用对象
    queryWrapper.gt(productVO.getPrice() != null, Product::getPrice, productVO.getPrice())
        .like(StringUtils.isNotBlank(productVO.getPname()),Product::getPname, productVO.getPname());

    List<Product> list = productMapper.selectList(queryWrapper);
    for (Product p : list) {
        System.out.println(p);
    }

}
设置查询字段

QueryWrapper.select() 可以对select 字段 部分进行设置

需求: 查询库存 num > 25 或者 price > 3000 的商品信息,只展示商品 id、pname、price、num 字段

# SQL语句
select id,pname,price,num
	from tbl_product
	where num > 25
	or 
	price > 3000
@Test
public void testSelect(){
    /*
        需求: 查询库存 num > 25 或者 price > 3000 的商品信息,只展示商品 id、pname、price、num 字段
        select id,pname,price,num
            from tbl_product
            where num > 25
            or
            price > 3000
         */

    LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
    //设置查询的字段 select
    wrapper.select(Product::getId, Product::getPname, Product::getPrice,Product::getNum);
    //设置查询条件 where
    wrapper.gt(Product::getNum,25)
        .or()
        .gt(Product::getPrice,3000);

    List<Product> list = productMapper.selectList(wrapper);
    list.forEach(System.out::println);
}
设置别名

QueryWrapper.select() 设置查询字段的别名

需求:查询每个品牌价格 > 3000 的商品各有多少库存

# SQL
select brand, sum(num) as num 
	from tbl_product tp 
	where price > 3000
	group by brand 
@Test
public void testSelectAlias(){
    /*
    需求:查询每个品牌价格 > 3000 的商品各有多少库存
    select brand, sum(num) as count
        from tbl_product tp
        where price > 3000
        group by brand
     */

    //设置查询条件
    QueryWrapper<Product> wrapper = new QueryWrapper<>();
    //设置select字段和别名
    wrapper.select("brand","sum(num) as count")
        //where条件
        .gt("price",3000)
        .groupBy("brand");

    List<Map<String, Object>> list = productMapper.selectMaps(wrapper);

    //遍历list
    list.forEach(entry -> {
        Set<String> keySet = entry.keySet();
        //遍历map中的键值对
        keySet.forEach(key -> {
            System.out.print(key+":"+entry.get(key)+", ");
        });
        System.out.println();
    });
}

需要在product类中添加 count 属性接收返回结果,并标注该属性在表中不存在

@TableField(exist = false)
private Integer count;
练习

提示: 从 BaseMpper 提供的方法名称 + 返回值类型,判断是否能实现需求

练习1: 查询商品名称以 OPPO开头,价格 > 1500 ,并且是上架状态的商品,结果按价格降序排序 BaseMapper.selectList(wrapper)

Product product = new Product();
        product.setPname("OPPO");
        product.setPrice(1500.0);
        QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.likeRight(product.getPname()!=null,"pname",product.getPname());
        wrapper.gt("price",1500);
        wrapper.eq("online",1);
        wrapper.orderByDesc("price");
        List<Product> products = productMapper.selectList(wrapper);
        for (Product p : products){
            System.out.println(p);
        }

练习2: 统计价格 > 5000 的商品数量 BaseMapper.selectCount(wrapper)

  		QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.gt("price",5000);
        Long aLong = productMapper.selectCount(wrapper);
        System.out.println(aLong);

练习2: 统计每个品牌各有多少件商品,结果按数量升序排序 BaseMapper.selectMaps(wrapper)

		QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.select("brand","SUM(num) num");
        wrapper.groupBy("brand");
        wrapper.orderByAsc("num");
        List<Map<String, Object>> maps1 = productMapper.selectMaps(wrapper);
        maps1.forEach(System.out::println);

练习3: 查询每个品牌的最低价、最高价 BaseMapper.selectMaps(wrapper)

		QueryWrapper<Product> wrapper = new QueryWrapper<>();
        wrapper.select("brand","MIN(price) min","MAX(price) max");
        wrapper.groupBy("brand");
        List<Map<String, Object>> maps = productMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);

练习4: 查询id包含 1,2,6,8 的商品信息 selectBatchIds(Collection)

练习5: 查询id包含 1,2,6,8 并且 pname 不为null, 且是上架状态 的商品信息 (和练习5的区别是这次有条件) BaseMapper.selectList(wrapper)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值