Mybatis教程

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的文件,温馨提示

  1. 配置文件名建议和Dao接口名一样,如:Dao层有个接口是UserDao,那XML创建名也是UserDao.xml
  2. Mapper文件是可以创建多个的
  3. 编写完成需要到上方配置文件里进行注册
<?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 <(转义符&lt;) #{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!=''">
                <!-- &lt <的转义符 --> 
                age &lt; #{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直接从内存拿,就不用和数据库交互
  • 一级缓存:

    1. 一级缓存是sqlSession级别(所以两个sqlSession的一级缓存是不能公用的)
    2. 一直是开启的
    3. 一级缓存也称本地缓存
  • 一级缓存失效情况:

    1. sqlSession 不同(就是实例化两次sqlSession)

      SqlSessionFactory sqlSFactory1 = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession session1= sqlSFactory.openSession();
      SqlSessionFactory sqlSFactory2 = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession session2= sqlSFactory.openSession();
      
    2. sqlSeesion 相同,查询内容不同

      SqlSessionFactory sqlSFactory = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession session1 = sqlSFactory.openSession();
      UserDao ud1 = session1.getMapper(UserDao.class);
      ud.getUser(1000);
      ud.getUser(2000);
      
    3. 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);
      
    4. 缓存被清理了

      session.clearCache();
      

二级缓存

  • 二级缓存机制

    • 在一级缓存关闭会话(SqlSession)时,把一级缓存的内容保存到二级缓存(类似于提升作用域),其它SqlSession下次就会去查找二级缓存
  • 二级缓存:

    1. 二级缓存是namespace级别
    2. 需要手动开启
    3. 二级缓存也称全局缓存
  • 使用

    1. 开启全局缓存(在核心配置文件里的里面设置)

      <settings> 
          <setting name="cacheEnabled" value="true"/>
      </settings> 
      
    2. 配置二级缓存(在每个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>
      
    3. 需要对实体类序列化(实现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、创建

详细查看官网,下面只说核心步骤

  1. 导入相关依赖(springboot项目)

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.2</version>
    </dependency>
    
  2. 全局配置

    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
    
  3. 使用

    public interface UserMapper extends BaseMapper<User> { //继承BaseMapper
    
    }
    
  4. 测试

    @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数据库的表名
schemaschema(@since 3.1.1)数据库库名
keepGlobalPrefix是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)(@since 3.1.1)-一般不用
resultMapxml 中 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里的#{ ... } 部分
conditionwhere 查询条件
updateupdate set 部分注入,更新时调用,优先于el
insertStrategy插入策略(默认DEFAULT也就是NOT_NULl,字段不存在不添加)
updateStrategy更新策略
whereStrategy条件策略
select是否进行 select 查询
keepGlobalFormat是否保持使用全局的 format 进行处理(@since 3.1.1)

@TableLogic

表字段逻辑处理注解(逻辑删除)

属性描述
value逻辑未删除值
delval逻辑删除值

@SqlParser

租户注解,支持method上以及mapper接口上

属性描述
filtertrue: 表示过滤SQL解析,即不会进入ISqlParser解析链,否则会进解析链并追加例如tenant_id等条件

@KeySequence

序列主键策略 oracle

属性描述
value序列名
clazzid的类型, 可以指定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、乐观锁

乐观锁:就是只对数据更新时做版本比较,版本一样则运行成功,不一样则失败

创建

  1. 创建配置类
@Configuration //配置类
public class MybatisPlusConfig {
    @Bean //乐观锁
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor(); 
    }
}
  1. 实体类加注解
@Version //实体类字段上加乐观锁注解
private Integer version; //对应数据库乐观锁字段

这样子只要执行增删改操作时,如果版本不一样就无法修改成功,保证数据的一致性
注:数据库一定要有一个乐观锁字段,命名自定义都可以

6、自动填充

自动填充:在插入数据或者修改数据时,自动添加修改时间到数据库字段里(字段自定义命名)

创建

  1. 创建配置类
@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()); 
    }
}
  1. 实体类加注解
//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就不进行返回,而管理员可以查看删除记录是因为查询时不带【逻辑删除字段】的判断

创建

  1. 修改全局配置
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag #全局逻辑删除字段值
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  1. 实体类添加注解
@TableLogic //逻辑删除注解
private int flag;

8、自动代码生成器

代码生成器:自动创建数据库表对应的三层结构以及实体类

创建

  1. 依赖(原项目(数据库、启动类、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>
    
  2. 创建生成器类

    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<=
betweenBETWEEN 值1 AND 值2
notBetweenNOT BETWEEN 值1 AND 值2
likeLIKE ‘%值%’
notLikeNOT LIKE ‘%值%’
likeLeftLIKE ‘%值’
likeRightLIKE ‘值%’
isNullIS NULL
isNotNullIS NOT NULL
inIN(可以对象)
notInNOT IN(可以对象)
inSqlIN ( sql语句 )
notInSqlNOT IN ( sql语句 )
groupByGROUP BY
orderByAsc排序:ORDER BY 升序
orderByDesc排序:ORDER BY 降序
orderBy排序:ORDER BY
having最小条件判断
oror
andand
nested正常嵌套 不带 AND 或者 OR
apply拼接 sql
last无视优化规则直接拼接到 sql 的最后
existsEXISTS ( 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是行锁(只锁要查询那一行)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值