目录
目录
1 Mybatis调用流程
- Spring容器为接口创建代理对象. Spring容器启动对象立即创建
- 根据 @Autowired 注解动态注入Mapper接口的代理对象
- 用户通过Mapper接口调用方法.(执行业务操作)
- Mybatis根据接口方法动态匹配xml的映射文件
(1.根据Mapper的接口路径匹配xml映射文件中的 com.jt.mapper.UserMapper
(2.根据接口的方法匹配xml映射文件中的sql id 之后执行Sql语句
(3.Mybatis将结果集封装为对象之后返回.
2 Mybatis与MybatisPlus
2.1 MybatisPlus特点
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
2.2 Mybatis与MybatisPlus区别
区别1:MybatisPlus自动注入基本 CURD
Mybatis是一种操作数据库的框架,提供一种Mapper类,支持让你用java代码进行增删改查的数据库操作,前提是你得先在xml中写好sql语句,但是每当要写一个业务逻辑的时候都要在DAO层写一个方法,再对应一个SQL,即使是简单的条件查询、或者是仅仅改变了一个条件都要在DAO层新增一个方法,还是有点麻烦。而MybatisPlus自动为Mybatis生成简单的增删改查SQL语句的工具,可以省去手写简单SQL语句的时间,只在XML中编写其它需要的SQL语句,所以MP与mybatis配合使用的话可以很好的提高开发效率。
区别2:MybatisPlus只增强不改变
Mybatis-Plus是一个Mybatis的增强工具,它在Mybatis的基础上做了增强,却不做改变。我们在使用Mybatis-Plus之后既可以使用Mybatis-Plus的特有功能,又能够正常使用Mybatis的原生功能。Mybatis-Plus是为简化开发、提高开发效率而生,但它也提供了一些很有意思的插件,比如SQL性能监控、乐观锁、执行分析等。
3 Mybatis与MybatisPlus对比
3.1 准备数据表demo_user
3.2 项目结构
3.2.1 导入依赖包
向pom.xml中导入相关依赖包
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<!--跳过测试类打包-->
<skipTests>true</skipTests>
</properties>
<!--原则:按需导入-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<!--springboot启动项(器),在包的内部springboot已经完成了项目的“整合”
(配置)用户拿来就能用-->
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--springboot数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<!--springboot项目与Maven整合的一个插件
可以通过插件 执行项目打包/测试/文档生成等操作
注意事项:该插件不能省略,如果省略启动时报错
(省略时)项目发布时:java -jar xxxx.jar 报错没有主清单信息!!
-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
3.2.2 创建对象关系映射User类
注意user类是与demo_user表对应,属性与字段对应。
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data//使用了lombok插件,不用手写set、get、tostring等方法
@Accessors(chain = true)
@TableName("demo_user")
public class User implements Serializable {
//ID代表主键,不是id字段
@TableId(type=IdType.AUTO)//主键自增
//@TableField("name")//如果属性与字段同名(包括驼峰规则)注解可以省略
private Integer id;
private String name;
private Integer age;
private String sex;
}
3.2.3 创建配置文件
创建UserMapper.xml文件,里面包含大量的SQL语句
<?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.jt.mapper.UserMapper">
<!--查询表中所有数据-->
<select id="getAll" resultType="User">
select * from demo_user
</select>
<!--查询指定id的数据-->
<select id="getById" resultType="User">
select * from demo_user where id = #{id}
</select>
<!--查询指定sex和age的数据-->
<select id="getByAgeSex" resultType="User">
select * from demo_user where sex = #{sex} and age = #{age}
</select>
<!--查询name包含指定字符的数据-->
<select id="getNameContain" parameterType="String" resultType="User">
select * from demo_user where name like "%"#{str}"%"
</select>
<!--查询按照age>xx,并按照age和sex排序的数据-->
<select id="toSort" resultType="User">
select * from demo_user where age > #{age} order by age asc ,sex desc
</select>
<!--根据name/age动态查询数据,如果name/age不为null则拼接where条件-->
<select id="getByNameAge" resultType="User">
select * from demo_user
<if test="name!=null">where name=#{name} <if test="age!=null">and age=#{age}</if></if>
</select>
<!--随机查询5个id的数据-->
<select id="getByIdS" resultType="User">
select * from demo_user where id in
<foreach collection="array" index="index" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>
<!--查询指定sex=xx和age>xx的数据-->
<select id="getBySexAge" resultType="User">
select * from demo_user where sex = #{sex} and age > #{age}
</select>
<!--根据name更新数据-->
<update id="updateByName">
update demo_user set name = #{aftername},age = #{age} where name = #{beforename}
</update>
<!--根据id主键删除单个数据-->
<delete id="delById">
delete from demo_user where id = #{id}
</delete>
<!--根据id主键删除多条数据-->
<delete id="delByIdS">
delete from demo_user where id in
<foreach collection="arr" item="id" index="index" open="(" separator="," close=")">#{id}</foreach>
</delete>
<!--根据指定name删除数据-->
<delete id="delByName">
delete from demo_user where name = #{name}
</delete>
<!--根据指定id主键更新数据-->
<update id="updateById1">
update demo_user set name = #{name},sex = #{sex},age = #{age} where id = #{id}
</update>
<!--插入数据-->
<insert id="insertInto">
insert into demo_user (name,sex,age) values (#{name},#{sex},#{age})
</insert>
</mapper>
创建application.yml文件,里面包含要连接数据库、驱动器、用户名与密码等配置信息
server:
port: 8090
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
username: root
password: root
#如果数据库密码以数字0开头 则必须使用""号包裹
#password: "01234"
#SpringBoot整合Mybatis配置
#定义别名包:实现对象映射
#只做增强不做改变
mybatis-plus:
type-aliases-package: com.jt.pojo
#映射文件加载路径
mapper-locations: classpath:/mybatisplus/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
#不打印日志
debug: false
logging:
level:
com.jt.mapper: debug
3.2.4 创建Mapper层接口文件
包路径要与xml配置文件的namespace值要一致,方法名和返回值类型分别要与xml文件的id和返回值类型要一致。
package com.jt.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
//继承接口时必须添加泛型对象,否则映射表报错
public interface UserMapper extends BaseMapper<User> {
//查询表中所有数据
List<User> getAll();
//询指定id的数据
List<User> getById(Integer id);
//查询指定sex和age的数据
List<User> getByAgeSex(@Param("sex") String sex,@Param("age") Integer age);
//查询name包含指定字符的数据
List<User> getNameContain(String str);
//按年龄排序和性别排序
List<User> toSort(Integer age);
//根据name/age动态查询数据,如果name/age不为null则拼接where条件
List<User> getByNameAge(@Param("name") String name, @Param("age") Integer age);
//随机查询5个id的数据
List<User> getByIdS(@Param("array") int[] id);
//查询按照age>xx,并按照age和sex排序的数据
List<User> getBySexAge(@Param("sex") String sex,@Param("age") Integer age);
//根据name更新数据
void updateByName(@Param("beforename") String beforename,@Param("aftername") String aftername,@Param("age") Integer age);
//根据id主键删除单个数据
void delById(Integer id);
//根据id主键删除多条数据
void delByIdS(@Param("arr") Integer[] id);
//根据指定name删除数据
void delByName(String name);
//根据指定id主键更新数据
void updateById1(@Param("id") Integer id,@Param("name") String name,@Param("sex") String sex,@Param("age") Integer age);
//插入数据(主键自增)
void insertInto(@Param("name") String name,@Param("sex") String sex,@Param("age") Integer age);
}
3.3 对比测试
3.3.1 创建测试类
注释里的(MP)代表该方法是使用MybatisPlus实现的。
说明:以下的所有方法都经过测试,并未出现错误结果,请根据实际情况向对应方法中传入合适参数。
package com.jt;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
* 测试注解只能在test包下使用
* 此注解表示可以从spring容器里面拿对象
* */
@SpringBootTest
public class TestSpringBoot {
@Autowired
private UserMapper userMapper;//代理对象
@Test//查询表中所有数据
public void testGetAll(){
// System.out.println(userMapper.getClass());
List<User> userList = userMapper.getAll();
System.out.println(userList);
}
@Test//查询指定id的数据
public void testGetById(){
System.out.println(userMapper.getById(50));
}
@Test//(MP)查询指定id的数据
public void getByID(){
User user = userMapper.selectById(1);
System.out.println(user);
}
@Test//查询指定sex和age的数据
public void testGetByAgeSex(){
System.out.println(userMapper.getByAgeSex("男",18));
}
@Test//(MP)查询指定sex和age的数据
public void getByAS(){
User user = new User();
user.setSex("女").setAge(18);
QueryWrapper queryWrapper = new QueryWrapper(user);
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
@Test//查询指定sex=xx和age>xx的数据
public void getBySexAge(){
List<User> userlist = userMapper.getBySexAge("男", 18);
System.out.println(userlist);
}
@Test//(MP)查询指定sex=xx和age>xx的数据
public void getBygtAS(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18).eq("sex","女");
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
@Test//查询name包含指定字符的数据
public void testGetNameContain(){
System.out.println(userMapper.getNameContain("孙"));
}
@Test//(MP)查询name包含指定字符的数据
public void getContain1(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "君");
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
@Test//查询按照age>xx,并按照age和sex排序的数据
public void testToSort(){
System.out.println(userMapper.toSort(18));
}
@Test//(MP)查询按照age>xx,并按照age和sex排序的数据
public void testOrderBy(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18).orderByDesc("age").orderByAsc("sex");
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
@Test//随机查询5个id的数据
public void testGetByIdS(){
int[] arr = {5,6,7,8,9};
System.out.println(userMapper.getByIdS(arr));
}
@Test//(MP)随机查询5个id的数据
public void testIn(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.in("id",1,3,5,7,6);
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
Integer[] ids = new Integer[]{1,3,5,7,6};
List<Integer> idList = Arrays.asList(ids);
List<User> userList1 = userMapper.selectBatchIds(idList);
System.out.println(userList1);
}
@Test//根据name/age动态查询数据,如果name/age不为null则拼接where条件
public void testGetByNameAge(){
System.out.println(userMapper.getByNameAge("如花",null));
}
@Test//(MP)根据name/age动态查询数据,如果name/age不为null则拼接where条件
public void testSelectNS(){
String name = "小乔";
String sex = "女";
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
boolean nameFlag = StringUtils.hasLength(name);
boolean sexFlag = StringUtils.hasLength(sex);
queryWrapper.eq(nameFlag,"name", name).eq(sexFlag,"sex", sex);
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println(userList);
}
@Test//根据name更新数据
public void updateByName(){
userMapper.updateByName("云英", "吴饭饭", 18);
System.out.println("成功");
}
@Test//(MP)根据name更新数据
public void updateTest(){
User user = new User();
user.setAge(100).setName("祝君好运");
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "吴反反");
userMapper.update(user,updateWrapper);
}
@Test//根据id主键删除单个数据
public void delById(){
userMapper.delById(235);
System.out.println("成功");
}
@Test//(MP)根据id主键删除单个数据
public void testDel(){
userMapper.deleteById(232);
System.out.println("成功");
}
@Test//根据id主键删除多条数据
public void delByIds(){
Integer[] arr = {233,234};
userMapper.delByIdS(arr);
System.out.println("成功");
}
@Test//(MP)根据id主键删除多条数据
public void testDelIds(){
Integer[] ids = new Integer[]{231,227};
List<Integer> integers = Arrays.asList(ids);
userMapper.deleteBatchIds(integers);
System.out.println("成功");
}
@Test//根据指定name删除数据
public void delByName(){
userMapper.delByName("吴饭饭");
System.out.println("成功");
}
@Test//(MP)根据指定name删除数据
public void testDelNA(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "吴亦凡").eq("age", 30);
userMapper.delete(queryWrapper);
System.out.println("成功");
}
@Test//根据指定id主键更新数据
public void updateById1(){
userMapper.updateById1(196, "饭饭", "男", 1000);
System.out.println("成功");
}
@Test//(MP)根据指定id主键更新数据
public void updateByIdMP(){
User user = new User();
user.setName("云英").setSex("男").setAge(16).setId(196);
userMapper.updateById(user);
System.out.println("成功");
}
@Test//插入数据
public void insertUser(){
userMapper.insertInto("凡凡", "男", 222);
System.out.println("成功");
}
@Test//(MP)插入数据
public void testInsert(){
User user = new User();
user.setId(555).setName("吴亦凡").setAge(30).setSex("男");
userMapper.insert(user);
System.out.println("成功");
}
}
3.3.2 MybatisPlus实现原理
- 用户执行User对象入库操作 userMapper.insert(user);
- 由于接口方法中需要传递泛型对象,则根据用户配置查找对应的泛型对象
- 根据用户的接口获取Mapper接口的父级接口BaseMapper,根据BaseMapper中的泛型对象 获取信息User.class类型
- 根据User.class 动态获取@TableName(“demo_user”) 获取对象对应的表名.之后通过@TableField(“name”)绑定与之对应的字段. 至此对象与表完成了映射.
- 根据上述的映射关系,动态的拼接Sql语句.
- MP将动态生成的Sql交给Mybatis执行最终实现数据入库操作!!!
为什么不建议你使用Mybatis-plus
代码整洁本身是为了降低修改成本的,MP本末倒置。
整洁的代码意味着阅读代码的时间成本降低,但是如果使用MP,在你阅读service层代码时,还会看到在代码中拼装SQL执行条件的部分。这样一来,service层代码和数据库访问层代码混淆在一起,让程序员很难专注的关心某一细节。常见的SQL操作MP可以通过Wrapper构造SQL执行条件,程序员就看不到一条条SQL语句,而阅读和检查SQL逻辑是比较耗费精力的事,MP只不过是以后期维护成本为代价换取开发时貌似整洁的优势。
SQL执行错误是最容易修改的错误
如果你使用纯Mybatis相比MP会更容易爆出SQL执行错误。其实大可不必太在乎这个问题,如果是SQL执行逻辑有问题,那不管MP还是Mybatis都会报错。如果是SQL语法错误,由于SQL语法错误在Java开发中错误日志非常显眼,所以你可以通过复制日志中报错的SQL然后代码全局搜索快速定位出错位置,而且SQL语法错误基本都是字段名写错或者执行条件顺序错误,这都是非常容易修改的BUG。
纯Mybatis具有更好的代码复用性
比如,我要查询班级id为7,的所有班干部同学。你在纯Mybatis中会一次性将SQL写进XML文件中,如果在项目中这条SQL需要调用N次,你只需要在这N个地方都传参调用即可。但如果用了MP,你需要将相同的代码复制N次。