MyBatisPlus的CRUD操作

一、MP简介

它是 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

那么它是怎么增强的呢?

其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。

二、SpringBoot项目整合 MP

引入了mybatis-plus,就不用再引入mybatis的依赖了,因为 mybatis-plus 中包含了。

1、pom:

		<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xdja</groupId>
    <artifactId>springboot-06-mybatisplus</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- 要想是一个SpringBoot项目的话,必须继承 parent 启动器-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
    </parent>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
    </properties>
    <dependencies>
 <!--mybatis-plus的springboot支持-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.1</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </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>
            <optional>true</optional>
        </dependency>
      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.xdja.springsecurity.MyApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2、启动类

在启动类中添加mapper接口的扫描器。注意@MapperScan注解是spring和mybatis整合包下提供的。

/**
 * @author liangqi
 * @date 2020/9/6 10:16
 *
 * @MapperScan("com.xdja.mp.mapper")  扫描 mapper接口所在的包,生成代理实现类对象并注入到容器中
 */
@SpringBootApplication
@MapperScan("com.xdja.mp.mapper")  // mapper包的扫描注解
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

3、配置文件配置 application.yml

  • 配置数据源

  • 指定 mapper映射文件 xml的位置

  • 给实体类Bean 起别名

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mybatisplus?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL =false
    username: root
    password: root
    
#mybatis-plus
#指定mapper映射文件的位置
#给实体类Bean 起别名
mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.xdja.mp.entity

4、分页插件的配置

只需要将MP的分页插件配置到spring容器中即可使用 MP的分页API。

/**
 * 分页插件注入
 */
@Configuration
public class MyBatisConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        // 分页插件的拦截器
        PaginationInterceptor interceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        interceptor.setOverflow(true);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        interceptor.setLimit(-1);
        return interceptor;
    }
}

// 列表查询——分页+条件
Page<User> page = new Page<>(2, 2);
IPage<User> userIPage = userMapper.selectPage(page, wrapper);

5、entity:

/**
 * employee实体类Bean
 *
 * @TableName(value = "tb_employee") 指定表名
 * @Data  //lombok
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "tb_employee")
public class Employee {
    /**
     * 指定自增策略
     * 		value属性:与数据库表中列名映射。若名称一样(或者满足驼峰和下划线格式)可省略value。
     *      type属性:指定 主键值得自增策略。   IdType.AUTO自增
     */
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;

    /**
     * value:若属性名与列名不一致,指定映射关系
     * exist:标明数据表 中有没有与这个属性 对应的列
     */
    @TableField(value = "name",exist = true)
    private String name;

    private String email;

    private Integer age;
}

6、PersonMapper接口编写:

继承mybatis-plus的基础类 BaseMapper<Employee> ,它提供了很多的单表操作CRUD的方法。

除此之外,还可以像 一般的mybatis一样,自定义操作接口,然后编写 mapper.xml文件进行使用。
自定义接口如下:

public interface PersonMapper extends BaseMapper<Person> {
    /**
     * 自定义操作接口(mybatis方式):
     * 获取 person列表
     * @return
     */
    List<Person> getPersonList();
}

PersonMapper.xml编写:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xdja.mp.mapper.PersonMapper">
    <!--映射关系-->
    <resultMap id="person" type="com.xdja.mp.entity.Person"/>

    <!--sql-->
    <select id="getPersonList" resultMap="person">
        SELECT  * FROM tb_person
    </select>
</mapper>

7、接下来就可以写一个测试类 ,注入PersonDao 对象,然后调用 MP提供的CRUD方法进行操作

/**
 * @author liangqi
 * @date 2020/9/4
 */
@SpringBootTest(classes = MySpringApplication.class)
@RunWith(SpringRunner.class)
public class Test1 {

    @Autowired
    private PersonMapper personMapper;

    @Test
    public void getById(){
		//1、根据id查询 selectById()
        User user = userMapper.selectById(1);

        //2、查询所有  selectList(null);
        // 参数为查询条件QueryWrapper对象,无条件,则传入null
        List<User> userList = userMapper.selectList(null);
        
        //3、1创建一个条件构造器。 泛型:为要查询数据的实体类类型
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //3、2.将 where username = app 等值查询 这个条件封装到 条件构造器
        //sql中的username = app 对应于eq("username","app")
        // 参数1 字段名 参数2 要查询的值
        queryWrapper.eq("username","app");//3、3.通过条件构造器 查询
        User user = userMapper.selectOne(queryWrapper);
    }
}

三、mp的通用crud实现单表操作:

需求:
存在一张tb_employee 表,且已有对应的实体类 Employee,实现tb_employee 表的 CRUD 操作我们需要做什么呢?

基于 Mybatis:
需要编写 EmployeeMapper 接口,并在 EmployeeMapper.xml 映射文件中手动编写 CRUD 方法对应的sql语句。

基于 MP:
只需要创建 EmployeeMapper 接口, 并继承 BaseMapper 接口。
我们已经有了Employee、tb_employee了,并且EmployeeDao也继承了BaseMapper了,接下来就使用crud方法。

1、insert操作:

insert 一个不包含id的实体对象。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/spring-dao.xml"})
public class test {
    @Autowired
    private EmplopyeeDao emplopyeeDao;
    @Test
    public void testInsert(){
        Employee employee = new Employee();
        employee.setLastName("东方不败");
        employee.setEmail("dfbb@163.com");
        employee.setGender(1);
        employee.setAge(20);
        emplopyeeDao.insert(employee);
        //自增后的id写回到该实体中
        System.out.println(employee.getId());
    }
}

执行添加操作,直接调用insert方法传入实体即可。
传入的实体,若它的属性值为null,则不会插入该字段。
自增后的id会回填到对象中。

2、update操作(2种):

更新操作有2种,一种是根据id更新,另一种是根据条件更新。
第一种:根据id查询 updateById

@Test
public void testUpdate(){
        Employee employee = new Employee();
        employee.setId(1);
        employee.setLastName("更新测试");
    
        //根据id进行更新,没有传值的属性 就不会更新(常用)
        emplopyeeDao.updateById(employee);
    
        //根据id进行更新,没传值的属性 就更新为null
        emplopyeeDao.updateAllColumnById(employee);
}

【注意】 注意这两个update操作的区别:


updateById方法,没有传值的字段不会进行更新,比如只传入了lastName,那么age、gender等属性就会保留原来的值;

updateAllColumnById方法,顾名思义,会更新所有的列,没有传值的列会更新为null。

第二种:根据QueryWrapper条件查询 update

 /**
* update
*   参数1  一个User对象(用于set值) 若写null的话,那么set的值,where的值都需要封装在wrapper对象中。
*   参数2  条件构造器 (用于构造set值 和 where条件)
*/
@Test public void testUpdate() {
	User user = new User(); 
	user.setAge(22); //更新的字段 
	
	//更新的条件 
	QueryWrapper<User> wrapper = new QueryWrapper<>(); 
	wrapper.eq("id", 6); 
	
	//执行更新操作 
	int result = this.userMapper.update(user, wrapper); 
	System.out.println("result = " + result); 
}

或者通过:UpdateWrapper进行更新:

@Test public void testUpdate() { 
	//更新的条件以及字段 
	UpdateWrapper<User> wrapper = new UpdateWrapper<>();
	wrapper.eq("id", 6).set("age", 23); 
	
	//执行更新操作 
	int result = this.userMapper.update(null, wrapper); 
	System.out.println("result = " + result); 
}
3、select操作:

(1)、根据id查询单个数据

Employee employee = emplopyeeDao.selectById(1);

(2)、根据查询条件 查询单个数据

 //1.创建一个条件构造器。 泛型:为要查询数据的实体类类型
QueryWrapper<User> queryWrapper = new QueryWrapper<>();

//2.将 where username = app 等值查询 这个条件封装到 条件构造器
//sql中的username = app 对应于eq("username","app")
// 参数1 字段名 参数2 要查询的值
queryWrapper.eq("username","app");

//3.通过条件构造器 查询
User user = userMapper.selectOne(queryWrapper);

【注意】:

这个方法的sql语句就是where id = 1 and last_name = 更新测试,若是符合这个条件的记录不止一条,那么就会报错。

(3)、 根据查询条件 查询多条数据

//方法1“
public void crudWrapperDemo2() {
        //1.创建条件构造器
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //2.构造条件  条件构造器的方法的第一个参数基本上都是字段名
        queryWrapper.eq("username","lisi").or().eq("password","123456");
        //3.查询
        /**
         * 批量查询 selectList() 方法中如果参数是null 就是查询所有
         */
        List<User> userList = userMapper.selectList(queryWrapper);
    }


//方法2:
//构造条件
Map<String,Object> columnMap = new HashMap<>();
columnMap.put("last_name","东方不败");//写表中的列名
columnMap.put("gender","1");

//列表查询
List<Employee> employees = emplopyeeDao.selectByMap(columnMap);
System.out.println(employees.size());

【注意】:

查询条件用map集合封装,columnMap,key写的是数据表中的列名,而非实体类的属性名。

比如属性名为lastName,数据表中字段为last_name,这里应该写的是last_name。selectByMap方法返回值用list集合接收。

(4)、 通过id批量查询:

//id集合
List<Integer> idList = new ArrayList<>();
idList.add(1);
idList.add(2);
idList.add(3);

//查询
List<Employee> employees = emplopyeeDao.selectBatchIds(idList);
System.out.println(employees);

注: 把需要查询的id都add到list集合中,然后调用selectBatchIds方法,传入该list集合即可,该方法返回的是对应id的所有记录,所有返回值也是用list接收。

(5)、 分页+Wrapper 查询:

public void test1(){
        // 构造条件
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("age", 22);

        // 列表查询——分页+条件
        Page<User> page = new Page<>(2, 2);
        IPage<User> userIPage = userMapper.selectPage(page, wrapper);

        System.out.println("总的数据条数:" + userIPage.getTotal());
        System.out.println("总页数:" + userIPage.getPages());

        //记录s
        List<User> records = userIPage.getRecords();

    }

注:

selectPage方法就是分页查询,在page中传入分页信息,后者为分页条件

这个分页其实并不是物理分页,而是内存分页。也就是说,查询的时候并没有limit语句。等配置了分页插件后才可以实现真正的分页

(6)、条件查询 总的记录数 selectCount:

@Test public void testSelectCount() { 
	//构造where 条件
	QueryWrapper<User> wrapper = new QueryWrapper<User>(); 
	wrapper.gt("age", 23); //年龄大于23岁 
	
	//根据条件查询数据条数
	Integer count = this.userMapper.selectCount(wrapper); 
	System.out.println("count = " + count); 
}
4、delete操作:

(1)、 根据id删除:

emplopyeeDao.deleteById(1);

(2)、 根据 columnMap 条件删除:

Map<String,Object> columnMap = new HashMap<>();
columnMap.put("gender",0);
columnMap.put("age",18);

int n = emplopyeeDao.deleteByMap(columnMap);

注: 该方法与selectByMap类似,将条件封装在columnMap中,然后调用deleteByMap方法,传入columnMap即可,返回值是Integer类型,表示影响的行数。

也可以根据 封装 Wrapper 条件构造器:

@Test public void testDeleteByMap() { 
	User user = new User(); 
	user.setAge(20); 
	user.setName("张三"); 
	
	//将实体对象进行包装,包装为条件 
	QueryWrapper<User> wrapper = new QueryWrapper<>(user); 
	
	int result = this.userMapper.delete(wrapper); 
	System.out.println("result = " + result); 
}

(3)、 根据id批量删除(参数为 Integer类型的id集合):

 List<Integer> idList = new ArrayList<>();
 idList.add(1);
 idList.add(2);
 emplopyeeDao.deleteBatchIds(idList);

注: 该方法和selectBatchIds类似,把需要删除的记录的id装进idList,然后调用deleteBatchIds,传入idList即可。

四、全局策略配置:

通过上面的小案例我们可以发现,实体类需要加@TableName注解指定数据库表名,通过@TableId注解指定id的增长策略。实体类少倒也无所谓,实体类一多的话也麻烦。

所以可以进行全局策略配置。

如此一来,实体类中的@TableName注解和@TableId注解就可以去掉了。

五、条件构造器(QueryWrapper):

以上基本的 CRUD 操作,我们仅仅需要继承一个 BaseMapper 即可实现大部分单表 CRUD 操作。BaseMapper 提供了多达 17 个方法供使用, 可以极其方便的实现单一、批量、分页等操作,极大的减少开发负担。但是mybatis-plus的强大不限于此,请看如下需求该如何处理:

需求:
我们需要分页查询 tb_employee 表中,年龄在 18~50 之间性别为男且姓名为 xx 的所有用户,这时候我们该如何实现上述需求呢?

使用MyBatis : 需要在 SQL 映射文件中编写带条件查询的 SQL,并用PageHelper 插件完成分页. 实现以上一个简单的需求,往往需要我们做很多重复单调的工作。
使用MP: 依旧不用编写 SQL 语句,MP 提供了功能强大的条件构造器 ------ QueryWrapper

接下来就直接看几个案例体会QueryWrapper的使用。

1、分页查询年龄在18 - 50且gender为0、姓名为tom的用户:

IPage<User> employees = userMapper.selectPage(new Page<User>(1,3),
                new QueryWrapper<User>()
                        .between("age",18,50)
                        .eq("gender",0)
                        .eq("last_name","tom")
        );

注: 由此案例可知,分页查询和之前一样,new 一个page对象传入分页信息即可。至于分页条件,new 一个QueryWrapper对象,调用该对象的相关方法即可。between方法三个参数,分别是column、value1、value2,该方法表示column的值要在value1和value2之间;eq是equals的简写,该方法两个参数,column和value,表示column的值和value要相等。注意column是数据表对应的字段,而非实体类属性字段。

2、查询gender为0且名字中带有老师、或者邮箱中带有a的用户:

List<User> users= userMapper.selectList(
                new QueryWrapper<User>()
                        .eq("gender",0)
                        .like("last_name","老师")
                        //.or()//和or new 区别不大
                        .or()
                        .like("email","a")
        );

注: 未说分页查询,所以用selectList即可,用QueryWrapper的like方法进行模糊查询,like方法就是指column的值包含value值,此处like方法就是查询last_name中包含“老师”字样的记录;“或者”用or或者orNew方法表示,这两个方法区别不大,用哪个都可以,可以通过控制台的sql语句自行感受其区别。

3、查询gender为0,根据age排序,简单分页:

 List<User> employees = userMapper.selectList(
                new QueryWrapper<User>()
                        .eq("gender",0)
                        .orderByAsc("age")
                        .last("desc limit 1,3")//在sql语句后面追加last里面的内容(分页)
        );

注: 若要降序,可以使用orderByDesc方法,也可以如案例中所示用last方法;last方法就是将last方法里面的value值追加到sql语句的后面,在该案例中,追加了limit 1,3所以可以进行分页。

5、根据条件更新:

@Test
public void testEntityWrapperUpdate(){
        Employee employee = new Employee();
        employee.setLastName("苍老师");
        employee.setEmail("cjk@sina.com");
        employee.setGender(0);
        emplopyeeDao.update(employee,
                new QueryWrapper<Employee>()
                .eq("last_name","tom")
                .eq("age",25)
        );
}

注: 该案例表示把last_name为tom,age为25的所有用户的信息更新为employee中设置的信息。

6、根据条件删除:

emplopyeeDao.delete(
        new QueryWrapper<Employee>()
        .eq("last_name","tom")
        .eq("age",16)
);

注: 该案例表示把last_name为tom、age为16的所有用户删除。

六、几个注解

@TableField : 常常解决的问题有2个:
value 属性:

  • 1、对象中的属性名和字段名不一致的问题(非驼峰)
  • 2、对象中的属性字段在表中不存在的问题
    exist属性:
    当该字段在数据库表中不存在时。

其他用法:select=true
sql查询的结果中 是否要包含这个字段。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值