上一节咱们完成了mybatis-plus
的集成,也已经在mp01
中添加相关的实体类的对应的数据库表,这一节咱们来实现基于mybatis-plus
的CRUD操做。java
首先按照上一节的操做,新建一个mp02
的 Module
,能够将mp01
中的内容所有复制过来,同时进行一下修改:mysql
修改 Employee
实体类:git
/**
* mybatis-plus默认会使用实体类的小写类名做为表名
*/
@Data
@ToString
//@TableName(value = "tbl_employee") // ==> 全局的表前缀策略配置
public class Employee {
/**
* @TableId:
* value: 指定表中的主键的列名,若是实体属性名与列名一致,能够省略不指定
* type:指定主键策略
* 设置主键自增
*/
// @TableId(value = "id", type = IdType.AUTO) // ==> 全局表主键生成策略
private Integer id;
@TableField(value = "last_name")
private String lastName;
private String email;
private Integer gender;
private Integer age;
// 当前字段是否在数据库中存在,若是不存在则忽略该字段插入到数据库中
@TableField(exist = false)
private Double salary;
}
复制代码
修改applicationContext.xml
文件,添加下面的内容:github
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.mp.beans"></property>
<!-- 注入配置-->
<!--<property name="configuration" ref="configuration"></property>-->
<!-- 注入全局配置策略-->
<property name="globalConfig" ref="globalConfiguration"></property>
<property name="plugins">
<!-- 分页查询插件 -->
<bean id="paginationInterceptor" class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<property name="dialectType" value="mysql" />
</bean>
</property>
</bean>
<!--这个等于Mybatis的全局配置文件,若是在MybatisSqlSessionFactoryBean里面已经配置了configLocation属性(外部加载Mybatis全局配置文件),就不能再配置configuration属性-->
<bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
<!--开启驼峰命名-->
<property name="mapUnderscoreToCamelCase" value="true"/>
<!--日志打印SQL语句-->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
</bean>
<!-- 定义mybatis-plus全局策略配置-->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<!-- 全局主键策略-->
<property name="dbConfig" ref="dbConfig"></property>
</bean>
<!-- 这里-->
<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<!-- 全局表主键生成策略 -->
<property name="idType" value="AUTO"></property>
<!-- 全局的表前缀策略配置 -->
<property name="tablePrefix" value="tbl_"></property>
</bean>
复制代码
完整的xml文件以下:spring
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring
http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 数据源 -->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource"
class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 事务管理器 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 基于注解的事务管理 -->
<tx:annotation-driven
transaction-manager="dataSourceTransactionManager"/>
<!-- 配置 SqlSessionFactoryBean
mybatis提供的:org.mybatis.spring.SqlSessionFactoryBean
mybatis-plus提供的:3.2.0 com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean
2.3 com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
-->
<bean id="sqlSessionFactoryBean"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.mp.beans"></property>
<!-- 注入配置-->
<!--<property name="configuration" ref="configuration"></property>-->
<!-- 注入全局配置策略-->
<property name="globalConfig" ref="globalConfiguration"></property>
<property name="plugins">
<!-- 分页查询插件 -->
<bean id="paginationInterceptor" class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<property name="dialectType" value="mysql" />
</bean>
</property>
</bean>
<!--这个等于Mybatis的全局配置文件,若是在MybatisSqlSessionFactoryBean里面已经配置了configLocation属性(外部加载Mybatis全局配置文件),就不能再配置configuration属性-->
<bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
<!--开启驼峰命名-->
<property name="mapUnderscoreToCamelCase" value="true"/>
<!--日志打印SQL语句-->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
</bean>
<!-- 定义mybatis-plus全局策略配置-->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<!-- 全局主键策略-->
<property name="dbConfig" ref="dbConfig"></property>
</bean>
<!-- 这里-->
<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<!-- 全局表主键生成策略 -->
<property name="idType" value="AUTO"></property>
<!-- 全局的表前缀策略配置 -->
<property name="tablePrefix" value="tbl_"></property>
</bean>
<!--
配置 mybatis 扫描 mapper 接口的路径
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage"
value="com.mp.mapper"></property>
</bean>
</beans>
复制代码
在 mp02\src\main\java\com\mp\mapper\EmployeeMapper.java
路径下新建 EmployeeMapper.java
mapper文件,EmployeeMapper
类须要继承 BaseMapper<T>
,代码以下:sql
/**
* @description: mapper接口
* 实现方式:
* 基于 Mybatis
* 须要编写 EmployeeMapper 接口,并手动编写 CRUD 方法
* 提供 EmployeeMapper.xml 映射文件,并手动编写每一个方法对应的 SQL 语句.
*
* 基于 MP
* 只须要建立 EmployeeMapper 接口, 并继承 BaseMapper 接口.这就是使用 MP
* 须要完成的全部操做,甚至不须要建立 SQL 映射文件。
* BaseMapper<T>:泛型指定的就是当前mapper接口所操做的实体类型
*/
public interface EmployeeMapper extends BaseMapper<Employee> {
// 使用 mybatis 插入数据时获取主键值
// Integer insertEmployee(Employee employee );
// <insert useGeneratedKeys="true" keyProperty="id" > SQL...</insert>
}
复制代码
修改mp02的pom.xml
文件:数据库
<?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">
<parent>
<artifactId>mybatis-plus-in-action</artifactId>
<groupId>com.demo.mybatis-plus</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mp02</artifactId>
<dependencies>
<!-- mp 依赖
mybatis-plus 会自动维护mybatis 以及 mybatis-spring相关的依赖
Mybatis 及 Mybatis-Spring 依赖请勿加入项目配置,以避免引发版本冲突!!!Mybatis-Plus 会自动帮你维护!
-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!--junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
复制代码
上面的准备工做作完了,下面咱们能够来进行CRUD操做了:apache
/**
* 通用 插入操做
*/
@Test
public void testCommonInsert() {
// 初始化employee 对象
Employee employee = new Employee();
employee.setLastName("MP");
employee.setEmail("mp@github.com");
employee.setGender(1);
employee.setAge(22);
employee.setSalary(2000.0);
// 插入到数据库 insert方法在插入时, 会根据实体类的每一个属性进行非空判断,只有非空的属性对应的字段才会出现到SQL语句中
int ret = employeeMapper.insert(employee);
System.out.println("result:" + ret);
// 获取当前数据在数据库中的主键值
Integer key = employee.getId();
System.out.println("key:" + key);
}
复制代码
/**
* 通用更新操做
*/
@Test
public void testCommonUpdate() {
// 初始化employee 对象
Employee employee = new Employee();
employee.setId(19);
employee.setLastName("MP");
employee.setEmail("mybatis-plus@github.com");
employee.setGender(0);
// employee.setAge(33); // 没有传的字段不会被更新
// 单个更新
Integer result = employeeMapper.updateById(employee);
System.out.println("result:" + result);
// 批量更新, 若是 Wrapper 为空,则所有更新
result = employeeMapper.update(employee, null);
System.out.println("result:" + result);
}
复制代码
/**
* 通用查询操做
*/
@Test
public void testCommonSelect() {
// 一、经过id查询
Employee employee = employeeMapper.selectById(14);
System.out.println(employee);
// 二、经过多个列查询 id+lastName
Employee employee1 = new Employee();
employee1.setId(13);
employee1.setLastName("White");
Wrapper<Employee> employeeWrapper = new QueryWrapper<Employee>(employee1);
Employee selectOne = employeeMapper.selectOne(employeeWrapper);
System.out.println(selectOne);
// 三、经过多个id进行查询
List<Integer> idList = new ArrayList<Integer>();
idList.add(11);
idList.add(12);
idList.add(13);
idList.add(14);
List<Employee> employeeList = employeeMapper.selectBatchIds(idList);
System.out.println("employeeList:" + employeeList);
// 四、经过 selectMaps 查询
Map<String, Object> columnMap = new HashMap<String, Object>();
columnMap.put("last_name", "White");
columnMap.put("gender", 0);
List<Employee> selectByMap = employeeMapper.selectByMap(columnMap);
System.out.println("selectByMap:" + selectByMap);
// 五、分页查询 selectPage 使用分页查询须要在 applicationContext.xml 中添加 分页查询插件 配置
IPage<Employee> employeeIPage = employeeMapper.selectPage(new Page<>(2, 1), null);
System.out.println("employeeIPage:" + employeeIPage.getRecords());
}
复制代码
注意:
缓存
一、columnMap中使用的是数据库中的字段名,而不是实体类的字段名mybatis
二、分页查询操做须要在 applicationContext.xml
中添加分页插件,完整代码能够看前面的完整配置:
<property name="plugins">
<!-- 分页查询插件 -->
<bean id="paginationInterceptor" class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<property name="dialectType" value="mysql" />
</bean>
</property>
复制代码
/**
* 通用删除操做
*/
@Test
public void testCommonDelete() {
// 一、根据id删除
Integer result = employeeMapper.deleteById(15);
System.out.println("result:" + result);
// 二、根据 deleteByMap 条件删除
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("last_name","MP");
columnMap.put("email","mybatis-plus@github.com");
Integer ret = employeeMapper.deleteByMap(columnMap);
System.out.println("ret:" + ret);
// 三、批量删除
List<Integer> idList = new ArrayList<Integer>();
idList.add(18);
idList.add(19);
Integer deleteResult = employeeMapper.deleteBatchIds(idList);
System.out.println("deleteResult:" + deleteResult);
}
复制代码
注意:
columnMap中使用的是数据库中的字段名,而不是实体类的字段名
public class TestMp {
private ApplicationContext ioc = new
ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper = ioc.getBean("employeeMapper", EmployeeMapper.class);
/**
* 通用删除操做
*/
@Test
public void testCommonDelete() {
// 一、根据id删除
Integer result = employeeMapper.deleteById(15);
System.out.println("result:" + result);
// 二、根据 deleteByMap 条件删除
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("last_name","MP");
columnMap.put("email","mybatis-plus@github.com");
Integer ret = employeeMapper.deleteByMap(columnMap);
System.out.println("ret:" + ret);
// 三、批量删除
List<Integer> idList = new ArrayList<Integer>();
idList.add(18);
idList.add(19);
Integer deleteResult = employeeMapper.deleteBatchIds(idList);
System.out.println("deleteResult:" + deleteResult);
}
/**
* 通用查询操做
*/
@Test
public void testCommonSelect() {
// 一、经过id查询
Employee employee = employeeMapper.selectById(14);
System.out.println(employee);
// 二、经过多个列查询 id+lastName
Employee employee1 = new Employee();
employee1.setId(13);
employee1.setLastName("White");
Wrapper<Employee> employeeWrapper = new QueryWrapper<Employee>(employee1);
Employee selectOne = employeeMapper.selectOne(employeeWrapper);
System.out.println(selectOne);
// 三、经过多个id进行查询
List<Integer> idList = new ArrayList<Integer>();
idList.add(11);
idList.add(12);
idList.add(13);
idList.add(14);
List<Employee> employeeList = employeeMapper.selectBatchIds(idList);
System.out.println("employeeList:" + employeeList);
// 四、经过 selectMaps 查询
Map<String, Object> columnMap = new HashMap<String, Object>();
columnMap.put("last_name", "White");
columnMap.put("gender", 0);
List<Employee> selectByMap = employeeMapper.selectByMap(columnMap);
System.out.println("selectByMap:" + selectByMap);
// 五、分页查询 selectPage 使用分页查询须要在 applicationContext.xml 中添加 分页查询插件 配置
IPage<Employee> employeeIPage = employeeMapper.selectPage(new Page<>(2, 1), null);
System.out.println("employeeIPage:" + employeeIPage.getRecords());
}
/**
* 通用更新操做
*/
@Test
public void testCommonUpdate() {
// 初始化employee 对象
Employee employee = new Employee();
employee.setId(19);
employee.setLastName("MP");
employee.setEmail("mybatis-plus@github.com");
employee.setGender(0);
// employee.setAge(33);
// 单个更新
Integer result = employeeMapper.updateById(employee);
System.out.println("result:" + result);
// 批量更新, 若是 Wrapper 为空,则所有更新
result = employeeMapper.update(employee, null);
System.out.println("result:" + result);
}
/**
* 通用 插入操做
*/
@Test
public void testCommonInsert() {
// 初始化employee 对象
Employee employee = new Employee();
employee.setLastName("MP");
employee.setEmail("mp@github.com");
employee.setGender(1);
employee.setAge(22);
employee.setSalary(2000.0);
// 插入到数据库 insert方法在插入时, 会根据实体类的每一个属性进行非空判断,只有非空的属性对应的字段才会出现到SQL语句中
int ret = employeeMapper.insert(employee);
System.out.println("result:" + ret);
// 获取当前数据在数据库中的主键值
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);
}
}
复制代码
完成上面的操做后,mp02的代码结构以下所示:
在咱们使用 BaseMapper 提供的方法来进行CRUD操做的时候,有没有想过为何咱们能直接使用这些方法,而这些方法又是何时加载到容器中呢?
xxxMapper 继承了 BaseMapper, BaseMapper 中提供了通用的 CRUD 方法,这些方法来源于 BaseMapper, 有方法就必须有 SQL, 由于 MyBatis 最终仍是须要经过SQL 语句操做数据。觉得 Mybatis-Plus 是在MyBatis 的基础上作了加强,因此咱们有必要了解一些前置知识:
MyBatis 源码中比较重要的一些对象, MyBatis 框架的执行流程 Configuration
:MyBatis 的全局配置对象 MappedStatement
:MappedStatement保存全部的SQL方法和执行语句 ……..
经过debug日志咱们能够看到,在咱们执行 employeeMapper.deleteById(id)方法以前,Mybatis-Plus已经帮咱们把 BaseMapper 中内置的方法就加载到了MappedStatement中。
咱们在 employeeMapper.deleteById(id)方法中打下断点,能够看到
A. employeeMapper
的本质是com.baomidou.mybatisplus.core.override.MybatisMapperProxy
,若是是mybatis 则看到的是 org.apache.ibatis.binding.MapperProxy
B. MapperProxy 中两个重要对象 sqlSession
–>SqlSessionFactory
C. SqlSessionFacotry
中又有两个重要的对象 Configuration
→ MappedStatements
每个 mappedStatement 都表示 Mapper 接口中的一个方法与 Mapper 映射文件中的一个 SQL。
Mybatis-Plus 在启动就会挨个分析 xxxMapper 中的方法,而且将对应的 SQL 语句处理好,保存到 configuration
对象中的 mappedStatements
中. D. 本质: Configuration
: MyBatis 或者 MP 全局配置对象
MappedStatement
:一个 MappedStatement 对象对应 Mapper 配置文件中的一个select/update/insert/delete 节点,主要描述的是一条 SQL 语句 SqlMethod
: 枚举对象 , MP 支持的 SQL 方法 TableInfo
: 数据库表反射信息 ,能够获取到数据库表相关的信息 SqlSource
: SQL 语句处理对象 MapperBuilderAssistant
: 用于缓存、 SQL 参数、查询方剂结果集处理等,经过 MapperBuilderAssistant 将每个 mappedStatement添加到 configuration 中的 mappedstatements 中 。
在下图所示的位置处打断点,能够观测到MP加载内置方法的整个过程,咱们在后边自定义全局操做添加自定义方法的时候,还会碰到AbstractMethod
对象和AbstractSqlInjector
对象,
Mybatis-Plus启动注入SQL的执行流程入下入所示(中间省略若干非关键步骤):
以上是基本的 CRUD 操做, 你所见,咱们仅仅须要继承一个 BaseMapper 便可实现大部分单表 CRUD 操做。 BaseMapper 提供了多达 17 个方法给你们使用, 能够极其方便的实现单1、批量、分页等操做。 极大的减小开发负担。
综上,基于 mybatis-plus
的CRUD演示就完成了,现有一个需求,咱们须要分页查询 tbl_employee 表中,年龄在 18~50 之间性别为男且姓名为 xx 的全部用户,这时候咱们该如何实现上述需求呢?
MyBatis : 须要在 SQL 映射文件中编写带条件查询的 SQL,并基于 PageHelper 插件完成分页. 实现以上一个简单的需求,每每须要咱们作不少重复单调的工做。 普通的 Mapper可以解决这类痛点吗? MP: 依旧不用编写 SQL 语句, MP 提供了功能强大的条件构造器 AbstractWrapper
。下面咱们就能够进入到下一节AbstractWrapper条件构造器
了。
相关示例完整代码:mybatis-plus-in-action