别在一直纠结MyBatis-Plus了,MyBatis才是真正的主人!

2024-01-07 00:40

本文主要是介绍别在一直纠结MyBatis-Plus了,MyBatis才是真正的主人!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

为什么要整理这样一篇文章咧❓

现在大家开发的过程中,基本上已经不用手写Mapper了,大多数可能用MyBatis-Plus啊,tkMapper啊,这些等等的封装了MyBatis的省时省力的东西,并不是说不用这些,当然省事省力就是给自己节约时间成本!

之所以要整理一份关于MyBatis的文章,一个是为了重新复习它,另一个原因就是,别因为这些简单省事的框架,把真正需要好好掌握的基本给忘记了!

话糙理不糙的说,比如几张表联查的统计查询?真正复杂的查询,还是需要自己把SQL搞出来的,难道不是嘛?一手SQL出神入化,任你多少表,我都能查!

正文

下面步入正题撒!

什么是MyBatis?

MyBatis是一个免费且优秀的开源持久性框架,支持自定义SQL,高级映射和存储过程。

再也不用手写JDBC了,因为MyBatis几乎免除了所有关于JDBC代码。

MyBatis可以通过XML或者注解的方式,来配置映射类型、接口和实体。

数据库配置

这个是用SpringBoot的配置写法:

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456

mysql-connector-java 6之后,driver驱动加入了cj,且需要指定时区serverTimezone
老版本的:com.mysql.jdbc.Driver

如果是SpringBoot,在启动类上写好扫码Dao包的位置

@MapperScan(basePackages = "com.test.dao")
下面开始举个栗子🌰

在定义好Dao层接口后,根据接口中的方法,可以写出与之对应的Mapper,可以理解为实现类。

Mapper.xml 放在Resources里就好啦,在配置中加入:

mybatis.mapper-locations=classpath:/*Mapper.xml

Dao层接口:

public interface UserMapper {// 增加用户int addUser(User user);
}

Mapper.xml具体实现:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.dao.UserMapper"><insert id="addUser" parameterType="user">insert into user(id, name, age) VALUES (#{id},#{name},#{age});</insert>
</mapper>

上面 mapper PUBLIC…管他啥用的,这么写就对了!

namespace 命名空间,这个xml是谁的实现,就写谁,完整包名,后面都以完整包名说,容易区分开。

写好之后,按住Ctrl你试试,能不能点进去?这些个打错字母的怪才,咱复制粘贴他不香嘛!

id 接口方法名,namespace是谁的,去谁里面找,都说是实现类了,名字要和方法一样。

parameterType 传入参数类型,写User完整名也行,再不就写参数的别名,这就是user,要是方法的参数 (User abc) 那么写abc他也好使

#{?,?,?} ?字符串,和实体类变量名对应,建议哈,表字段和实体类变量属性名字大小写一致,免得一会表字段,一会实体属性的出问题,或用resultMap也可以,后面说这个。

#与 $ 这两个都是其中能包含属性的:

$ 可能也看见过,之所以不用,实际就是对SQL语句的拼接而不安全,容易被SQL注入攻击。
SQL注入是什么,请自行度娘
#就是个占位符,SQL语句会预编译它。

基本上只要是参数是实体的,增删改返回int类型的,这么写就没毛病,写就完了!

1、单个查询:

接口中的方法:

//根据id查询单个用户
User findUserById(@Param("id") String id);

Mapper实现:

<select id="findUserById" resultType="com.test.User" parameterType="java.lang.String">select * from user where id = #{id};
</select>

resultType 指返回的类型,返回哪个实体,写那个实体就好
parameterType 这次他不再是实体对象了,但同理,用谁写谁
@Param 这个注解内容尽量和参数名相同,省的自己搞乱了;这个注解是在多个参数的情况下才需要写的,只有一个参数忽略不计。
但好像是19版的IDEA还有忘记了哪个版本之上的MyBatis更新后可以忽略不写,这个自行度娘吧!

查询全部

接口中的方法:

// 查询全部用户
List<User> findAllUsers();

Mapper实现:

<select id="findAllUsers" resultType="com.test.User">select * from user;
</select>

这里明明是List,resultType 为啥要写User?
你写个返回List还带泛型的,我瞅瞅咋写的!
这里的 resultType ,根据泛型是什么就写什么。

resultMap 结果集映射

resultMap 是 MyBatis 中最强大的一个元素
它可以做JDBC不支持的操作,简单的不需要管,复杂的描述之间的关系就OK!

接口的方法:

//查询所有用户
List<User> findAllUsers();

实现类:

<select id="findAllUsers" resultMap="userMap">select * from user;</select><resultMap id="userMap" type="com.test.User"><result column="id" property="id"/><result column="name" property="name"/><result column="age" property="age"/>
</resultMap>

select 的 resultMap 就要和 resultMap 的 id 相同了
type 代表了映射的是哪个实体,内部是实体属性和表字段的关系,属性想要返回什么写什么就好了,可不都写上去。

参数是Map或者List怎么办?使劲办!😁

先说说Map:

//根据map获取用户
List<User> findAllUsers (Map<String,String> map);
<select id="findAllUsers" resultType="com.test.User" parameterType="java.util.Map">select * from userwhereusername=#{username}andpassword=#{password};</select>
Map<String,String> map = new HashMap<String,String>();
map.put("username","老吴");
map.put("password","123456");

存入参数中的key就是#{key}

List:

//根据list获取用户
List<User> findAllUsers (List<String> list);
<select id="findAllUsers" resultType="com.test.User" parameterType="java.util.List">select * from userwhere<foreach collection="list" index="index" item="item" separator="," open="(" close=")">#{item}</foreach></select>
List<String> list = new ArrayList<String>();
list.add("老王");
list.add("老吴");

使用 foreach 语句循环集合中的数据,item 就是循环到的数据,名字随便取
collection 值 要写 list

多对一 与 一对多(举例说下一对多的情况,实际学生与老师是多对多的)

就以学生和老师举例子吧,
学生和他的老师,多对一:

//学生
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {private String id;private String name;private Teacher teacher;
}//老师
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {private String id;private String name;
}
List<Student> findAllStudentAndTeacher();
    <select id="findAllStudentAndTeacher" resultMap="teacherAndStudent">select t.id tid,t.name tname,s.id,s.name,s.teacherfrom teacher t,student swhere s.teacher = t.id;</select><resultMap id="teacherAndStudent" type="com.test.Student"><result column="id" property="id"/><result column="name" property="name"/><association property="teacher" javaType="com.test.Teacher"><result column="tid" property="id"/><result column="tname" property="name"/></association></resultMap>

这里主要用到 association ,它是一个对象标签

老师的学生们,一对多:

//学生
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {private String id;private String name;// 对应老师的ID就行private String teacher;
}//老师
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {private String id;private String name;// 老师的学生列表private List<Student> students;
}
List<Teacher> findAllTeacherAndStudent();
 <select id="findAllTeacherAndStudent" resultMap="teacherAndStudent">select t.id,t.name,s.id sid,s.name sname,s.teacher sTeacherfrom teacher t,student swhere t.id = s.teacher;</select><resultMap id="teacherAndStudent" type="com.test.Teacher"><result property="id" column="id"/><result property="name" column="name"/><collection property="students" ofType="com.test,Student"><result property="id" column="sid"/><result property="name" column="sname"/><result property="teacher" column="teacher"/></collection></resultMap>

既然是学生们了,那是个List集合吧,泛型是User

动态SQL — if

动态SQL可以增加同一个标签中,重复使用。
比如我想要根据username查询用户或者根据id查询用户,实际上使用一个就可以

List<User> findUserByParam (@Param("username") String username, @Param("id") String id);
<select id="findUserByParam" resultType="com.test.User">select * from userwhere 1 = 1<if test="username != '' || username != null ">and name = #{name}</if><if test="id != ‘’ || id != null">and id = #{id}</if></select>

给id传空值,那就是根据username查了,对吧?写代码嘛!省事为主,哈!
if 中 test 就是 判断条件,true 或 false

至于为什么要加 where 1 = 1 呢?
虽然这种写法不太符合规定,但是可以有效避免所有查询条件都是空,至少正常显示结果,已经必有了1=1的条件,不会出现错误。

动态SQL — chose

chose的用法思想,实际上和我刚说到 if 是一样的,他可以理解成java中的switch循环

<select id="findUserByParam" resultType="com.test.User">select * from userwhere 1 = 1<choose><when test="username != ''">and username = #{username}</when><when test="id != ''">and id = #{id}</when><otherwise>and 1 = 1</otherwise></choose></select>
动态SQL — foreach

实际上他就是个for循环,只是写法奇怪了点,刚说参数是LIst的时候,实际上已经用到他了,往上翻👆

MyBatis的缓存鸡汁🐤

一级缓存:
MyBatis 内置了一个强大的事务性查询缓存机制,默认情况下,只启用了本地的会话缓存(一级缓存)

1、映射语句文件中的所有 select 语句的结果将会被缓存。
2、映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
3、缓存会使用算法LRU,最近最少使用算法,来清除不需要的缓存。
4、缓存不会定时进行刷新(也就是说,没有刷新间隔)。
5、缓存获取到的对象并不是共享的,可以被调用者修改,而不干扰其他调用者或线程所做的修改。

二级缓存:
如果没有开启二级缓存,在两个不同的SqlSession中发起查询,系统会执行两次SQL指令

在mapper.xml中可以配置开启:
< cache type=“org.apache.ibatis.cache.impl.PerpetualCache” /> 默认1024个对象个数
开启后,不同的SqlSession发起查询,可如果是相同语句,只会真正的执行一次SQL命令。

MyBatis启动时,都做了哪些操作?

在这里插入图片描述
借了大佬一张图,看看MyBatis启动都干了什么?

(1)读取MyBatis配置: XML写法的话那就是mybatis-config.xml的文件,
如果是只用了application.properties(yml),其实道理是一样的,读取其配置的MyBatis的运行环境呀,数据库连接信息啊,以及MyBatis的设置等等这些信息。

(2)加载映射文件: 就是我们所写的Mapper文件,加载操作数据库的SQL,一次性可以加载多个,每个文件实际就是数据库的一张表。

(3)SqlSessionFactory: 会话工厂,通过SqlSessionFactoryBuilder对象类获得,而SqlSessionFactoryBuilder则可以从配置文件构建出SqlSessionFactory的实例.每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心,SqlSessionFactory就是创建SqlSession的工厂.。

(4)SqlSession: 会话对象,其包含了执行SQL的所有方法,也是程序和数据库直接交换的单线程对象,它是线程不安全的,就想HttpSession一样,用完及时关闭,当然了这件事MyBatis自己已经做了,实际上他的底层就是JDBC的连接,所以可以好不客气的说,MyBatis最核心的就是它了。

(5)Executor: SqlSession对象的执行器,是SqlSession的内部属性,SqlSession创建的时候也顺便把Executor给创建好了。说白了,就是执行SQL的执行器,它会将SqlSession对象传递的参数,动态生成需要执行的SQL。可能说SqlSession扔过来一堆东西,但并不是直接可执行的SQL语句,Executor执行器会把这一堆变成一个能执行的SQL,并且执行,而且Executor也负责了查询缓存的维护工作。

(5.1) Executor的缓存功能:👆前面说过的,一级默认开启,二级需要手动开启。
如果在配置中开启了二级缓存,SqlSessionFactory在创建SqlSession对象时,其内部创建的是CachingExecutor执行器,它继承了Executor,目的就是为了操作缓存。它内部有个查询二级缓存的方法,如果没有查到某个namespace(也就是mapper里面写的dao文件)的二级缓存,就交给Executor执行器继续查询一级缓存,如果连一级缓存不存在,就去查询数据库。
简单来说,CachingExecutor有个二级缓存管理器,且内部方法专门查询二级的,如果二级没有,交给Executor后它就不管了。这就是Executor执行器的查询缓存的维护工作。

(5.2) 除了CachingExecutor执行器以外,还有BaseExecutor的接口,而此接口中存在三个子类实现:
ReuseExecutor、BatchExecutor、SimpleExecutor(executor默认的),
具体干嘛的自行度娘吧!超出本宝宝能力范围了! 😄

(6)MappedStatement:在Executor执行方法中的参数,对映射信息(SQL语句的id、参数等信息)的封装,实际就是paramterType、resultType这些参数。
它是mapper文件路径的配置信息:mybatis.mapper-locations=classpath:/*Mapper.xml来加载的,
如果没有配置这条,它会先按照@Select这些注解来封装映射信息,如果注解也没用的话,它会调用内部方法,将按dao接口名称来寻找对应的Mapper。

(7)(8)
根据MappedStatement中封装的映射信息、参数等,Executor执行器真正的去执行SQL,并且返回给封装好的映射方法,输入输出类型可以是Map、List等等类型,也可以是对象实体,看mapper怎么写的喽!

MyBatis和Hibernate的区别?

Hibernate我理解的不多,看看大佬怎么说的吧
https://blog.csdn.net/firejuly/article/details/8190229

我这里想说的就是,无论哪种框架,适合项目,适合自己就好了。
谁优谁良这种事,小生道行甚浅,不敢妄自评价!

总结

第一次写近W字的文章,闲暇时间搞了近3天时间,可能有些啰嗦,但不光为了自己回忆MyBatis,也是希望对大家有些许助力吧!

Mapper写法都是我在编辑器手敲上去的,直接复制粘贴我可不敢保证好用啊!😁
特别深入的东西,我不懂,也不敢多讲,大佬们请理解!如果有问题,请评论告知,我及时修改!谢谢啦!

参考文章:
https://blog.csdn.net/qq_41464123/article/details/108172276
https://blog.csdn.net/cold___play/article/details/102729810

希望对大家有点帮助!我是左小涩,一个独自在大城市努力的年轻人。

这篇关于别在一直纠结MyBatis-Plus了,MyBatis才是真正的主人!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/578168

相关文章

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

MyBatis延迟加载与多级缓存全解析

《MyBatis延迟加载与多级缓存全解析》文章介绍MyBatis的延迟加载与多级缓存机制,延迟加载按需加载关联数据提升性能,一级缓存会话级默认开启,二级缓存工厂级支持跨会话共享,增删改操作会清空对应缓... 目录MyBATis延迟加载策略一对多示例一对多示例MyBatis框架的缓存一级缓存二级缓存MyBat

mybatis直接执行完整sql及踩坑解决

《mybatis直接执行完整sql及踩坑解决》MyBatis可通过select标签执行动态SQL,DQL用ListLinkedHashMap接收结果,DML用int处理,注意防御SQL注入,优先使用#... 目录myBATiFBNZQs直接执行完整sql及踩坑select语句采用count、insert、u

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

mybatis映射器配置小结

《mybatis映射器配置小结》本文详解MyBatis映射器配置,重点讲解字段映射的三种解决方案(别名、自动驼峰映射、resultMap),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定... 目录select中字段的映射问题使用SQL语句中的别名功能使用mapUnderscoreToCame

mybatis-plus如何根据任意字段saveOrUpdateBatch

《mybatis-plus如何根据任意字段saveOrUpdateBatch》MyBatisPlussaveOrUpdateBatch默认按主键判断操作类型,若需按其他唯一字段(如agentId、pe... 目录使用场景方法源码方法改造首先在service层定义接口service层接口实现总结使用场景my

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa