1、Mybatis的问题
【简介】: 每个实体类对应一个实体类,对应一个mapper接口,对应一个mapper.xml文件,每个mapper接口都有重复的crud方法,每一个mapper.xml都有重复的crud的sql配置。
2、Mybatis-plus简介
1、官网
- Mybatis-plus
- Mybatis-plus
2、MyBatis是什么?
1、简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
2、特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
3、框架结构
4、Mybatis与Mybatis-plus的对比
项目 | 优点 | 缺点 |
---|---|---|
mybatis | 1、开发者自己操作sql语句,比较灵活 2、sql与业务代码分离,易于阅读和维护 3、提供了动态sql可以根据需求灵活操作 | 1、简单的增删改查也需要自己书写sql语句 2、需要维护大量的xml文件 3、如果需要拓展必须依赖第三方插件、比如分页操作 |
Mybatis-plus | 1、提供无sql操作的增删改查 2、内置了多个插件、如分页、代码生成器、性能分析器、 3、提供了功能丰富的条件构造器可以实现无sql开发 | —— |
3、快速开始
1、创建数据库表
CREATE TABLE `department` (`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '表示部门编号,由两位数字所组成',`name` varchar(14) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '部门名称,最多由14个字符所组成',`sn` varchar(13) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '部门编号',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 42 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;INSERT INTO `department` VALUES (1, '财务部', 'cwb');
INSERT INTO `department` VALUES (2, '调研部', 'dyb');
INSERT INTO `department` VALUES (3, '销售部', 'xsb');
INSERT INTO `department` VALUES (4, '运营部', 'yyb');
INSERT INTO `department` VALUES (5, '小卖部', 'xmb');
INSERT INTO `department` VALUES (6, '总经办', 'zjb');CREATE TABLE `employee` (`id` bigint(0) NOT NULL AUTO_INCREMENT,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`age` int(0) NULL DEFAULT NULL,`admin` bit(1) NULL DEFAULT NULL,`dept_id` bigint(0) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1327139013313564674 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;INSERT INTO `employee` VALUES (1, 'admin', '1', 'admin@abc.com', 40, b'1', 6);
INSERT INTO `employee` VALUES (2, '赵总', '1', 'zhaoz@wolfcode.cn', 35, b'0', 1);
INSERT INTO `employee` VALUES (3, '赵一明', '1', 'zhaoym@wolfcode.cn', 25, b'0', 1);
INSERT INTO `employee` VALUES (4, '钱总', '1', 'qianz@wolfcode.cn', 31, b'0', 2);
INSERT INTO `employee` VALUES (5, '钱二明', '1', 'qianem@wolfcode.cn', 25, b'0', 2);
INSERT INTO `employee` VALUES (6, '孙总', '1', 'sunz@wolfcode.cn', 35, b'0', 3);
INSERT INTO `employee` VALUES (7, '孙三明', '1', 'sunsm@wolfcode.cn', 25, b'0', 3);
INSERT INTO `employee` VALUES (8, '李总', '1', 'liz@wolfcode.cn', 35, b'0', 4);
INSERT INTO `employee` VALUES (9, '李四明', '1', 'lism@wolfcode.cn', 25, b'0', 4);
INSERT INTO `employee` VALUES (10, '周总', '1', 'zhouz@wolfcode.cn', 19, b'0', 5);
INSERT INTO `employee` VALUES (11, '周五明', '1', 'zhouwm@wolfcode.cn', 25, b'0', 5);
INSERT INTO `employee` VALUES (12, '吴总', '1', 'wuz@wolfcode.cn', 41, b'0', 5);
INSERT INTO `employee` VALUES (13, '吴六明', '1', 'wulm@wolfcode.cn', 33, b'0', 5);
INSERT INTO `employee` VALUES (14, '郑总', '1', 'zhengz@wolfcode.cn', 35, b'0', 3);
2、创建springBoot项目导入如下依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><!--SpringBoot版本--><version>2.4.3</version><relativePath/></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>11</java.version></properties><dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><!-- Mysql驱动包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- 阿里数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.6</version></dependency><!-- 阿里JSON解析器 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version><scope>provided</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>
3、编写配置文件
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverdruid:url: jdbc:mysql://localhost:3306/mybatis-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=trueusername: rootpassword: *******
logging:level:cn:simplelife:mapper: debug
4、书写实体类
@Data
public class Employee {private Long id;private String name;private String password;private String email;private int age;private int admin;private Long deptId;
}
5、书写mapper接口
public interface EmployeeMapper extends BaseMapper<Employee> {
}
6、在启动类中mapper扫描
@SpringBootApplication
@MapperScan(basePackages = "cn.simplelife.mapper")
public class MyBatisApplication {public static void main(String[] args) {SpringApplication.run(MyBatisApplication.class, args);}
}
7、编写测试类
@SpringBootTest
public class CRUTest {@Autowiredprivate EmployeeMapper employeeMapper;@Testpublic void testSave() {Employee employee = new Employee();employee.setAdmin(1);employee.setAge(18);employee.setDeptId(1L);employee.setEmail("dafei@wolfcode");employee.setName("dafei");employee.setPassword("111");employeeMapper.insert(employee);}@Testpublic void testUpdate() {Employee employee = new Employee();employee.setId(1327139013313564673L);employee.setAdmin(1);employee.setAge(18);employee.setDeptId(1L);employee.setEmail("dafei@wolfcode.cn");employee.setName("xiaofei");employee.setPassword("111");employeeMapper.updateById(employee);}@Testpublic void testDelete() {employeeMapper.deleteById(1327139013313564673L);}@Testpublic void testGet() {System.out.println(employeeMapper.selectById(1327139013313564673L));}@Testpublic void testList() {System.out.println(employeeMapper.selectList(null));}
}
8、测试结果
4、常用注解
1、@TableName
- 描述:表名注解,标识实体类对应的数据库中的表。(表示一种映射关系)
- 使用位置:数据库数据表对应的实体类。
- 举例:
@TableName("sys_user")
public class User {private Long id;private String name;private Integer age;private String email;
}
- 属性及取值
注意事项: - 因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 Entity 字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。
- 而对于 typeHandler 属性,MyBatis 只支持写在 2 个地方:
- 定义在 resultMap 里,作用于查询结果的封装
- 定义在 insert 和 update 语句的 #{property} 中的 property 后面(例:#{property,typehandler=xxx.xxx.xxx}),并且只作用于当前 设置值
- 除了以上两种直接指定 typeHandler 的形式,MyBatis 有一个全局扫描自定义 typeHandler 包的配置,原理是根据您的 property 类型去找其对应的 typeHandler 并使用
2、@TableId
- 描述:主键注解
- 使用位置:实体类中的主键字段
- 举例:
@TableName("sys_user")
public class User {@TableIdprivate Long id;private String name;private Integer age;private String email;
}
- 属性及取值
- IdType
3、@TableField
- 描述:字段主键(非主键),当数据库中字段名称与实体类中属性名称不一致的时候使用
- 使用位置:字段
- 举例:
@TableName("sys_user")
public class User {@TableIdprivate Long id;@TableField("nickname")private String name;private Integer age;private String email;
}
- 常用属性及取值
4、@Version(了解)
- 描述:乐观锁注解、标记 @Version 在字段上
5、@EnumValue(了解)
- 描述:普通枚举类注解(注解在枚举字段上)
6、@TableLogic(了解)
- 描述:表字段逻辑处理注解(逻辑删除)
7、@KeySequence(了解)
- 描述:序列主键策略 oracle
- 属性:value、dbType
8、@OrderBy(了解)
- 描述:内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
5、创建springBoot项目
1、导入依赖
<dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><!-- Mysql驱动包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- 阿里数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.6</version></dependency><!-- 阿里JSON解析器 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version><scope>provided</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>
2、编写配置信息
# spring相关配置
spring:# 数据源配置datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverdruid:url: jdbc:mysql://localhost:3306/mybatis-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=trueusername: password:
# 配置sql打印日志
#logging:
# level:
# cn:
# simplelife:
# mapper: debug
# 配置sql日志打印
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、编写启动类
@SpringBootApplication
@MapperScan(basePackages = "cn.simplelife.mapper")
public class MyBatisApplication {public static void main(String[] args) {SpringApplication.run(MyBatisApplication.class, args);}
}
4、创建对应目录结构
5、创建数据库及实体类
@Data
public class Employee {@TableId(value = "id", type = IdType.AUTO)private Long id;private String name;private String password;private String email;private Integer age;private Integer admin;private Long deptId;@TableField(exist = false)private Department department;
}
6、通用Mapper接口
// 自定义接口继承mybatis-plus的基础接口
// 泛型传入的是当前类需要操作的实体对象
// 1、在mapper中没有定义接口,为什么可以使用crud方法?
//答案:EmployeeMapper继承了BaseMapper接口,自然可以继承其中的方法
// 2、没有书写sql语句,为什么可以进行数据库操作?
// 答案:
// 1、mybatis-plus自动帮我们拼接了sql
//2、mybatis-plus拼接sql过程 以查询为例
// select id,name,age,password,email from employee
// 要想拼接出sql必须提供表名和查询字段
public interface EmployeeMapper extends BaseMapper<Employee> {
}
7、CRUD测试
@SpringBootTest
public class CRUTest {@Autowiredprivate EmployeeMapper employeeMapper;@Testpublic void testSave() {Employee employee = new Employee();employee.setAdmin(1);employee.setAge(18);employee.setDeptId(1L);employee.setEmail("zhangsan@wolfcode");employee.setName("张三");employee.setPassword("111");employeeMapper.insert(employee);}@Testpublic void testUpdate() {Employee employee = new Employee();employee.setId(1L);employee.setAdmin(1);employee.setAge(18);employee.setDeptId(1L);employee.setEmail("dafei@wolfcode.cn");employee.setName("xiaofei");employee.setPassword("111");employeeMapper.updateById(employee);}@Testpublic void testDelete() {employeeMapper.deleteById(21L);}@Testpublic void testGet() {System.out.println(employeeMapper.selectById(1L));}@Testpublic void testList() {System.out.println(employeeMapper.selectList(null));}
}
执行结果:
8、条件构造器
1、AbstractWrapper
2、allEq(全部相等)
- 方法
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
// params : key为数据库字段名,value为字段值
// null2IsNull : 为true则在map的value为null时调用 isNull 方法,为false时则忽略value为null的
- 案例
allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null
allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'
3、eq(相等)
- 方法
eq(R column, Object val)
eq(boolean condition, R column, Object val)
- 举例
eq("name", "老王")--->name = '老王'
4、ne(不等)
- 方法
ne(R column, Object val)
ne(boolean condition, R column, Object val)
- 举例
ne("name", "老王")--->name <> '老王'
5、gt(大于)
- 方法
gt(R column, Object val)
gt(boolean condition, R column, Object val)
- 举例
gt("age", 18)---> age > 18
6、ge(大于等于)
- 方法
ge(R column, Object val)
ge(boolean condition, R column, Object val)
- 举例
ge("age", 18)---> age >= 18
7、lt(小于)
- 方法
lt(R column, Object val)
lt(boolean condition, R column, Object val)
- 举例
lt("age", 18)---> age < 18
8、le(小于等于)
- 方法
le(R column, Object val)
le(boolean condition, R column, Object val)
- 举例
le("age", 18)---> age <= 18
9、between (区间)
- 方法
between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)
- 举例
between("age", 18, 30)--->age between 18 and 30
10、notBetween(区间)
- 方法
notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)
- 举例
notBetween("age", 18, 30)--->age not between 18 and 30
11、like(‘%值%’)
- 方法
like(R column, Object val)
like(boolean condition, R column, Object val)
- 举例
like("name", "王")--->name like '%王%'
12、notLike(‘值%’)
- 方法
notLike(R column, Object val)
notLike(boolean condition, R column, Object val)
- 举例
notLike("name", "王")---> name not like '%王%'
13、likeLeft(‘%值’)
- 方法
likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)
- 举例
likeLeft("name", "王")--->name like '%王'
14、likeRight(‘值%’)
- 方法
likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)
- 举例
likeRight("name", "王")---> name not like '王%'
15、notLikeLeft
- 方法
notLikeLeft(R column, Object val)
notLikeLeft(boolean condition, R column, Object val)
- 举例
notLikeLeft("name", "王")--->name not like '%王'
16、notLikeRight
- 方法
notLikeRight(R column, Object val)
notLikeRight(boolean condition, R column, Object val)
- 举例
notLikeRight("name", "王")--->name not like '王%'
17、isNull
- 方法
isNull(R column)
isNull(boolean condition, R column)
- 举例
isNull("name")--->name is null
18、isNotNull
- 方法
isNotNull(R column)
isNotNull(boolean condition, R column)
- 举例
isNotNull("name")--->name is not null
19、in
- 方法
in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)
- 举例
in("age",{1,2,3})--->age in (1,2,3)
20、notIn
- 方法
notIn(R column, Collection<?> value)
notIn(boolean condition, R column, Collection<?> value)
- 举例
notIn("age",{1,2,3})--->age not in (1,2,3)
21、inSql
- 方法
inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
- 举例
inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)
22、notInSql
- 方法
notInSql(R column, String inValue)
notInSql(boolean condition, R column, String inValue)
- 举例
notInSql("age", "1,2,3,4,5,6")--->age not in (1,2,3,4,5,6)
notInSql("id", "select id from table where id < 3")--->id not in (select id from table where id < 3)
23、groupBy
- 方法
groupBy(R... columns)
groupBy(boolean condition, R... columns)
- 举例
groupBy("id", "name")--->group by id,name
24、orderByDesc
- 方法
orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
- 举例
orderByDesc("id", "name")--->order by id DESC,name DESC
25、orderBy
- 方法
orderBy(boolean condition, boolean isAsc, R... columns)
- 举例
orderBy(true, true, "id", "name")--->order by id ASC,name ASC
26、having
- 方法
having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)
- 举例
having("sum(age) > 10")--->having sum(age) > 10
having("sum(age) > {0}", 11)--->having sum(age) > 11
27、or
- 方法
or()
or(boolean condition)
- 举例
eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'
or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')
28、and
- 方法
and(Consumer<Param> consumer)
and(boolean condition, Consumer<Param> consumer)
- 举例
and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')
29、nested
- 方法
nested(Consumer<Param> consumer)
nested(boolean condition, Consumer<Param> consumer)
- 举例
nested(i -> i.eq("name", "李白").ne("status", "活着"))--->(name = '李白' and status <> '活着')
30、apply
- 方法
apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
- 举例
apply("id = 1")--->id = 1
apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
31、last
- 方法
last(String lastSql)
last(boolean condition, String lastSql)
- 举例
last("limit 1")
32、exists
- 方法
exists(String existsSql)
exists(boolean condition, String existsSql)
- 举例
exists("select id from table where age = 1")--->exists (select id from table where age = 1)
33、exists
- 方法
notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
- 举例
notExists("select id from table where age = 1")--->not exists (select id from table where age = 1)
9、QueryWrapper
1、select
- 方法
select(String... sqlSelect)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
- 举例
select("id", "name", "age")
select(i -> i.getProperty().startsWith("test"))
10、UpdateWrapper
1、set
- 方法
set(String column, Object val)
set(boolean condition, String column, Object val)
- 举例
set("name", "老李头")
set("name", "")---> 数据库字段值变为空字符串
set("name", null)---> 数据库字段值变为null
1、setSql
- 方法
setSql(String sql)
- 举例
setSql("name = '老李头'")