Mybatis和MP

 MyBatis

概念:Mybatis是一个优秀的持久层框架,基于ORM(对象关系映射)设计思想,实现了以对象的方式操作数据库。

mybatis和mp都会动态回显

官网:mybatis – MyBatis 3 | 入门

一、springboot整合mybatis

1、添加依赖

<!--mybatis依赖包 -->  
<dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.2.0</version>
 </dependency>
<!-- jdbc依赖包     -->  
 <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
   </dependency>

2、配置application.yml 

server:
  port: 8092

#整合1.数据源
spring:
  datasource:
    # mysql高版本加cj
    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开头为0,则整个密码需要使用引号引起  password: "0123456"
    password: root

url各参数类型:
1. serverTimezone=GMT%2B8  指定时区 东八区
2. useUnicode=true&characterEncoding=utf8   开启使用Unicode编码,并且指定字符集utf-8
3. autoReconnect=true   断线是否重新链接.
4. &allowMultiQueries=true   是否允许批量操作

    # 连接池
    hikari:
      # 连接池name
      pool-name: seckill-system
      # 最小空闲连接
      minimum-idle: 5
      # 空闲连接最大时间  默认10分钟
      idle-timeout: 180000
      # 最大连接数, 默认10
      maximum-pool-size: 10
      # 从连接池返回的连接自动提交
      auto-commit: true
      # 连接最大存活时间 0表示永久存货, 默认半小时
      max-lifetime: 180000
      # 连接超时时间,m默认30秒
      connection-timeout: 30000
      # 测试连接是否是可用的查询语句
      connection-test-query: SELECT 1
    

#SpringBoot整合MP
mybatis-plus:
  #指定别名包
  type-aliases-package: com.jt.pojo
  #加载指定的xml映射文件
  mapper-locations: classpath:/mybatis/mappers/*.xml
  #开启驼峰映射
  configuration:
    map-underscore-to-camel-case: true

#Sql日志文件打印
logging:
  level:
    com.jt.mapper: debug

3、dao层添加注解

3.1 @Mapper

@Mapper注解是由Mybatis框架中定义的一个描述数据层接口的注解,即dao层开发。注解往往起到的都是一个描述性作用,用于告诉sprigng框架此接口的实现类由Mybatis负责创建,并将其实现类对象存储到spring容器中。

3.2 @MapperScan

在SpringBoot启动类上面添加@MapperScan注解

作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类

@SpringBootApplication
@MapperScan("com.example.mapper")
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

使用@MapperScan注解多个包

@SpringBootApplication  
@MapperScan({"com.example.mapper1","com.example.mapper2"})  
public class SpringbootApplication {  
    public static void main(String[] args) {  
       SpringApplication.run(SpringbootApplication.class, args);  
    }  
} 

二、MyBatis 编程步骤

准备dept表

实现步骤

(1)配置mybatis-config.xml。在官网里面找到核心配置文件(mybatis-config.xml)的内容复制下来做修改

<?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">
                <!-- 连接数据库配置 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://13.118.127.27:3306/cun_chu_guo_cheng?serverTimezone=GMT%2B8&amp;useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;allowMultiQueries=true"/>
                <property name="username" value="root"/>
                <property name="password" value="111min"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 指定xml文件路径-->
        <mapper resource="mapping/CunchuguochengMapper.xml"/>
    </mappers>
</configuration>
  @Test
  public void mybatis() throws IOException {
    //(2)读取指定的核心配置文件.
    String resource = "mybatis/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //(3)通过SelSessionFactoryBuilder.buid方法创建SqlSessionFactory.
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //(4)获取SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //(5)获取Mapper的接口
    CunchuguochengMapper cunchuguochengMapper = sqlSession.getMapper(CunchuguochengMapper.class);
    //(6)执行mapper接口中的方法,获取业务数据   (7)调用xml文件的Sql语句实现数据获取   (8)mybatis自动封装为对象返回交给用户处理
    List<HashMap<String, Object>> list = cunchuguochengMapper.selectDept();
    // 遍历数据
    for (HashMap<String, Object> hashMap : list) {
      System.out.println(hashMap);
    }
    // 关闭
    sqlSession.close();
  }
}

mapper接口

List<HashMap<String, Object>> selectDept();

xml文件

<select id="selectDept" resultType="java.util.HashMap">
     select * from dept
</select>

po

import lombok.Data;

/**
 * @Description
 * @ClassName Dept2
 * @Author syh
 * @Date 2023/4/20 14:44
 */
@Data
public class Dept2 {
    private Integer deptNo;
    private String dName;
    private String loc;
}

代码结构

运行结果

三、Mapper接口中方法的参数封装: 

1、Mapper层参数封装

(1)将参数封装为实体对象,如果传递的参数是一个对象,则直接调用属性  #{属性名}

(2)将参数封装为Map集合,如果传入的参数是一个Map集合,则直接调用集合中key   #{key值}

(3)删除多条数据的sql语法,将参数封装为数组或list或map,删除多条数据的sql语法,在xml中使用foreach标签

(4)如果传入多个参数,在接口方法中使用注解@Param               List<DemoUser> findAll1(@Param("age") int age, @Param("sex") String sex);

  2、post/put接收参数为一个变量外加一个数组

 

四、sql语句的标签中常用属性

<select id="selectUserInfoById" parameterType="String" resultMap="BaseResultMap">
    select * from userinfos where UserInfoID = #{id}
</select>

1、id :与mapper接口中的方法相对应

2、resultType:返回值类型。增删改不需要。 当结果集中的字段名与属性的名称一致时,才会实现自动的数据封装

3、resultMap:当结果集中的字段名称,与对象中的属性不一致时,可以使用resultMap实现自定义的封装

4、parameterType:表示传入的参数类型,程序会自动识别,建议省略不写

5、statementType:标记操作SQL的对象。取值说明:

STATEMENT:直接操作sql,不进行预编译。获取数据:

<update id="update4" statementType="STATEMENT">
    update tb_car set price=${price} where id=${id}
</update>

PREPARED:预处理,参数,进行预编译,默认。获取数据:

<update id="update5" statementType="PREPARED">
    update tb_car set xh=#{xh} where id=#{id}
</update>

CALLABLE:执行存储过程。获取数据:

<select id="jzajqkdb_tjdetails"statementType="CALLABLE"resultMap="getCaseCount_details_resultMap">
     call jzajqkdb_tjdetails(#{starttime,mode=IN},#{endtime,mode=IN},#{type,mode=IN})
</select>


五、动态sql

1、foreach标签

将传入参数(1,2,3,4,5)封装为①数组 ②List集合 ③Map

集合套入List集合。然后使用foreach标签遍历 

 <delete id="adddelete">
    delete from demo_user where id in
    <foreach collection="array" item="id" open="(" close=")" separator=","> 
       #{id}              
    </foreach>
</delete>

foreach标签中各个属性的作用:
1.collection:(1)类型为数组或者list集合时,数组为array,集合为list。(2)封装到map集合中的list key。(3)封装在对象中的list集合 属性名 
2.item 每次遍历的数据的形参
3.open 循环的开始标签     
4.close 循环的结束标签       
5.index 循环遍历下标 一般不用
6.separator 循环遍历的分割符 //设坡瑞特

 2、Sql-where

动态拼接where,避免传入条件为空,自动去除为空条件的and

<select id="selectByUser" parameterType="Demouser" >
     select * from demo_user
     <where>
          <if test="id !=null"> id= #{id}</if>
          <if test="name !=null">  and name = #{name}</if>
          <if test="age !=null">  and age = #{age}</if>
      </where>
</select>

3、动态Sql-set条件

动态拼接更新语句,自动去除末尾逗号

<update id="updateUserSet" parameterType="user">
     update demo_user
          <set>
              <if test="name !=null">name = #{name},</if>
              <if test="age != null"> age = #{age},</if>
          </set>
          where id = #{id}
</update>  

  

 4、动态Sql-choose、when、otherwise

该放法类似于if  else-if   else

<select id="selectChoose" resultType="User">
     select * from demo_user
     <where>
         <choose>    
              <when test='name !=null'> name = #{name} </when> 
              <when test='birthday != null '> u.birthday = #{birthday} </when> 
              <otherwise> sex = #{sex} </otherwise>    
         </choose>
     </where>
</select>

 六、sql语句简化操作

1、xml中sql语句复用

<!--该标签用户重复sql的封装,一般用于封装字段-->
<sql id="aa">  select * from demo_user</sql> 


<select id="addSelect" resultType="DemoUser">
    <include refid="aa"> </include>  where name = #{name}
</select>

2、SQL 语句映射

@Select、@Insert、@Update、@Delete,简化xml配置

@Delete("delete from user where id=#{id}")
void deleteUserById(Integer id);

3、简化resultType返回值为全类名的情况

当返回的类型为pojo对象,需要写入全类名,简化方法

(1)给resuType的值起别名

(2)当pojo中有多个类中,引入了别名包概念

(3)使用注解,直接在pojo包中类上使用注解@Alias(“别名”)

(4)建议写全路径,方便维护

七、其他标签使用

 1、if标签使用

<select id="getCaseNumTwo" resultType="java.lang.Integer" parameterType="com.hssmartcity.model.CaseDeadline">
        select
         count(*)
        FROM
        casedeadline
        <if test="mark == 1">
            RIGHT JOIN (
            SELECT
            *
            FROM
            casetypes
            WHERE
            CategoryCode IN ( SELECT CaseTypeID FROM casetypes WHERE CategoryCode = #{forCaseTypeId,jdbcType=VARCHAR} )) AS casetypes ON casedeadline.ForCaseTypeId = casetypes.CasetypeID
        </if>
        <if test="mark == 2">
            RIGHT JOIN ( SELECT * FROM casetypes WHERE CategoryCode = #{forCaseTypeId,jdbcType=VARCHAR} ) AS casetypes ON casedeadline.ForCaseTypeId = casetypes.CasetypeID
        </if>
    </select>

2、trim标签

一般用户insert操作,作用为去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀。

<trim prefix="WHERE" prefixOverrides="AND">  </trim>
prefix	给sql语句拼接的前缀
suffix	给sql语句拼接的后缀
prefixOverrides	去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides	去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定
<!--查询操作,去除where条件中多余and-->
<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG 
    <trim prefix="WHERE" prefixOverrides="AND">
	    <if test="state != null">
	      state = #{state}
	    </if> 
	    <if test="title != null">
	      AND title like #{title}
	    </if>
	    <if test="author != null and author.name != null">
	      AND author_name like #{author.name}
	    </if>
    </trim>
</select》

这里写图片描述

     

八、xml文件注意事项

1、#{} 和 ${} 的区别

(1)#{}是占位符,会进行预编译处理,mybatis会将sql中的#{}换为?号,在sql执行前会使用PreparedStatement给sql的   ?号占位符设置参数值,能够避免SQL注入。

(2)$是变量拼接符,属于静态文本替换,不能避免sql注入。当传入参数为字段名称的时候,使用${column}获取参数值,但有sql注入的风险。

2、xml转义字符

'<'在sql语句中出现会报错,原因:xml文档将<符号认成标头,当有大量转义字符的时候,使用:<![CDAT[...内容]]>  将里  面内容变成普通字符串.

3、mybatis使用模糊查询

   1、传入的参数自带%

   2、传入时不带参数,需要在sql中name like '%${name}%'

   3、

<if test="groupname != null  and groupname != ''"> and GroupName like concat('%', #{groupname}, '%')</if>

4、sql语句中包含关键字处理

order是sql语句的关键字,添加反引号进行处理

5、sql语句条件判断

对于String类型,需要先判null,在判''
<if test="userName!=null and userName != ''">
       and UserName like '%${userName}%'
</if>

九、结果集封装

1、resultType

pojo类中的属性名和sql查询出的结果集中字段名保持一致,将查询结果自动封装到pojo中

或者是sql语句查询出来的内容可以使用普通类型接收(String  Integer  等等)

2、resultMap映射 

当结果集中的字段名称与对象中的属性不一致时使用resultMap实现自定义的封装.     

<select id="findAll" resultMap="deptRM">
         select * from dept
</select>

<!--自定义映射关系-->
<resultMap id="deptRM" type="com.jt.Dept">
        <id column="dept_id" property="deptId"/>
        <result column="dept_name" property="deptName"/>
</resultMap>


1.id标签代表主键 (每张表中都会有一个主键)
      1.1.column: 代表sql语句查询出的结果集中的字段.
      1.2.property: pojo对象中的属性
2.result 主键之外其他字段映射关系

3、resultMap标签内容继承

<resultMap id="BaseResultMap" type="com.hssmartcity.model.StandardSpecification">
    <id column="LawId" jdbcType="VARCHAR" property="lawId" />
    <result column="LawName" jdbcType="VARCHAR" property="lawName" />
    <result column="ArticleContent" jdbcType="VARCHAR" property="articleContent" />
</resultMap>

继承上面的resultMap
<resultMap id="ExtBaseResultMap" type="com.hssmartcity.model.StandardSpecification" extends="BaseResultMap">
    <result column="UserName" jdbcType="VARCHAR" property="userName" />
    <result column="ParentLawName" jdbcType="VARCHAR" property="parentLawName" />
</resultMap>

4、不使用映射

mapper

List<HashMap<String, String>> selectProblemSource();

xml

    <select id="selectProblemSource" resultType="java.util.HashMap">
        select * from problem_source
    </select>

结果集

集合中 k 对应字段,v 对应值

 十、 关联查询

1、一对一表关系

pojo封装一对一数据

(1)关联查询

<!-- 一个emp员工表中对应一个dept部门信息 -->
<select  id="allSelect"  resultMap="empRM">
    select emp.id,emp.name,emp.age,dept.dept_id,dept.dept_name  from emp left join dept on emp.dept_id=dept.dept_id
</select>


<!-- 如果映射的字段与对象的属性一致,则可以直接autoMapping=true,省略手动映射,
     当映射字段和对象属性不一致,开启自动映射驼峰规则(dept_id自动变为deptId),
     继而autoMapping省略手动映射。实际开发中推荐手写映射 -->
<resultMap id="empRM" type= "com.jt.pojo.Emp" autoMapping="true">
    <id column="id" property="id"></id>
    <!-- property本表对应的实体类属性 javaType属性表示该类的全路径 /əˌsəʊ siˈeɪʃn/ -->
    <association property="deptPojo" javaType="com.jt.pojo.Dept">   
        <!-- id标签表示主键的字段映射,当然主键字段映射也可以使用result,实际开发中不使用id标签 -->
        <id column="dept_id" property="deptId"></id>
        <result property="deptName" column="dept_name"/>
    </association>
</resultMap>

<!--
association标签是MyBatis中用来定义一对一或多对一关联关系的标签。在ResultMap中使用。其属性如下:
1、property:指定Java Bean中需要映射的属性名。
2、javaType:指定Java Bean的类型。
-->

(2)子查询

<!-- 员工表 一个emp员工表中对应一个dept部门信息 -->
<select id="allSelect1" resultMap="empRM1">
    select * from emp
</select>

<resultMap id="empRM1"  type="com.jt.pojo.Emp">
    <result column="id"  property="id"></result>
    <!-- property:本表对应的实体类属性  
         javaType:另一张表对应的实体类  
         column:本表字段(相关联字段) 
         select:对应的子查询mapper层方法名字,可以跨包调用--> 
    <association property="deptPojo"  javaType="com.jt.pojo.Dept" 
                 column="dept_id" select="com.hssmartcity.mapper.deptSelect"> 
    </association>  
</resultMap>

<!--dept部门表查询-->
<select id="deptSelect" resultMap="deptRM">
     select * from dept where dept_id = #{dept_id}
</select>
<resultMap id="deptRM" type="com.jt.pojo.Dept">
    <result column="dept_id" property="deptId"></result>
    <result column="dept_name" property="deptName"></result>
</resultMap>

<!--
association标签是MyBatis中用来定义一对一或多对一关联关系的标签。在ResultMap中使用。其属性如下:
1、property:指定Java Bean中需要映射的属性名。
2、javaType:指定Java Bean的类型。
3、column:关联字段。
4、select:指定关联查询所使用的Statement ID。
其中,property和javaType两个属性必须设置,其他属性可根据不同情况灵活选择。
-->

2、一对多表关系

pojo封装一对多数据:

(1)关联查询

<!-- 一个部门对应多个员工 -->
<select id="findDept" resultMap="listRM">
      SELECT d.dept_id,d.dept_name,e.id,e.name,e.age FROM
      dept d  LEFT JOIN  emp e  ON   d.dept_id = e.dept_id
</select>


<resultMap id="listRM" type="Dept">
    <id column="dept_id" property="deptId"/>
    <result column="dept_name" property="deptName"/>
    <!-- collection: 封装集合的固定写法.  property:指定属性 ofType:封装List集合的泛型对象 -->
    <collection property="emps" ofType="Emp">
        <id column="id" property="id"/>
    </collection>
</resultMap>

(2)一对多子查询

<!-- 部门表 一个部门对应多个员工 -->
<select id="allSelect3" resultMap="dept">
        select * from dept
</select>
<resultMapid="dept" type="com.jt.pojo.Dept" autoMapping="true">
    <id column="dept_id" property="deptId"></id>
    <result column="dept_name" property="deptName"></result>
    <!-- property:本表实体类属性  
         ofType:另一张表的实体类  
         column:本表字段(相关联字段)
         select:对应的子查询mapper层方法名字,可以跨包调用--> 
    <collection property="empList" 
                ofType="com.jt.pojo.Emp" 
                column="dept_id" 
                select = "com.hssmartcity.mapper.SiBaoInfosMapper.empRm">   
    </collection>
</resultMap>
<!-- 
collection标签是MyBatis中用来定义一对多或多对多关联关系的标签,在ResultMap中使用。其属性如下:
1、property:指定Java Bean中需要映射的集合属性名。
2、ofType:指定Java Bean集合中对象的类型。
3、column:本表关联的字段。
4、select:指定关联查询所使用的Statement ID。
其中,property和ofType两个属性必须设置,其他属性可根据不同情况灵活选择。
 -->


<!-- 员工表 -->
<select id="empRM" resultMap="emp">
      select * from emp where dept_id = #{dept_id}
</select>
<resultMap id="emp" type="com.jt.pojo.Emp"  autoMapping="true">
      <id column="id"  property="id"></id>
</resultMap>

十一、缓存机制

引入缓存可以有效降低用户访问物理设备的频次.提高用户响应速度.

1、默认情况下,只有一级缓存( SqlSession级别的缓存也称为本地缓存)开启。一级缓存默认开启,SqlSession内共享数据

2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。二级缓存 默认开启,需要标识 SqlSessionFactory内共享数据。在映射XML文件中写入cache标签可以使用二级缓存。

1、一级缓存

开启log4j,可以看到试验效果,项目中添加log4j依赖。

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

配置日志打印配置

<setting name="logImpl" value="STDOUT_LOGGING"/>

测试

测试结果一级缓存生效 

假如清除了缓存,看下图效果 

 

一级缓存总结 

前提条件:同一个会话中

1、SQLsession 相同,执行同一个sql(完全相同,查询条件等等),会命中缓存

2、SQLsession 相同,两次查询间执行了增删改操作,不会命中缓存

3、SQLsession 相同,但是手动清除了缓存,不会命中缓存


2、二级缓存(全局缓存)

基于namespace级别的缓存,一个namespace(也就是一个Mapper.xml文件)对应一个二级缓存

工作机制

一个会话,查询一条数据,这个数据会被放在当前会话的一级缓存中;如果会话关闭:如果会话关闭; 一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存;不同的namespace查出的数据会放在自己对应的缓存map中。

效果:数据会从二级缓存中获取查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中

开启二级缓存

第一步:修改mybatis-config.xml

<setting name="cacheEnabled" value="true"/>

如果是springboot集成的mybatis,在配置文件中修改

第二步:配置mapper.xml

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="false"/>


属性:
1、eviction :缓存的回收策略:
    LRU(默认) -最近最少使用的:移除最长时间不被使用的对象。
    FIFO -先进先出:按对象进入缓存的顺序来移除它们。
    SOFT -软引用:移除基于垃圾回收器状态和软引用规则的对象。
    WEAK 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

2、flushInterval:缓存刷新间隔缓存多长时间清空一次,默认不清空,设置一个毫秒值

3、readOnly :是否只读:
        true:只读; mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
        false非只读: mybati s觉得获取的数据可能会被修改。mybatis会利用序列化&反序列的技术克隆一份新的数据给你。非常安全,但是速度慢

4、size存放多少元素

5、type:指定自定义缓存的全类名(默认即可)

 第三步:对应的实体类要实现序列化接口

import lombok.Data;

import java.io.Serializable;

/**
 * @Description
 * @ClassName Dept2
 * @Author syh
 * @Date 2023/4/20 14:44
 */
@Data
public class Dept2 implements Serializable {
    private Integer deptNo;
    private String dName;
    private String loc;
}

测试

 

 修改readOnly 为true,再次测试

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

xml文件中关于缓存的其他设置

设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。所以:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

 <select id="selectByPrimaryKey" resultMap="BaseResultMap" useCache="true">
    select 
    <include refid="Base_Column_List" />
    from t_dm_cust
    where id = #{id,jdbcType=BIGINT}
  </select>

清空缓存,一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。但是如果你不想刷新缓存只需要这么做:

 <update id="updateByPrimaryKey" flushCache="false">
    update t_dm_cust
    set create_time = #{createTime,jdbcType=TIMESTAMP}
 where id = #{id,jdbcType=BIGINT}
  </update>

mybatis有缓存,为什么还要用redis

1、mybatis一级缓存作用域是session,session在commit之后缓存就消失了。

2、mybatis二级缓存作用域是sessionfactory,该缓存是以namespace为单位的(也就是一个Mapper.xml文件),不同namespace下操作互不影响。  
  
3、所有对数据表的改变操作都会刷新缓存,但是一般不要用二级缓存,例如,在UserMapper.xml中有大多数针对User表的操作,但是在另一个XXXManpper.xml中,还有针对user单表的操作,这会导致user在两个命名空间下的数据不一致。

4、如果在UserMapper.xml中做了刷新缓存的操作,在XXXMapper.xml中缓存仍然有效,如果有针对user的单标查询,使用缓存的结果可能会不正确,读到脏数据。

5、redis很好的解决了这个问题,而且比之一、二级缓存的更好,redis可以搭建在其它服务器上,缓存容量可扩展,redis可以灵活的使用在需要的缓存的数据上,比如一些热点数据,统计点赞啊。

十二、PageHelper分页插件(适用于mybatis和mp)

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.4.1</version>
</dependency>
#pagehelper分页插件配置
pagehelper:
  helper-dialect: mysql
  reasonable: false
  support-methods-arguments: true
  params: count=countSql
public PageInfo PageInfogetPunchingCardRecordInfor(Integer pageNum, Integer pageSize, Integer type, DateVO dateVO) {
    // 传入页码和每页条数,只针对最近的一条sql语句
    if (pageNum != null && pageSize!= null) {
         PageHelper.startPage(pageNum, pageSize);
    }
    // 在数据库获得数据
    List<WorkRecord> workRecordList = workRecordDao.getPunchingCardRecordInfor(hashMap);
    // 将获得的数据传入
    PageInfo<WorkRecord> pageInfo = new PageInfo<>(workRecordList);
    // 最后返回前端数据,getTotal是数据总量
    return pageInfo;

    /*   分页插件源码
    @Data
    public class PageList {
        private long totalNum;
        private Object Data;
        public PageList(long totalNum, Object data) {
            this.totalNum = totalNum;
            Data = data;
        }
    }
    */
}

遇到过获取总条数失效情况

1、分页代码与slq语句不相邻

2、new一个PageInfo之后,继续操作,比如将do转为vo就会造成总条数失效

十三、调用储存过程返回多个结果集

创建一个dept表

定义一个存储过程

CREATE DEFINER=`root`@`%` PROCEDURE `hello_procedure`(IN deptName VARCHAR(20))
begin
	SELECT deptName;
	SELECT * FROM dept;
end

controller

  @Autowired
  public CunchuguochengMapper cunchuguochengMapper;

  @GetMapping("/cunchuguocheng")
  public List<List<?>> cunchuguocheng() {
    List<List<?>> list = cunchuguochengMapper.cunchuguocheng("阿里巴巴");
    return list;
  }

mapper

List<List<?>> cunchuguocheng(String deptName);

xml

1、statementType取值说明:
STATEMENT:直接操作sql,不进行预编译,获取数据:$ >> Statement
PREPARED:预处理,参数,进行预编译,获取数据:# >> PreparedStatement(默认)
CALLABLE:执行存储过程————CallableStatement
2、mode=IN:
存储过程有三种类型的参数,分别为 IN(输入参数),OUT(输出参数),INOUT(输入输出参数)。
一个存储过程,可以有多个 IN 参数,至多有一个 OUT 或 INOUT 参数。mode=IN 表示该参数类型为IN。
3、当有多个结果集,就使用多个映射

    <resultMap id="tept1" type="com.syh.demo.Pojo.Dept1">
        <result property="deptName" column="deptname"></result>
    </resultMap>

    <resultMap id="tept2" type="com.syh.demo.Pojo.Dept2">
        <result property="deptNo" column="deptno"></result>
        <result property="deptName" column="deptname"></result>
        <result property="loc" column="loc"></result>
    </resultMap>

    <select id="cunchuguocheng" statementType="CALLABLE" resultMap="tept1, tept2">
        call hello_procedure(#{deptName,mode=IN,jdbcType=VARCHAR})
    </select>

浏览器测试

总结:

当有一个结果集,正常映射。当有多个结果集,手动创建多个映射

xml中的映射可以不用对象,例如

    <resultMap id="map1" type="java.util.HashMap">
        <result property="siBaoType" column="siBaoType" />
        <result property="total" column="total" />
        <result property="weiGuiNum" column="weiGuiNum" />
        <result property="weiWeiGuiNum" column="weiWeiGuiNum" />
        <result property="senHeLv" column="senHeLv" />
    </resultMap>

    <select id="selectProblemNum" statementType="CALLABLE" resultMap="map1, map1, map1, map1">
        call selectProblemNum(#{startTime,mode=IN,jdbcType=VARCHAR}, #{endTime,mode=IN,jdbcType=VARCHAR})
    </select>

十四、动态回显

insert语句插入数据,动态回显id

如果传入对象,那么将自动生成的主键映射到对象的id属性上;如果传入Map,那么将自动生成的主键映射到Map的id key上。

<!-- 
useGeneratedKeys="true",以启用自动生成主键,并将生成的主键列名指定为 id,
将自动生成的主键值映射到 Java 对象的 id 属性上

keyColumn: 这个属性指定了数据库表中用于存储自动生成的主键值的列名。在你的例子中,
keyColumn="id" 表示自动生成的主键值将会存储在表中名为 id 的列中。

keyProperty: 这个属性指定了将自动生成的主键值映射到哪个 Java 对象的属性上。
在你的例子中,keyProperty="id" 表示自动生成的主键值将会映射到 Java 对象的 id 属性上。
 -->
<insert id="addKunjing" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into big_difficulty_child
        (
            id,
            type
        )
        values
        (
            null,
            #{type}
        )
</insert>

MyBatis-Plus

全自动的ORM,可以做大部分的单表的CRUD。是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

官网:MyBatis-Plus (baomidou.com)

一、springboot整合mybatis

 1、修改pom文件和yml文件

<dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.4.3</version>
</dependency>
mybatis-plus:
    # 指定别名包
    type-aliases-package:com.jt.pojo

2、编辑POJO对象

核心: 实现对象关系映射->对象与数据库中表的映射关系

  1. 对象名称与表名一一映射.
  2. 对象的属性与表中的字段一一映射.
@TableName("demo_user")  //对象与表名映射   注意:当类名和包名一样时,可以省略括号及其内容
public class User implements Serializable {

    @TableId(type = IdType.AUTO)//标记这是主键自增
    private Integer id;

    private String name;

    @TableField("age")  //实现属性与字段映射  注意:当字段名与属性名一样,TableField注解可以省略
    private Integer age;

    private String sex;

    @TableField(exist = false) //表示该属性不是数据库里面的字段,默认true
    private List<ItemCat> children;  //业务属性

}

3、编辑Mapper对象

继承BaseMapper<> 接口


 

二、MP动态Sql实现原理

1. 用户调用接口方法 userMapper.insert(User)方法

2. 根据UserMapper的接口找到父级接口BaseMapper<T>

3. 根据父级接口动态获取当前接口的泛型对象T

4. 根据泛型T 获取指定的注解@TableName("demo_user"),之后获取表名demo_user

5. 根据泛型对象T,获取其中的属性,之后再找到属性的注解@TableField("id"),之后再次获取注解的值, 即字段名称.

6. 根据字段名称,获取对应属性的值.

7. 根据Sql拼接 形成最终的可以执行的Sql.

8. MP将生成的Sql交给Mybatis执行入库操作.

三、MP的crud

1、增加操作

(1)insert()

@Test
void updateUser() {
    User user = new User();
    user.setAge(12).setName("mybatis-plus").setSex("男").setId(null);
    //insert根据传入对象中不为空的属性进行sql语句书写
    userMapper.insert(user);
}

(2)insertList() 批量插入

@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {
    @Override
    public void insertList() {
        List<Users> list = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            Users users = new Users();
            users.setUserName("yy" + i);
            users.setAge(18);
            users.setEmail("123@qq.com");
            users.setAddress("临汾" + i);
            users.setAccount("1" + i);
            users.setPassword("pw" + i);
            list.add(users);
        }
        long start = System.currentTimeMillis();
        baseMapper.insertList(list);
        long end = System.currentTimeMillis();
        System.out.println("5000条数据插入,耗时:" + (end - start));
    }
}

2、查询操作

(1)selectById()参数为主键列的值

Integer id =1;
User user =  userMapper.selectById(id);

(2)selectList()拼接where条件

// QueryWrapper条件查询
@Test
public void selectLIst2(){
     String name = "黑熊精";
     Integer age =   null;
     QueryWrapper<User> queryWrapper = new QueryWrapper<>();
     // 参数一为传入条件判断,boolean类型,true放行   参数二为数据库字段    参数三传入的数据
     // 条件之间默认连接符是and,省略不写。如果是or,条件之间需要.or()
     // 连接符:  eq =      gt >      lt <      ge >=    le <=       ne <>
     queryWrapper.eq(StringUtils.hasLength(name), "name", name).or().eq(age != null, "age", age);
     List<User> list = userMapper.selectList(queryWrapper);
     System.out.println(list);
     // 条件构造器实现复用
     queryWrapper.clean  
}

(3)selectObjs()只获取主键列的数据,关联查询时使用

@Test

public void selectObjs(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("sex","女");
    List<Object> list = userMapper.selectObjs(queryWrapper);
    System.out.println(list);
}

(4)selectOne 获取详情

@Override
public UserInfos findUserByUserAccount(String userAccount) {
   // 查询条件
   QueryWrapper<UserInfos> queryWrapper = new QueryWrapper<>();
   queryWrapper.eq("UserAccount", userAccount);
   UserInfos userInfos = userInfosDao.selectOne(queryWrapper);
   return userInfos;
}

(5)模糊查询

queryWrapper.likeLeft("name", "君").eq("sex", "女");
like:%君%    likjeLeft:%君  likeRight:君%

(6)in和order的使用

@Test

public void selectInOrder(){
    Integer[] integers = {1,2,3,4,5,6,7,8};
    //不能传入int[]
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    //orderByDesc:降序    orderByAsc:升序
    queryWrapper.in("id", integers).orderByDesc("age", "sex");
    List<User> list = userMapper.selectList(queryWrapper);
    System.out.println(list);
}

(7)查询部分字段

方法一:查询id和username字段

@RequestMapping("/selectByWrapper1")
public List<SysUser> selectByWrapper1(){
   QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
   queryWrapper.select("id", "username");//指定查询某字段
   List<SysUser> sysUsers=sysUserService.list(queryWrapper);
   return sysUsers;
}

方法二:查询除了id和email字段除外的其他字段

@RequestMapping("/selectByWrapper2")
public List<SysUser> selectByWrapper2(){
   QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
   queryWrapper.select(SysUser.class, info ->!info.getColumn().equals("id") && !info.getColumn().equals("email"));//查询指定某字段以外的数据
   List<SysUser> sysUsers=sysUserService.list(queryWrapper);
   return sysUsers;
}

(8)selectCount根据某个字段获取条数

QueryWrapper<CommunityActivity> communityActivityQueryWrapper = new QueryWrapper<>();
communityActivityQueryWrapper.eq("CommunityName", communityActivityDTO.getCommunityName());
Integer integer = communityActivityMapper.selectCount(communityActivityQueryWrapper);

(9)分组查询

QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as count, tel");
lqw.groupBy("tel");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);

3、更新操作方式

(1)updateById的方式

这种方式需要传入实体类,生成的Sql,根据主键更新所有字段。所以更推荐第二种。

User user = new User();
user.setUserId(1);
user.setAge(23);
userMapper.updateById(user);

(2)UpdateWrapper构造修改条件

这种方式可以只更新指定的几个字段。

UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper
.set("age", 23);   //set实现的是sql语句的update set age = 18的部分
.eq("name","张三")   //eq实现的是sql 的where 后面的部分。
userMapper.update(null, updateWrapper);   //第一个参数一定是null,才只会更新Wrapper里的指定字段。

4、删除操作

(1)根据Id删除

@Test
public void testDeleteById(){
    int result = userMapper.deleteById(5L);
    system.out.println(result);
}

(2)批量删除

@Test
public void testDeleteBatchIds(String[] ids) {
    int result = enterpriseMapper.deleteBatchIds(Arrays.asList(ids));
    system.out.println(result);
}

@Override
public int deleteResidentiaInfoByIds(String[] ids) {
    List<String> collect = Arrays.stream(ids).collect(Collectors.toList());
    return residentiaInfoMapper.deleteBatchIds(collect);
}

(3)deleteByMap条件删除

@Test
public void testDeleteByMap() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("name", "Helen");
    map.put("age", 18);
    int result = userMapper.deleteByMap(map);
    system.out.println(result);
}

(4)QueryWrapper条件删除

@Test
public void deleteByWrapper2(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("age",23).eq("name","向中");
    int delete = userMapper.delete(queryWrapper);
}

(5)LambdaQueryWrapper删除

// LambdaQueryWrapper条件删除
@Test
public void deleteByWrappersLamdba(){
    LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.<User>lambdaQuery();
    lambdaQueryWrapper.eq(User::getAge,33).eq(User::getEmail,"xn@baomidou.com");
    int delete = userMapper.delete(lambdaQueryWrapper);
    System.out.println("删除记录:"+delete);
}

四、MP分页查询

selectPage()方法,分页查询为方言,使用MP可以实现代码跨数据库使用,需要手动指定是哪款数据库

1、分页查询实现步骤

(1)在config包中新建MybatisPlusConfig类,类上面加注解:@Configuration  ,标记这是一个配置类,相当于早期的xml文件

(2)引入官网中的方法

/**
 * @Description
 * @ClassName MybatisPlusConfig
 * @Author syh
 * @Date 2022/11/10 15:05
 */
@Component
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

(3)配置Service层代码

   /**
     * @MethodName getBusinessAdministration11
     * @Description  mybatis-plus分页测试  可分页可不分页版本
     * @param pageNum   页码
     * @param pageSize   每页条数
     * @return com.hssmart.vo.PaginationVO
     *
     * @Author syh
     * @Date 2022/11/10 16:25
     */
    @Override
    public PaginationVO getBusinessAdministration11(Integer pageNum, Integer pageSize) {

        Page<EnterprisePartyBuilding> page;
        if (pageNum == null || pageSize == null) {
            /*
             * 如果想不分页,两个参数就传入负值
             * 参数一为开始索引  参数二为条数。实际应用中会传入页码和条数
             * */
            page = new Page<>(-1, -1);
        } else {
            page = new Page<>((long) (pageNum - 1) * pageSize, pageSize);
        }
        LambdaQueryWrapper<EnterprisePartyBuilding> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 分页查询方法:参数一page<>接口类型   参数二为传入的其他where条件
        page = enterprisePartyBuildingMapper.selectPage(page, lambdaQueryWrapper);
        // 获取总条数
        long total = page.getTotal();
        // 获取分页后的数据
        List<EnterprisePartyBuilding> records = page.getRecords();
        PaginationVO paginationVO = new PaginationVO();
        paginationVO.setTotalNum(total);
        paginationVO.setData(records);
        return paginationVO;
    }
/**
 * @Description 分页信息封装
 * @ClassName PaginationVO
 * @Author syh
 * @Date 2022/11/10 16:10
 */
@Data
public class PaginationVO {

    private Long totalNum;

    private Object data;
}

五、自动填充功能

1、编辑pojo层,添加注解

@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{

    //表示入库时需要自动赋值
    @TableField(fill = FieldFill.INSERT)
    private Date created;  

    //表示入库/更新时自动赋值.
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updated;  
}

2、实现元对象处理器接口

创建一个config包,创建MyMetaObjectHandler类实现接口

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        Date date =new Date();
        this.setFieldValByName("created",date,metaObject);
        this.setFieldValByName("updated",date, metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        Date date =new Date();
        this.setFieldValByName("updated",date,metaObject);
    }
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

S Y H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值