MyBatis
介绍
简介
- MyBatis 是一款优秀的持久层框架(也叫ORM框架)
- MyBatis 极大简化 JDBC 操作
- MyBatis 通过简单的XML或注解来操作数据源
Hibernate 和 MyBatis 的区别
截取于http://c.biancheng.net/view/4302.html
1)sql 优化方面
- Hibernate 不需要编写大量的 SQL,就可以完全映射,提供了日志、缓存、级联(级联比 MyBatis 强大)等特性,此外还提供 HQL(Hibernate Query Language)对 POJO 进行操作。但会多消耗性能。
- MyBatis 手动编写 SQL,支持动态 SQL、处理列表、动态生成表名、支持存储过程。工作量相对大些。
2)开发方面
- MyBatis 是一个半自动映射的框架,因为 MyBatis 需要手动匹配 POJO、SQL 和映射关系。
- Hibernate 是一个全表映射的框架,只需提供 POJO 和映射关系即可。
3)Hibernate 优势
- Hibernate 的 DAO 层开发比 MyBatis 简单,Mybatis 需要维护 SQL 和结果映射。
- Hibernate 对对象的维护和缓存要比 MyBatis 好,对增删改查的对象的维护要方便。
- Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL。
- Hibernate 有更好的二级缓存机制,可以使用第三方缓存。MyBatis 本身提供的缓存机制不佳。
4)Mybatis优势
- MyBatis 可以进行更为细致的 SQL 优化,可以减少查询字段。
- MyBatis 容易掌握,而 Hibernate 门槛较高。
总的来说,MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架,Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。
第一个MyBatis(基于Spring)
过程不懂可以查看官方文档
结构图如下:
依赖
1. mybatis 依赖包 (mybatis框架)
2. mysql 依赖包(连接mysql使用)
3. log4j 依赖包(日志查看用)
4. junit 依赖包(做测试类用)
# 自行在maven仓库查找
https://mvnrepository.com/search?q=sharding-transaction-spring-boot-starter
配置文件
文件名称任意
<!--
配置jdbc连接 和 Mapper.xml(编写SQL的文件)
-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--JDBC连接语句对应的键值-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/word"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--声明(注册)Mapper.xml文件-->
<!-- resource 文件路径 -->
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
Mapper配置文件
编写SQL的文件,温馨提示
- 配置文件名建议和Dao接口名一样,如:Dao层有个接口是UserDao,那XML创建名也是UserDao.xml
- Mapper文件是可以创建多个的
- 编写完成需要到上方配置文件里进行注册
<?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">
<!--
namespace="对应Dao层接口的全类名"
-->
<mapper namespace="org.mybatis.example.BlogMapper">
<!--
select:查询语句
id:接口里对应的方法名
resultType:返回类型,基本类型除外需要全类名
parameterType:传入的参数类型,基本类型除外需要全类名
-->
<select id="getUser" resultType="String" parameterType="String">
select * from Blog where id = #{id}
</select>
</mapper>
Dao层
Dao层一般用来存放操作SQL相关的内容,可以自定义名称不过相关路径配置也要修改,问题不大
在Dao层里面创建UserDao接口
package dao;
public interface UserDao {
//@Param是为了规范,让逻辑更加严谨,《拓展》章节会讲,这里不加也可以
//不加@Param String getUser(Integer a);
String getUser(@Param("id") Integer a);
}
调用
原始的调用方法,到了springboot
就不用这样子了,实在是麻烦
public class test {
@Test
public void dome1 () throws IOException {
//配置文件(不是Mapper那个)读取到流中
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//根据配置文件创建工厂
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
//通过工厂获取SqlSession
SqlSession session= sqlSessionFactory.openSession();
//执行sql第一种方法(反射机制实例化对象,调用对象方法)
UserDao ud = session.getMapper(UserDao.class);
ud.getUser(1000);
//执行sql第二种方法(调用session查询一条记录的方法)
String a= session.selectOne("dao.UserDao.getUser",1000);
//释放资源
session.close();
}}
配置文件说明
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 主配置文件 -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--引用配置文件-->
<properties resource="mybatis.properties"/>
<!-- 设置交互的环境
如:二级缓存、延迟加载、日志等等
(海量数据优化方法 1.SQL定义索引 2.使用缓存机制(redis数据库) 3.Elasticsearch与solr(针对电商))
-->
<settings>
<setting name="" value=""/>
</settings>
<!-- 别名:可以方便我们设置sql接受和返回类型时不用写全类名 -->
<typeAliases>
<!--自定义类型的别名 type:类型的全类名 alias:自定义的别名 (特别方便,但不宜记忆和操作次数太大)-->
<typeAlias type="domian.user" alias="u"/>
<!--自动起别名 name=要起别名的文件/指定文件夹下的全部文件 (方便)
自动别名的名称就是类名 如:com.xiao.domain.User 别名:User,其实不区分大小写 -->
<package name="domian"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--
JDBC连接语句对应的键值,不引用配置文件情况下
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/word"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
-->
<!--
引用配置文件情况下(推荐,框架整合后维护性高)
加上 <properties resource="配置文件名"/> 到刚刚上面写的 引用配置文件 那个位置
value = ${引用文件对应的键}
引用配置文件mybatis.properties,mybatis.properties的内容如下:4个键值对
url=jdbc:mysql://localhost:3306/word?useSSL=false&serverTimezone=UTC
driver=com.mysql.jdbc.Driver
password=root
username=root
-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--
声明(注册)Mapper.xml文件,必须的
写文件的相对路径
-->
<mapper resource="src/main/java/dom/UserDao.xml"/>
<mapper resource="src/main/resources/mybatis-config.xml"/>
</mappers>
</configuration>
标签 | 作用 |
---|---|
引用配置文件设置连接 | |
自定义别名,返回或传递参数时填写别名即可,就不用填写全类名 | |
自动别名,指定包下自动别名 | |
设置交互的环境,里面有许多的配置,如:二级缓存、延迟加载、日志等等 |
动态SQL
什么是动态SQL
根据不同的条件生成不同的sql语句,由此就需要对条件进行判断等一系列操作
Spring
1)where(针对查询语法的自动解析)
自动解析where条件的语句,来达到SQL的正确表达式
如:为了防止if查询是 select * from u_user where and id = 1000 这种不合法的的结果
加上where标签就会变为 select * from u_user where id = 1000
2)If(判断条件是否满足)
<!--
复杂查询(满足要求拼接where条件)
当name为空age不为空,那么where=( age <(转义符<) #{age} )
当name和age都不为空,那么where=(name like '%' #{name} '%' and name like '%' #{age} '%')以此类推
-->
<select id="selectBlog5" resultType="User" >
select * from u_user;
<where>
<if test="name!=null and name!=''">
<!-- 模糊查询 %{0,n}个字符 _{0,1}个字符 -->
name like '%' #{name} '%'
</if>
<if test="age!=null and age!=''">
<!-- < <的转义符 -->
age < #{age}
</if>
</where>
</select>
3)choose(类似于switch)
<!-- 类似于switch case 满足最先的when就不会执行其它,都不满足就执行otherwise
如:满足"tx2!=null",where=(age = #{age}) 就不会执行其它
都不满足就执行otherwise,where=(name = #{name})
-->
<select id="selectBlog8" resultType="User">
select * from u_user
<where>
<choose>
<when test="tx1!=null">
id = #{id}
</when>
<when test="tx2!=null">
age = #{age}
</when>
<otherwise>
name = #{name}
</otherwise>
</choose>
</where>
</select>
4)Foreach(针对查询条件in的方法)
<!-- 针对查询条件in的方法(foreach遍历数组)
collection:传递的参数类型 array数组/list集合 item:当前遍历到的对象,使用时要在#{}中
open:开始符号 close:结束符号 separator:分隔符
传递数组:arr[]={"001","002","003"} 变为 in('001','002','003')
-->
<select id="selectBlog6" resultType="User">
select * from u_user where id in
<foreach collection="array" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
5)Set(针对更新语法的自动解析)
<!-- 针对更新语法的自动解析,去除,
当name为空age不为空 sql=(select * from u_user set age = #{age} where id={id})
当name和age都不为空 sql=(select * from u_user set name = #{name},age = #{age} where id={id})
注: 如果条件都不满足会报错
-->
<update id="selectBlog4" resultType="map" >
select * from u_user
<set>
<if test="name!=null and name!=''">
name = #{name},
</if>
<if test="age!=null and age!=''">
age = #{age}
</if>
</set>
where id={id}
</update>
6)SQL(SQL片段)
<!--sql片段,多用来子查询但是不推荐-->
<sql id="sql1">
select * from u_user
</sql>
<!--结果:select * from u_user where name=#{name} -->
<select id="selectBlog7" resultType="User">
<include refid="sql1"/> where name=#{name}
</select>
Spring boot
provider
//provider类,可以不使用XML来完成动态SQL的处理
public class GoodsPro {
public String updOne(Goods goods){
return new SQL(){{
UPDATE("goods");
if (goods.getGoodsName()!= null && !goods.getGoodsName().equals("")) {
SET("goods_name = #{goodsName}");
}
if (goods.getDescription() != null && !goods.getDescription().equals("")) {
SET("description = #{description}");
}
if (goods.getTags() != null && !goods.getTags().equals("")) {
SET("tags = #{tags}");
}
if (goods.getTypeId() != null) {
SET("type_id = #{typeId}");
}
if (goods.getImgurl() != null && !goods.getImgurl().equals("")) {
SET("imgurl = #{imgurl}");
}
if (goods.getPriceRichang() != null && goods.getPriceRichang()>0) {
SET("price_richang = #{priceRichang}");
}
if (goods.getPriceTehui() != null && goods.getPriceTehui()>=0 && goods.getPriceTehui()<= goods.getPriceRichang()) {
SET("price_tehui = #{priceTehui}");
}
if (goods.getIsSell() != null) {
SET("is_sell = #{isSell}");
}
WHERE("goods_id = #{goodsId}");
}}.toString();
};
}
Mapper
public interface GoodsMapper {
//UpdateProvider可以指定provider类来处理SQL语句
@UpdateProvider(type = GoodsPro.class,method = "updOne")
int updOne(Goods goods);
}
缓存机制
很多时候mybatis的缓存都是使用Redis
一级缓存
-
一级缓存机制:
- 就是把读取sql数据储到内存,下次查询一样的sql直接从内存拿,就不用和数据库交互
-
一级缓存:
- 一级缓存是sqlSession级别(所以两个sqlSession的一级缓存是不能公用的)
- 一直是开启的
- 一级缓存也称本地缓存
-
一级缓存失效情况:
-
sqlSession 不同(就是实例化两次sqlSession)
SqlSessionFactory sqlSFactory1 = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session1= sqlSFactory.openSession(); SqlSessionFactory sqlSFactory2 = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session2= sqlSFactory.openSession();
-
sqlSeesion 相同,查询内容不同
SqlSessionFactory sqlSFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session1 = sqlSFactory.openSession(); UserDao ud1 = session1.getMapper(UserDao.class); ud.getUser(1000); ud.getUser(2000);
-
sqlSeesion 相同,查询内容相同,但中间加了增删改
SqlSessionFactory sqlSFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session1 = sqlSFactory.openSession(); UserDao ud1 = session1.getMapper(UserDao.class); ud.getUser(1000); ud.addUser("zhangsan",24) //中间加了增删改语句 ud.getUser(1000);
-
缓存被清理了
session.clearCache();
-
二级缓存
-
二级缓存机制
- 在一级缓存关闭会话(SqlSession)时,把一级缓存的内容保存到二级缓存(类似于提升作用域),其它SqlSession下次就会去查找二级缓存
-
二级缓存:
- 二级缓存是namespace级别
- 需要手动开启
- 二级缓存也称全局缓存
-
使用
-
开启全局缓存(在核心配置文件里的里面设置)
<settings> <setting name="cacheEnabled" value="true"/> </settings>
-
配置二级缓存(在每个Mapper文件加入)
<?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="dao.UserDao"> <!-- 开启二级缓存 <cache/> --> <!-- 直接<cache/>就可以,也可以进行设置 eviction:缓存回收策略、flushInterval:缓存刷新间隔 size:存放数量、readOnly:是否只读 --> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" /> <!-- useCache:是否使用缓存 --> <select id="getUser" resultType="String" parameterType="int" useCache="true"> select * from u_login where uid = #{id} </select> </mapper>
-
需要对实体类序列化(实现Serializable接口即可)
public class User implement Serializable{}
-
-
工作流程图
注解开发
-
注解开发可以方便我们开发简单的SQL,不用写Mapper里的SQL语法
-
注解开发本质是通过 反射机制 实现,底层是动态代理
(spring下不利于维护和开发复杂SQL力不从心,了解即可!)
spring
半脱离Mapper文件开发
Dao层
//接口
public interface UserDao {
@Select("select account from u_login where uid = #{id}")
String getUser(Integer id);
}
Mapper文件
<?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="dao.UserDao">
</mapper>
配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<!--JDBC连接语句对应的键值-->
</environment>
</environments>
<mappers>
<!--声明(注册)Mapper.xml文件-->
</configuration>
测试类
@Test //测试类下的一个方法
public void dome2() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//根据配置文件创建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂获取SqlSession
SqlSession session= sqlSessionFactory.openSession();
//加载mapper的接口
UserDao ud = session.getMapper(UserDao.class);
//执行sql
ud.getUser(1000);
}
spring boot
完全脱离Mapper文件
application.yaml
全局配置文件里配置jdbc连接
spring:
datasource:
password: root
username: root
url: "jdbc:mysql://localhost:3306/cakeshop?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC"
driver-class-name: com.mysql.cj.jdbc.Driver
Dao层
@Mapper //加这个注解就不用使用@MapperScan,不过只对单个文件有效
public interface UserDao {
@Select("select account from u_login where uid = #{id}")
String getUser(Integer id);
}
启动类
扫描指定包下的所有Mapper文件,被扫描到就会被注册
@MapperScan(value = "com.rongxiaozhan.cake.mapper")
public class CakeApplication {
public static void main(String[] args) {
SpringApplication.run(CakeApplication.class, args);
}
}
测试类
@Autowired
UserDao userDao;
public void dome2() throws IOException {
userDao.getUser(1);
}
拓展(可以不看)
1)@Param 详解
public interface UserDao {
String getUser(@Param("userId") Integer id,@Param("userName") String name);
}
<?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="dao.UserDao">
<!--因为加了@Param,使得代码更加严谨,参数就清楚的知道自己对应的字段在哪里
@Param("userId")=#{userId} @Param("userName")=#{userName}
-->
<select id="getUser" resultType="String" parameterType="Map">
select account from u_login where uid = #{userId} and account=#{userName}
</select>
<!-- 向sql传入两个不同的类型时,parameterType不能写 -->
<select id="ada" resultType="user">
select account from u_login where uid = #{userId} and account=#{userName}
</select>
</mapper>
2)制作util工具类1
频繁的开启和关闭(inputStream、SqlSessionFactory、SqlSessionFactoryBuilder)是特别浪费时间、影响性能和占用空间,所以创建 mybatis的工具类 可以来抑制这种现象,到达 inputStream只调用一次、SqlSessionFactory只有一个(顺便解决事务问题)和SqlSessionFactoryBuilder只创建一次
package util;
import jdk.internal.loader.Resource;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* mybatis的工具类
*/
public class SqlSessionUtil {
/**
* SqlSessionFactory:sql会话工厂(接口对象)
* ThreadLocal:线程变量对象(底层map集合),确保每个变量隔离互不干扰
* --如:static声明的变量(不带ThreadLocal)可以被其他线程共享,
* -- :static声明的变量(带ThreadLocal)就不会共享,且每个线程都有属于自己的static声明的变量,互不干扰
*/
private static SqlSessionFactory sqlSessionFactory;
private static ThreadLocal<SqlSession> t = new ThreadLocal<>();
static {
try {
//InputStream:字符流对象,来读取配置文件
InputStream inputStream =
Resources.getResourceAsStream("mybatis-config.xml");
//根据配置文件创建工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 禁止外界通过new方法创建
*/
private SqlSessionUtil() {}
/**
* 获取SqlSession(用来执行映射文件里sql的对象)
* 因为SqlSession要满足事务的需求,所以要求获取的SqlSession是同一个对象
* 如:两个类要获取同一个SqlSession时,解决方法是线程变量
* @return
*/
public static SqlSession getSession(){
//获取线程变量t的值
SqlSession session = t.get();
//判断是否有session是非为空
if(session == null){
//为空,通过工厂获取SqlSession对象再储存到session和线程变量t
session= sqlSessionFactory.openSession();
t.set(sqlSessionFactory.openSession());
}
//返回session
return session;
}
/**
* 释放资源
*/
public static void close(){
//获取当前线程的线程变量t,然后存储到session里面
SqlSession session = t.get();
//如果有的话,进行SqlSession的释放和线程变量t的删除
if(session!=null){
session.close();
t.remove();
}
}
}
3)制作util工具类2
为了事务层不处理事务以外的事情,来提交事务所做的工具类(知道即可)
package util;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理模型来增强方法,实现事务提交
*/
public class ProxySqlSession implements InvocationHandler {
private Object target;
//存储对象
public ProxySqlSession(Object target) {
this.target = target;
}
/**
* 处理器
* 参数一:代理对象
* 参数二:封装代理对象调用的方法的对象
* 参数三:代理对象调用方法时传递的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession session =null;
Object object =null;
try {
//获取session对象
session = SqlSessionUtil.getSession();
//执行方法
object = method.invoke(target,args);
//提交事务
session.commit();
} catch (Exception e) {
e.printStackTrace();
//事务回滚
session.rollback();
}
return object;
}
/**
* 传达对象,调用处理器执行方法
* 参数一:类加载器(选真实对象的类)
* 参数二:接口数组(选真实对象的类)
* 参数三:处理器( new InvocationHandler()) 注:代理对象的所有方法都会调用该方法
* @return
*/
public Object getProxy (){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
}
MyBatis Plus(基于spring boot)
MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
1、创建
详细查看官网,下面只说核心步骤
-
导入相关依赖(springboot项目)
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency>
-
全局配置
spring: datasource: username: root password: root # 注:如果中文查询不到需要指定utf-8编码集 url: 补上&autoReconnect=true&useUnicode=true&characterEncoding=utf8 url: jdbc:mysql://localhost:3306/db01?useSSL=false&serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver
-
使用
public interface UserMapper extends BaseMapper<User> { //继承BaseMapper }
-
测试
@RunWith(SpringRunner.class)//更改测试类的运行器为SpringRunner.class @SpringBootTest //springboot测试类 public class SampleTest { @Autowired private UserMapper userMapper; //mapper @Test public void testSelect() { System.out.println(("----- selectAll method test ------")); List<User> userList = userMapper.selectList(null); //查询全部用户信息 Assert.assertEquals(5, userList.size());//断言方法,如果俩个参数一样就继续往下执行,反之不执行 userList.forEach(System.out::println); } } //UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以填null就是无任何条件
这样子最基础的crud操作就不用我们手写了(继承即可)
2、配置
# 配置使用什么日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3、注解
@TableName
表名注解
属性 | 描述 |
---|---|
value | 数据库的表名 |
schema | schema(@since 3.1.1)数据库库名 |
keepGlobalPrefix | 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)(@since 3.1.1)-一般不用 |
resultMap | xml 中 resultMap 的 id |
autoResultMap | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入)(@since 3.1.2) |
@TableId
主键字段
属性 | 描述 |
---|---|
value | 主键字段名 |
type | 主键类型 【AUTO(0) 数据库id自增】【NONE(1) 未设置主键】 【INPUT(2) 手动输入】【ID_WORKER(3) 默认值,雪花算法】 【UUID(4)uuid全局唯一id】【ID_WORKER_STR(5) 字符串表示法】 |
@TableField
字段注解(非主键)
属性 | 描述 |
---|---|
value | 数据库字段名 |
exist | 该字段是否为数据库表字段(默认true) |
fill | 字段自动填充策略 |
el | 相当于写在 mapper.xml 里的SQL里的#{ ... } 部分 |
condition | where 查询条件 |
update | update set 部分注入,更新时调用,优先于el |
insertStrategy | 插入策略(默认DEFAULT也就是NOT_NULl,字段不存在不添加) |
updateStrategy | 更新策略 |
whereStrategy | 条件策略 |
select | 是否进行 select 查询 |
keepGlobalFormat | 是否保持使用全局的 format 进行处理(@since 3.1.1) |
@TableLogic
表字段逻辑处理注解(逻辑删除)
属性 | 描述 |
---|---|
value | 逻辑未删除值 |
delval | 逻辑删除值 |
@SqlParser
租户注解,支持method上以及mapper接口上
属性 | 描述 |
---|---|
filter | true: 表示过滤SQL解析,即不会进入ISqlParser解析链,否则会进解析链并追加例如tenant_id等条件 |
@KeySequence
序列主键策略 oracle
属性 | 描述 |
---|---|
value | 序列名 |
clazz | id的类型, 可以指定String.class,这样返回的Sequence值是字符串"1" |
@Version
乐观锁注解,作用于字段
@EnumValue
通枚举类注解(注解在枚举字段上)
4、主键生成策略
当我们使用MP插入数据时,不设置id字段的数据,MP会通过主键生成策略帮我们自动插入的
// 测试插入
@Test
public void testInsert(){
User user = new User();
user.setName("狂神说Java");
user.setAge(3);
user.setEmail("24736743@qq.com");
int result = userMapper.insert(user); // 帮我们自动生成id
System.out.println(result); // 受影响的行数
System.out.println(user); // 发现,id会自动回填
}
//注:一旦手动添加id了就会为null
常见主键生成策略如下:
雪花算法:
介绍:有这么一种说法,自然界中并不存在两片完全一样的雪花的。每一片雪花都拥有自己漂亮独特的形状、独一无二。雪花算法也表示生成的ID如雪花般独一无二。
组成结构大致由:首位无效符、时间戳差值,机器(进程)编码,序列号四部分组成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pQ3pk2rE-1625226174873)(D:\笔记\图片\redis\雪花算法.PNG)]
UUID随机数:
UUID 是指Universally Unique Identifier,翻译为中文是通用唯一识别码,UUID 的目的是让分布式系统中的所有元素都能有唯一的识别信息。
UUID 是由一组32位数的16进制数字所构成,以连字号分隔的五组来显示,形式为 8-4-4-4-12,总共有 36个字符(即三十二个英数字母和四个连字号),详细查看大佬博客
123e4567-e89b-12d3-a456-426655440000 //32位数的16进制数字
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx //形式为 8-4-4-4-12
数据库自增:
这就是小白最明白的了,每次添加自动自增,但是这种方式是存在单点故障风险,且迁移合并等比较麻烦
设置自增策略方法:
//加在实体类上(pojo类)
@TableField(fill = FieldFill.INSERT)
5、乐观锁
乐观锁:就是只对数据更新时做版本比较,版本一样则运行成功,不一样则失败
创建:
- 创建配置类
@Configuration //配置类
public class MybatisPlusConfig {
@Bean //乐观锁
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
- 实体类加注解
@Version //实体类字段上加乐观锁注解
private Integer version; //对应数据库乐观锁字段
这样子只要执行增删改操作时,如果版本不一样就无法修改成功,保证数据的一致性
注:数据库一定要有一个乐观锁字段,命名自定义都可以
6、自动填充
自动填充:在插入数据或者修改数据时,自动添加修改时间到数据库字段里(字段自定义命名)
创建:
- 创建配置类
@Configuration
public class MybatisPlusConfig implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//【参数1:固定的metaObject】【参数2:实体类属性】【参数3:类的class】【参数4:调用方法】
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
//【参数1:固定的metaObject】【参数2:实体类属性】【参数3:类的class】【参数4:调用方法】
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
- 实体类加注解
//value:数据库对应的字段 fill:填充方式(插入时更新)
@TableField(value = "create_Time", fill = FieldFill.INSERT)
private Date createTime;
//value:数据库对应的字段 fill:填充方式(插入修改时更新)
@TableField(value = "update_Time", fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
这样子在修改数据时就会自动填充时间戳了,注:如果数据插入的时间戳不喜欢可以自己创建时间工具类使用
7、逻辑删除
逻辑删除:其实就是修改数据库逻辑删除字段的值,不进行真正的删除操作,查询时根据逻辑删除字段的值来进行排除该字段,简单说,我们在论坛里删除的记录不会真正的删除,而是把【逻辑删除字段】的值改成1,在查询时如果【逻辑删除字段】=1就不进行返回,而管理员可以查看删除记录是因为查询时不带【逻辑删除字段】的判断
创建:
- 修改全局配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag #全局逻辑删除字段值
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
- 实体类添加注解
@TableLogic //逻辑删除注解
private int flag;
8、自动代码生成器
代码生成器:自动创建数据库表对应的三层结构以及实体类
创建:
-
依赖(原项目(数据库、启动类、web依赖等等)后还需要的核心额外依赖)
<!--自动生成代码--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.3.2</version> </dependency> <!--模板引擎 依赖--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.2</version> </dependency>
-
创建生成器类
public class YaojiuCode { public static void main(String[] args) { AutoGenerator mpg = new AutoGenerator();// 代码自动生成器对象 //1、全局配置 GlobalConfig gc = new GlobalConfig();//全局配置对象 String projectPath = System.getProperty("user.dir"); //获取系统下的user.dir,固定写法 gc.setOutputDir(projectPath + "/src/main/java");//要输出的目录,固定写法 gc.setAuthor("yaojiu");//作者名字 gc.setOpen(false);//是否打开win窗口 gc.setSwagger2(true);//是否开启swagger gc.setDateType(DateType.ONLY_DATE);//设置日期类型 gc.setIdType(IdType.AUTO);//设置id自增策略 mpg.setGlobalConfig(gc); //2、设置数据源 DataSourceConfig dsc = new DataSourceConfig();//数据源对象 dsc.setPassword("root");//设置密码 dsc.setUsername("root");//设置用户 dsc.setUrl("jdbc:mysql://localhost:3306/db01?useSSL=false&serverTimezone=UTC");//设置URL dsc.setDriverName("com.mysql.cj.jdbc.Driver");//设置驱动 mpg.setDataSource(dsc); //3、生成后的包配置 PackageConfig pc = new PackageConfig();//包对象 pc.setModuleName("blog");//设置模块名字(package名),生成的代码会放在这里面 pc.setParent("com.yaojiu");//父类 pc.setEntity("pojo");//存放实体类包名 pc.setMapper("mapper");//mapper层名 pc.setService("service");//service层名 pc.setController("controller");//controller层名 mpg.setPackageInfo(pc); //4、策略配置 StrategyConfig sc = new StrategyConfig();//策略对象 sc.setInclude("user");//设置需要构建的数据库表名 sc.setNaming(NamingStrategy.underline_to_camel);//转驼峰命名 sc.setColumnNaming(NamingStrategy.underline_to_camel);//列的命名为转驼峰 //sc.setRestControllerStyle(true);//Restful式的风格(驼峰或者下划线) sc.setEntityLombokModel(true);//开启自动lombok sc.setLogicDeleteFieldName("deleted");//设置逻辑删除的字段 sc.setVersionFieldName("version");//设置乐观锁的字段 TableFill tfCreate = new TableFill("create_time", FieldFill.INSERT);//插入更新时间字段 TableFill tfUpdate = new TableFill("update_time", FieldFill.INSERT_UPDATE);//插入更新和更新时间字段 ArrayList<TableFill> list = new ArrayList<>();//list集合 list.add(tfCreate); list.add(tfUpdate); sc.setTableFillList(list); mpg.setStrategy(sc); mpg.execute();//执行 } }
运行即可自动创建,很简单,但是运行后还需要进行微调整,不然就会被坑,要配置乐观锁、逻辑删除、自动填充的配置类和全局配置、还要导入其它相关依赖,如:swagger2、lombok(因为配置时选择了启用,不导入会报错)
重点坑:dao(mapper)层要加上@Repository,启动类加上@MapperScan(“com.yaojiu.blog.mapper”) 或者 直接dao(mapper)层加上@Mapper 不然会报错
9、crud操作
详细请看官网
@SpringBootTest
class MybatispulsApplicationTests {
@Autowired
IUserService iUserService;
@Test //查询全部
void text1() {
iUserService.list(null).forEach(System.out::println);
}
@Test //条件查询一个
void text2(){
QueryWrapper<User> wrapper = new QueryWrapper<>();//条件构造器
wrapper.eq("id","1"); //id=1
System.out.println(iUserService.getOne(wrapper));
}
@Test //条件查询多个
void text3(){
QueryWrapper<User> wrapper = new QueryWrapper<>();//条件构造器
//wrapper.between("id","1","3");//between 1 to 3
wrapper.in("id","1","2","3"); //id in {1,2,3}
iUserService.list(wrapper).forEach(System.out::println);
}
@Test //分页查询 需要配置bean
/*@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
*/
void text4(){
//参数1:页数,参数2:显示数量(选择第二页开始,一页最多3条数据)
Page<User> page = new Page<>(2,3);
iUserService.page(page);
page.getRecords().forEach(System.out::println); //获取分页结果
}
@Test//排序查询
void text5(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("age");//按age升序顺序
iUserService.list(wrapper).forEach(System.out::println);
}
@Test//模糊查询
void text6(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name","a");//查询包含a的name
iUserService.list(wrapper).forEach(System.out::println);
}
@Test//or and 用法
void text7(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("id","1").or().eq("id","2"); //or 等等
iUserService.list(wrapper).forEach(System.out::println);
}
}
等等要去官网查看,重点掌握QueryWrapper wrapper = new QueryWrapper<>()判断用法
方法 | 描述 |
---|---|
allEq | 满足全部条件 |
eq | = |
ne | <> |
gt | > |
ge | >= |
lt | < |
le | <= |
between | BETWEEN 值1 AND 值2 |
notBetween | NOT BETWEEN 值1 AND 值2 |
like | LIKE ‘%值%’ |
notLike | NOT LIKE ‘%值%’ |
likeLeft | LIKE ‘%值’ |
likeRight | LIKE ‘值%’ |
isNull | IS NULL |
isNotNull | IS NOT NULL |
in | IN(可以对象) |
notIn | NOT IN(可以对象) |
inSql | IN ( sql语句 ) |
notInSql | NOT IN ( sql语句 ) |
groupBy | GROUP BY |
orderByAsc | 排序:ORDER BY 升序 |
orderByDesc | 排序:ORDER BY 降序 |
orderBy | 排序:ORDER BY |
having | 最小条件判断 |
or | or |
and | and |
nested | 正常嵌套 不带 AND 或者 OR |
apply | 拼接 sql |
last | 无视优化规则直接拼接到 sql 的最后 |
exists | EXISTS ( sql语句 ) |
知识点补充
1、mysql引擎
在mysql5之后,支持的存储引擎有十几个,但是常用的就那么几种(MyISAM、MEMORY、InnoDB),默认支持的是InnoDB
MyISAM
使用这个存储引擎,每个MyISAM在磁盘上存储成三个文件。(1)frm文件:存储表的定义数据(2)MYD文件:存放表具体记录的数据(3)MYI文件:存储索引 ,在锁粒度为表级锁
InnoDB
是默认的数据库存储引擎,允许自增长(auto_increment),允许事务(MVCC(并发版本控制)来实现),锁粒度为行级锁,支持外键约束,有缓冲管理(加快查询)
Memory
将数据存在内存,为了提高数据的访问速度,每一个表实际上和一个磁盘文件关联。文件是frm。
MyISAM和InnoDB最大的区别在于锁粒度,MyISAM是表锁(每次查询都要把整个表锁起来,带查询完才解锁)如果在海量数据下、高并发下非常不合适,而InnoDB是行锁(只锁要查询那一行)