一、入门 HelloWorld
1、通用 CRUD
(1)提出问题:
- 假设我们已存在一张 tbl_employee 表,且已有对应的实体类 Employee,实现 tbl_employee 表的 CRUD 操作我们需要做什么呢?
(2)实现方式:
基于 Mybatis
- 需要编写 EmployeeMapper 接口,并手动编写 CRUD 方法
- 提供 EmployeeMapper.xml 映射文件,并手动编写每个方法对应的 SQL 语句
- 只需要创建 EmployeeMapper 接口, 并继承 BaseMapper 接口.这就是使用 MP
- 需要完成的所有操作,甚至不需要创建 SQL 映射文件。
2、编码实现
2.1、通用方法插入
(1)创建一个EmployeeMapper接口
package org.apache.mybatisplus.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.apache.mybatisplus.bean.Employee;
/**
* Mapper接口
*
* 基于Mybatis: 在Mapper接口中编写CRUD相关的方法 提供Mapper接口所对应的SQL映射文件 以及 方法对应的SQL语句.
*
* 基于MP: 让XxxMapper接口继承 BaseMapper接口即可.
* BaseMapper<T> : 泛型指定的就是当前Mapper接口所操作的实体类类型
*
*/
public interface EmployeeMapper extends BaseMapper<Employee> {
}
一定很好奇继承BaseMapper有什么用途吧?当我们点击进去查看,发现它实现了以下接口方法:
/**
* <p>
* 插入一条记录
* </p>
*
* @param entity 实体对象
* @return int
*/
Integer insert(T entity);
/**
* <p>
* 插入一条记录
* </p>
*
* @param entity 实体对象
* @return int
*/
Integer insertAllColumn(T entity);
/**
* <p>
* 根据 ID 删除
* </p>
*
* @param id 主键ID
* @return int
*/
Integer deleteById(Serializable id);
/**
* <p>
* 根据 columnMap 条件,删除记录
* </p>
*
* @param columnMap 表字段 map 对象
* @return int
*/
Integer deleteByMap(@Param("cm") Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,删除记录
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return int
*/
Integer delete(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 删除(根据ID 批量删除)
* </p>
*
* @param idList 主键ID列表
* @return int
*/
Integer deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/**
* <p>
* 根据 ID 修改
* </p>
*
* @param entity 实体对象
* @return int
*/
Integer updateById(@Param("et") T entity);
/**
* <p>
* 根据 ID 修改
* </p>
*
* @param entity 实体对象
* @return int
*/
Integer updateAllColumnById(@Param("et") T entity);
/**
* <p>
* 根据 whereEntity 条件,更新记录
* </p>
*
* @param entity 实体对象
* @param wrapper 实体对象封装操作类(可以为 null)
* @return
*/
Integer update(@Param("et") T entity, @Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 whereEntity 条件,更新记录
* </p>
*
* @param setStr set字符串
* @param wrapper 实体对象封装操作类(可以为 null)
* @return
*/
Integer updateForSet(@Param("setStr") String setStr, @Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 ID 查询
* </p>
*
* @param id 主键ID
* @return T
*/
T selectById(Serializable id);
/**
* <p>
* 查询(根据ID 批量查询)
* </p>
*
* @param idList 主键ID列表
* @return List<T>
*/
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/**
* <p>
* 查询(根据 columnMap 条件)
* </p>
*
* @param columnMap 表字段 map 对象
* @return List<T>
*/
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,查询一条记录
* </p>
*
* @param entity 实体对象
* @return T
*/
T selectOne(@Param("ew") T entity);
/**
* <p>
* 根据 Wrapper 条件,查询总记录数
* </p>
*
* @param wrapper 实体对象
* @return int
*/
Integer selectCount(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 entity 条件,查询全部记录
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<T> selectList(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* 注意: 只返回第一个字段的值
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<Object>
*/
List<Object> selectObjs(@Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 entity 条件,查询全部记录(并翻页)
* </p>
*
* @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT)
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<T> selectPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录(并翻页)
* </p>
*
* @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT)
* @param wrapper 实体对象封装操作类
* @return List<Map<String, Object>>
*/
List<Map<String, Object>> selectMapsPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);
(2)测试插入操作的问题
package org.apache.test;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 插入操作
*/
@Test
public void testCommonInsert() {
//初始化Employee对象
Employee employee = new Employee();
employee.setLastName("MP");
employee.setEmail("mp@apache.mybatis-plus.com");
employee.setGender(1);
employee.setAge(22);
// insert方法在插入时, 会根据实体类的每个属性进行非空判断,只有非空的属性对应的字段才会出现到SQL语句中
Integer result =employeeMapper.insert(employee);
System.out.println("result: " + result );
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
启动程序报错
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Could not set
property 'id' of 'class org.apache.mybatisplus.bean.Employee'
with value '1437004223093944322' Cause: java.lang.IllegalArgumentException:
argument type mismatch
分析原因:MybatisPlus会默认使用实体类的类名到数据中找对应的表
解决方案:需要在实体类的主键id添加一个叫“@TableId”注解与指定主键策略
再次运行,发现又出现另一个异常
解决方案:需要在实体类上添加@TableName(value="tbl_employee")
再次运行,发现数据被插入进来了
以上说明:MyBatisPlus插入ok
2.2、全局策略配置
(1)首先除了配置以下参数:
<!--TODO 定义MybatisPlus的全局策略配置-->
<bean id ="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!-- 在2.3版本以后,dbColumnUnderline 默认值就是true -->
<property name="dbColumnUnderline" value="true"></property>
<!-- 全局的主键策略 -->
<property name="idType" value="0"></property>
<!-- 全局的表前缀策略配置 -->
<property name="tablePrefix" value="tbl_"></property>
</bean>
(2)其次,还需要在把它注入到注入全局MP策略配置中,不然无法生效
如图所示:
(3)当参数生效后,我们就不需要在实体类添加@TableName与@TableId注解
(4)控制台打印输出信息:
2.3、注解“@TableField”
当实际开发过程中,我们知道在2.3版本以后,dbColumnUnderline 默认值就是true,回顾
以往再次去讨论是没意义的,比如用2.3版本之前,不支持该功能特性,而它的默认值为false时,在开发问题会遇到一些的bug。
以上,表明:数据库与实体类的驼峰命名不一致
为了解决这个问题:@TableField可以解决
插入数据获取主键值
当有的时候,数据库没有这个字段,而我们是人为的bug设置后 会出现一些错误的
为了解决这个问题,我们可以在实体类采用注解“@TableField” 用排除自己判断,去解决这个问题
2.4、insertAllColumn方法
package org.apache.test;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 插入操作
*/
@Test
public void testCommonInsert() {
//初始化Employee对象
Employee employee = new Employee();
employee.setLastName("MP");
employee.setEmail("mp@apache.mybatis-plus.com");
// insert方法在插入时, 会根据实体类的每个属性进行非空判断,只有非空的属性对应的字段才会出现到SQL语句中
employee.setSalary(2000.0);
Integer result = employeeMapper.insertAllColumn(employee);
System.out.println("result: " + result );
//获取当前数据在数据库中的主键值
Integer key = employee.getId();
System.out.println("key:" + key );
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出信息:
说明:insert与insertAllColumn方法插入的区别
insert方法在插入时, 会根据实体类的每个属性进行非空判断,只有非空的属性对应的字段才会出现到SQL语句中
insertAllColumn方法在插入时, 不管属性是否非空, 属性所对应的字段都会出现到SQL语句中
3、更新操作
package org.apache.test;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 更新操作
*/
@Test
public void testCommonUpdate() {
//初始化修改对象
Employee employee = new Employee();
employee.setId(7);
employee.setLastName("小泽老师");
employee.setEmail("xz@sina.com");
employee.setGender(0);
Integer result = employeeMapper.updateAllColumnById(employee);
System.out.println("result: " + result );
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出信息:
4、 查询操作
4.1、通过selectById方法操作
代码实现:
package org.apache.test;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 查询操作
*/
@Test
public void testCommonSelect() {
//1. 通过id查询
Employee employee = employeeMapper.selectById(7);
System.out.println(employee);
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出:
4.2、通过selectOne方法操作
代码实现:
package org.apache.test;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 查询操作
*/
@Test
public void testCommonSelect() {
//2. 通过多个列进行查询 id + lastName
Employee employee = new Employee();
employee.setId(7);
employee.setLastName("小泽老师");
System.out.println("result: " +result );
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出:
比如:查询多个的话
它的条件就会出现多个
4.3、通过selectBatchIds方法实现批量查询
代码实现:
package org.apache.test;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 查询操作
*/
@Test
public void testCommonSelect() {
//3. 通过多个id进行查询 <foreach>
List<Integer> idList = new ArrayList<>();
idList.add(4);
idList.add(5);
idList.add(6);
idList.add(7);
List<Employee> emps = employeeMapper.selectBatchIds(idList);
System.out.println(emps);
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出:
4.4、selectByMap方法条件查询
代码实现:
package org.apache.test;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 查询操作
*/
@Test
public void testCommonSelect() {
//4. 通过Map封装条件查询
Map<String,Object> columnMap = new HashMap<>();
columnMap.put("last_name", "Tom");
columnMap.put("gender", 1);
List<Employee> emps = employeeMapper.selectByMap(columnMap);
System.out.println(emps);
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出:
分析控制台打印的sql语句:
SELECT id,last_name AS lastName,email,gender,age
FROM tbl_employee WHERE gender = ? AND last_name = ?
4.5、通过selectPage方法实现分页查询
查询BaseMapper类,源码的实现接口【了解一下】
编码实现:
package org.apache.test;
import com.baomidou.mybatisplus.plugins.Page;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 查询操作
*/
@Test
public void testCommonSelect() {
//5. 分页查询,分3页,每页查询2条数据
List<Employee> emps = employeeMapper.selectPage(new Page<>(3, 2), null);
System.out.println(emps);
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出
分析控制台打印的sql语句:
SELECT id,last_name AS lastName,
email,gender,age FROM tbl_employee
5、删除操作
5.1、通过deleteById方法
编码实现:
package org.apache.test;
import com.baomidou.mybatisplus.plugins.Page;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 删除操作
*/
@Test
public void testCommonDelete() {
//1 .根据id进行删除
Integer result = employeeMapper.deleteById(11);
System.out.println("result: " + result );
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出:
分析控制台打印的sql语句:
DELETE FROM tbl_employee WHERE id=?
5.2、通过deleteByMap方法实现条件删除
编码实现:
package org.apache.test;
import com.baomidou.mybatisplus.plugins.Page;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 删除操作
*/
@Test
public void testCommonDelete() {
//2. 根据 条件进行删除
Map<String,Object> columnMap = new HashMap<>();
columnMap.put("last_name", "MP");
columnMap.put("email", "mp@apache.mybatis-plus.com");
Integer result = employeeMapper.deleteByMap(columnMap);
System.out.println("result: " + result );
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
打印输出:【删除4条数据】
分析控制台打印的sql语句:
DELETE FROM tbl_employee WHERE last_name = ? AND email = ?
5.3、通过deleteBatchIds方法实现批量删除
编码实现:
package org.apache.test;
import com.baomidou.mybatisplus.plugins.Page;
import org.apache.mybatisplus.bean.Employee;
import org.apache.mybatisplus.mapper.EmployeeMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
/**
* 通用 删除操作
*/
@Test
public void testCommonDelete() {
//3. 批量删除
List<Integer> idList = new ArrayList<>();
idList.add(3);
idList.add(4);
idList.add(5);
Integer result = employeeMapper.deleteBatchIds(idList);
System.out.println("result: " + result );
}
@Test
public void testEnvironment() throws Exception {
DataSource ds = ioc.getBean("dataSource", DataSource.class);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
控制台打印输出:
6、MP 启动注入 SQL 原理分析
(1)问题: xxxMapper
- 继承了 BaseMapper<T>, BaseMapper 中提供了通用的 CRUD 方法, 方法来源于 BaseMapper, 有方法就必须有 SQL, 因为 MyBatis 最终还是需要通过 SQL 语句操作数据
(2)通过现象看到本质
- employeeMapper 的本质 org.apache.ibatis.binding.MapperProxy
- MapperProxy 中 sqlSession –>SqlSessionFactory
(3)SqlSessionFacotry 中 → Configuration→ MappedStatements
- 每一个 mappedStatement 都表示 Mapper 接口中的一个方法与 Mapper 映射文件 中的一个 SQL。
- MP 在启动就会挨个分析 xxxMapper 中的方法,并且将对应的 SQL 语句处理好,保存到 configuration 对象中的 mappedStatements 中
本质:
- Configuration: MyBatis 或者 MP 全局配置对象
- MappedStatement:一个 MappedStatement 对象对应 Mapper 配置文件中的一个
- select/update/insert/delete 节点,主要描述的是一条 SQL 语句
- SqlMethod : 枚举对象 ,MP 支持的 SQL 方法
- TableInfo:数据库表反射信息 ,可以获取到数据库表相关的信息
- SqlSource: SQL 语句处理对象
- MapperBuilderAssistant: 用于缓存、SQL 参数、查询方剂结果集处理等,通过 MapperBuilderAssistant 将每一个 mappedStatement添加到 configuration 中的 mappedstatements 中
7、通用 CRUD 小结
(1)以上是基本的 CRUD 操作,如您所见,我们仅仅需要继承一个 BaseMapper 即可实现 大部分单表 CRUD 操作。BaseMapper 提供了多达 17 个方法给大家使用, 可以极其方 便的实现单一、批量、分页等操作。极大的减少开发负担,难道这就是 MP 的强大之处 了吗?
(2)提出需求: