MyBatis学习笔记整理详细

MyBatis笔记

写在前面:欢迎来到「发奋的小张」的博客。我是小张,一名普通的在校大学生。在学习之余,用博客来记录我学习过程中的点点滴滴,也希望我的博客能够更给同样热爱学习热爱技术的你们带来收获!希望大家多多关照,我们一起成长一起进步。也希望大家多多支持我鸭,喜欢我就给我一个关注吧!

文章目录

  • MyBatis笔记
    • 1.配置
    • 2.目录结构
    • 3.MyBatisUtils.java配置
    • 4.pom.xml配置
    • 5.Mapper.xml配置
    • 6.mybatis-config.xml配置
    • 7.CRUD操作
      • 1.namespace
      • 2.select
      • 3.增删改均需要提交事务,否则数据无法写入数据库
      • 4.优化CRUD操作
        • 1.万能Map
        • 2.模糊查询
    • 8.配置解析
      • 1.核心配置文件
      • 2.环境配置(environments)
      • 3.属性(properties)(掌握)
      • 4.类型别名(typeAliases)(掌握)
      • 5.设置(settings)
      • 6.其他配置
      • 7.映射器
      • 8.结果映射(重点)
    • 8.日志
      • 8.1日志工厂
      • 8.2LOG4J
    • 9.分页
      • 9.1使用Limit分页
      • 9.2RowBounds分页
      • 9.3分页插件
    • 10.使用注解开发
      • 10.1、面向接口编程
      • 10.2使用注解开发
      • 10.3CRUD
    • 11.Lombok
      • Lombok的优缺点
      • 总结
    • 12.多对一处理
      • 测试环境搭建
      • 按照查询嵌套处理
      • 按照结果嵌套处理
    • 13.一对多处理
      • 按照结果嵌套处理
      • 按照查询嵌套处理
      • 小结
    • 14.动态SQL
      • 搭建环境:
      • if
      • choose、when、otherwise
      • trim、where、set
      • 总结
      • SQL片段
      • 注意事项:
      • Foreach
      • 总结:
      • 建议:
    • 15.缓存
      • 15.1简介
      • 15.2、Mybatis缓存
      • 15.3一级缓存
      • 15.4二级缓存
      • 15.5缓存原理图
      • 自定义缓存:
    • 主流缓存:
    • 小结

官方文档:https://mybatis.org/mybatis-3/zh/index.html

1.配置


  1. mysql数据库
  2. Maven
  3. mybatis3.6.1

2.目录结构

MyBatis学习笔记整理详细_第1张图片

3.MyBatisUtils.java配置

package com.zhang.utils;

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;


/**
 * 工具类
 */
//sqlSessionFactory-->sqlSession
public class MyBatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            //使用mubatisd第一步:获取sqlSessionFactory对象

//            String resource = "org/mybatis/example/mybatis-config.xml";
            String resource = "./mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //第二步:获取对象
    //既然有了 SqlSessionFactory,顾名思义,
    //我们可以从中获得 SqlSession 的实例
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }


}

4.pom.xml配置



    4.0.0

    com.rui
    MyBatistest
    pom
    1.0-SNAPSHOT

    
    
        
        
            mysql
            mysql-connector-java
            5.1.47
        
        
        
            org.mybatis
            mybatis
            3.5.2
        
        
        
            junit
            junit
            4.12
        
    



    
        
            
                src/main/resources
                
                    **/*.properties
                    **/*.xml
                
                true
            

            
                src/main/java
                
                    **/*.properties
                    **/*.xml
                
                true
            
        
    



5.Mapper.xml配置



//namespace是搭建Dao操作与Mapper接口的连接

//id是调用Dao里的函数名
    

注意:运行时建议删除Mapper.xml中文注释,防止中文产生的报错情况

6.mybatis-config.xml配置




    
        
            
            
                
                
                
                
            
        
    
    //每次增加mapper.xml记得注册到这里
    
        
    

注意:config-xml的中文注释建议运行时删除,防止运行出错,每次新增Mapper.xml记得注册到这里

7.CRUD操作

1.namespace

namespace中的包名要和Dao/Mapper接口的包名一致

2.select

选择,查询语句

  • id:就是对应的namespace中的方法名
  • resultType:sql语句执行的返回值
  • parameterType:参数类型
  1. 编写接口

    import com.zhang.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        /**
         * 查询全部用户
         * @return
         */
        public List getUserList();
    
        /**
         * 根据id查询用户
         * @param id
         * @return
         */
        public User getUserById(int id);
    
        /**
         * 添加一个用户
         * @param user
         * @return
         */
        public int insert(User user);
    
        /**
         * 修改用户
         * @param user
         * @return
         */
        public int updateUser(User user);
    
        /**
         * 删除
         * @param id
         * @return
         */
        public int deleteUser(int id);
    }
    
  2. 编写Mapper.xml对应的sql语句

    
    
    
        
    
        
    
        
            insert into mybatis.user (id,name ,pwd) values (#{id},#{name},#{pwd})
        
    
        
            update mybatis.user set name = #{name},pwd=#{pwd} where id=#{id};
        
        
        
            delete from mybatis.user where id=#{id}
        
    
    
  3. 测试

    package com.zhang.dao;
    
    import com.zhang.pojo.User;
    import com.zhang.utils.MyBatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.junit.Test;
    
    import java.util.List;
    
    public class UserDaoTest {
        @Test
        public void test(){
            //获得sqlsession对象
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
            //执行sql
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List userList = mapper.getUserList();
    
            //打印
            for (User user:userList) {
                System.out.println(user);
            }
    
            //关闭sqlsessiopn
            sqlSession.close();
        }
        @Test
        public void getUserById(){
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            User use = mapper.getUserById(1);
    
            System.out.println(use);
    
            sqlSession.close();
        }
        @Test
        public void addUser(){
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            int t = mapper.insert(new User(6, "李老六", "111"));
    
            if (t>0){
                System.out.println("插入成功!");
                //提交事务
                sqlSession.commit();
            }
    
            sqlSession.close();
        }
        @Test
        public void updateUser(){
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            int i = mapper.updateUser(new User(2, "大傻子", "222"));
    
            if (i>0){
                System.out.println("修改成功!");
                sqlSession.commit();
            }
            sqlSession.close();
        }
        @Test
        public void deleteUser(){
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            int i = mapper.deleteUser(2);
    
            if (i>0){
                System.out.println("删除成功!");
                sqlSession.commit();
            }
            sqlSession.close();
        }
    }
    
    

3.增删改均需要提交事务,否则数据无法写入数据库

//提交事务
sqlSession.commit();

4.优化CRUD操作

1.万能Map

  • 假设我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map!

  •    /**
         * 添加多个
         * @param map
         * @return
         */
        public int addUser2(Map map);
    
    
    
  •     
            insert into mybatis.user (id,name ,pwd) values (#{userid},#{username},#{userpwd})
        
    
  •        map.put("userid","8");
            map.put("username","王老六");
            map.put("userpwd","888888");
         
            mapper.addUser2(map);
    
    
    **这样可以达到参数灵活使用**
    
    Map传递参数,直接在sql中取出key即可!【parameterType="map"】
    
    对象传递参数,直接在sql中取出对象的属性即可!【parameterType="com.zhang.pojo.User"】
    
    只有一个基本类型参数情况下,可以直接在sql中取到!【parameterType="int"】
    
    **多个参数用Map或者注解**
    

2.模糊查询

模糊查询怎么写?

1,java代码执行时,传递通配符% %

List list = mapper.getUserLike("李");

2.在sql拼接中使用通配符!

select * from mybatis.user where name like "%"#{value}"%"

8.配置解析

1.核心配置文件

网页链接:https://mybatis.org/mybatis-3/zh/configuration.html

  • mybatis-config.xml
  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

2.环境配置(environments)

MyBatis 可以配置成适应多种环境

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

注意一些关键点:

  • 默认使用的环境 ID(比如:default=“development”)。
  • 每个 environment 元素定义的环境 ID(比如:id=“development”)。
  • 事务管理器的配置(比如:type=“JDBC”)。
  • 数据源的配置(比如:type=“POOLED”)。

Mybatis默认的事务管理器是jdbc,连接池:POOLED

学会使用配置多套环境

3.属性(properties)(掌握)

我们可以通过properties属性来实现引用配置

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。 【db.properties】

编写配置文件:

db.properties

driver = com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username=root
password=root

在核心配置文件中引入:


  
  

可以直接引入外部文件

可以在其中增加属性配置

如果外部和内部字段相同,优先使用外部属性

4.类型别名(typeAliases)(掌握)

  • 类型别名可为 Java 类型设置一个缩写名字。
  • 它仅用于 XML 配置,意在降低冗余的全限定类名书写 .
 

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean .

扫描实体类的包,它默认别名就是这个实体类的类名,首字母小写。

 
        
 

在实体类少的时候,使用第一种方式。

在实体类多的时候,建议使用第二种。

还可以在实体类上增加注解

@Alias("user")
public class User {

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

5.设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled 是否允许单个语句返回多结果集(需要数据库驱动支持)。 true | false true
useColumnLabel 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 true | false true
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException) NONE, WARNING, FAILING NONE
defaultExecutorType 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 任意正整数 未设置 (null)
defaultFetchSize 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 任意正整数 未设置 (null)
defaultResultSetType 指定语句默认的滚动策略。(新增于 3.5.2) FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) 未设置 (null)
safeRowBoundsEnabled 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 true | false False
safeResultHandlerEnabled 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 true | false True
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 SESSION | STATEMENT SESSION
jdbcTypeForNull 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 OTHER
lazyLoadTriggerMethods 指定对象的哪些方法触发一次延迟加载。 用逗号分隔的方法列表。 equals,clone,hashCode,toString
defaultScriptingLanguage 指定动态 SQL 生成使用的默认脚本语言。 一个类型别名或全限定类名。 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) 一个类型别名或全限定类名。 org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 true | false false
returnInstanceForEmptyRow 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) true | false false
logPrefix 指定 MyBatis 增加到日志名称的前缀。 任何字符串 未设置
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
proxyFactory 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 CGLIB | JAVASSIST JAVASSIST (MyBatis 3.3 以上)
vfsImpl 指定 VFS 的实现 自定义 VFS 的实现的类全限定名,以逗号分隔。 未设置
useActualParamName 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) true | false true
configurationFactory 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) 一个类型别名或完全限定类名。 未设置

一个配置完整的 settings 元素的示例如下:


  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

6.其他配置

  1. typeHandlers(类型处理器)
  2. objectFactory(对象工厂)
  3. plugins(插件)
    • mybatis-generator-core
    • mybatis-puls
    • 通用mapper

7.映射器

MapperRegistry:注册绑定我们的Mapper文件:

方式一:【推荐使用】

​```properties



方式二:

```properties
    

        
    

​ 使用方式二注意点:

  • 接口和Mapper配置文件必须同名
  • 接口和配置文件必须在同一个包下

方式三:

    


        
    

​ 使用方式三注意点:

  • 接口和Mapper配置文件必须同名
  • 接口和配置文件必须在同一个包下

8.结果映射(重点)

   
        
        
        
    
    

resultMap 元素是 MyBatis 中最重要最强大的元素 ,.

ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
    1. mybatis-generator-core
    2. mybatis-puls
    3. 通用mapper

8.日志


8.1日志工厂

如果一个数据库操作出现了异常,我们需要排错,日志是最好的工具。

曾经sout,debug

现在:日志工厂!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Qwu3jSs-1589942405751)(C:\Users\张锐的~1\AppData\Local\Temp\1588294711954.png)]

  • SLF4J
  • LOG4J 【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】
  • NO_LOGGING

在Mybatis中具体使用哪一个日志,在设置中设定

STDOUT_LOGGING标准日志输出

在MyBatis核心配置文件mybatis-config.xml中,配置我们的日志!

   
        
    

日志信息:

Opening JDBC Connection
Created connection 1282811396.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4c762604]
==>  Preparing: select * from mybatis.user where id=? 
==> Parameters: 3(Integer)
<==    Columns: id, name, pwd
<==        Row: 3, 王五, 123456
<==      Total: 1
User{id=3, name='王五', pwd='123456'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4c762604]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4c762604]
Returned connection 1282811396 to pool.

8.2LOG4J

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件.
  • 也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

1.先导包log4j的包到pom.xml

<dependencies>
        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.17version>
        dependency>
    dependencies>

2.在resources文件中配置log4j.properties

  1. #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    log4j.rootLogger=DEBUG,console,file
    
    #控制台输出的相关设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    
    #文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/zhang.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
    
    #日志输出级别
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    log4j.logger.java.sql.PreparedStatement=DEBUG
    

3.在mybatis-config.xml中配置log4j为日志的实现

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    settings>

4.log4j的使用

简单使用

  1. 在要使用的LOG4J的类中,导入import org.apache.log4j.Logger;

  2. 日志对象,参数为当前类的class

    static Logger logger = Logger.getLogger(UserDaoTest.class);
    
  3. 日志级别

            logger.info("info:进入了testLog4j");
            logger.debug("debug:进入了testLog4j");
            logger.error("error:进入了testLog4j");
    

9.分页

思考:为什么要分页?

  • 减少数据的处理量

9.1使用Limit分页

语法:SELECT * FROM user LIMIT startIndex,pageSize
SELECT * FROM user LIMIT 3;#[0,n]

使用Mybatis实现分页,核心sql

  1. 接口

        /**
         * 分页查询
         * @param map
         * @return
         */
        List<User> getUserByLimit(Map<String, Integer> map);
    
  2. Mapper.xml

        <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
            select * from mybatis.user limit #{startIndex},#{pageSize}
        select>
    
  3. 测试

    @Test
    public void getUserLimit(){
        //获得sqlsession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //        logger.info("进入sql语句查询-----------------------");
        //执行sql
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        Map<String, Integer> map=new HashMap<>();
    
        map.put("startIndex",0);
        map.put("pageSize",3);
    
        List<User> user = mapper.getUserByLimit(map);
    
        //打印
        for (User t:user) {
            System.out.println(t);
        }
        //        logger.info("查询结束--------------------------");
        //关闭sqlsessiopn
        sqlSession.close();
    }
    

9.2RowBounds分页

了解即可

不再使用sql实现分页

  1. 接口

     /**
         * 分页查询2
         * @return
         */
        List<User> getUserByRowBounds();
    
  2. Mapper.xml

    <select id="getUserByRowBounds" resultMap="UserMap">
        select * from mybatis.user
    select>
    
  3. 测试

    @Test
    public void getUserRowBounds(){
        //获得sqlsession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //        logger.info("进入sql语句查询-----------------------");
        //创建RowBounds
        RowBounds bounds = new RowBounds(1,2);
    
        //执行sql
        List<User> user = sqlSession.selectList("com.zhang.dao.UserMapper.getUserByRowBounds",null,bounds);
    
    
        //打印
        for (User t:user) {
            System.out.println(t);
        }
        //        logger.info("查询结束--------------------------");
        //关闭sqlsessiopn
        sqlSession.close();
    }
    

9.3分页插件

了解即可。

MyBatis学习笔记整理详细_第2张图片

https://pagehelper.github.io/

10.使用注解开发


10.1、面向接口编程

.大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
.根本原因: 解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准I使得
开发变得容易,规范性更好
-在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部
是如何实现自己的,对系统设计人员来讲就不那么重要了;
-而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设
计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

关于接口的理解

-接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
.接口的本身反映了系统设计人员对系统的抽象理解。
. 接口应有两类:
-第一-类是对一个个体的抽象,它可对应为一一个抽象体(abstract class);
-第二类是对一个个体某- -方面的抽象,即形成一一个抽象面(interface) ;
.-个体有可能有多个抽象面。抽象体与抽象面是有区别的。

三个面向区别

. 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法.
-面向过程是指,我们考虑问题时,以- -个具体的流程(事务过程)为单位,考虑它的实现.
-接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一一个问题.更多的体现就是对系统整体的
架构

10.2使用注解开发

1.注解在接口上实现

    @Select("select * from user")
    List<User> getUsers();

2.需要在核心配置文件绑定接口!

    <mappers>
        <mapper class="com.zhang.dao.UserMapper"/>
    mappers>

3.测试

本质:反射机制实现

低层:动态代理

10.3CRUD

我们可以在工具类JDBC.class创建的时候实现自动提交事务!

   public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);
    }

编写接口,增加注解!

    @Select("select * from user")
    List<User> getUsers();

    /**
     * 方法前面存在多个参数,每个参数前加注解@Param
     * @param idd
     * @param namee
     * @return
     */
    @Select("select * from user where id=#{id} and name=#{name}")
    User getUserById(@Param("id") int idd,@Param("name")String namee);

    @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
    int addUser(User user);

    /**
     * 修改
     * @param user
     * @return
     */
    @Update("update user set name=#{name},pwd=#{password} where id=#{id}")
    int updateUser(User user);

    /**
     * 删除
     * @param uid
     * @return
     */
    @Delete("delete from user where id=#{id}")
    int deleteUser(@Param("id") int uid);

注解的命名和sql语句的命名匹配,否则无法对应取值!

【注意:我们必须要将接口注册绑定到mybatis-config.xml核心配置中】

   <mappers>
        <mapper class="com.zhang.dao.UserMapper"/>
    mappers>

关于@param()注解

  • 基本类型的参数类型或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型,可以忽略,但是建议加上
  • 我们在sql中引用的就是param中设定的属性

//#{}防止sql注入,${}不安全

11.Lombok


Lombok是一款Java开发插件, 使得Java开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的Java
模型对象(POJO)。在开发环境中使用L .ombok插件后,Java开发人员可以节省出重复构建,诸如hashCode和equals这样的方法以及各
种业务对象模型的accessor和ToString等方法的大量时间。对于这些方法,它能够在编译源代码期间自动帮我们生成这些方法,并没有如
反射那样降低程序的性能。

Lombok常用注解说明
●@NonNull: 用在方法参数前,会自动对该参数进行非空校验,为空抛出NPE (NullPointerException)
●@Clenup:自动管理资源,用在局部变量之前在当前变量范围内即将执行完毕退出前会遣理资源 生成-nilly的代码关闭流
@Gbter/@Setter.用在霸性上,不用自己手写etterogetter万法, 还可指定访问范围
●@ToSting: 用在类上,可以自动覆写toString方法
●@EqualsAndHashCode: 用在类上,自动生成equals方法和hashCode方法
@NoArgsConstructor. @RequredArgsConstructor and @AllArgsConstructor:用在类上,自动生成无参构造和使用所有参数的有参构造函数。
●@Data: 用在类上,相当于同时使用了@ToStnng. @EquaisAnarlasnCote. @Getter, @Setter和@RequnedArgsConstrutor这些注解, 对
POJO类十分青用
●@Value: 用在类上,是@Data的不可变形式,相当于为属性添加final声明, 只提供getter方法,而不提供setter方法
@SneakyThrows:自动抛受检异常.而无需显式在方法上使用throws语句
●@Synchronized: 用在方法上,将方法声明为同步的,并自动加锁
●@Getter(azy=true): 可以替代经典的Double Check Lock样板代码

使用步骤:

  1. 在idea中安装Lombok插件

  2. 在项目pom.xml中导入Lombok的jar包

    <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
     </dependency>
    
  3. 在实体类上加lombok注解

    @Getter and @Setter
    @FieldNameConstants
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
    @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
    @Data
    @Builder
    @SuperBuilder
    @Singular
    @Delegate
    @Value
    @Accessors
    @Wither
    @With
    @SneakyThrows
    

    @Data:无参构造,get,set,hashcode,tostring,equals等方法.

    @AllArgsConstructor有参构造
    @NoArgsConstructor无参构造
    

Lombok的优缺点

优点:
1.能通过注解的形式自动生成构造器、getter/setter. equals. hashcode. toString等方法, 提高了一定的开发效率
2.让代码变得简洁,不用过多的去关注相应的方法
3.属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等
缺点:
1.不支持多种参数构造器的重载
2.虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度

总结

Lombok虽然有很多优点,但L ombok更类似于一种IDE插件, 项目也需要依赖相应的jar包。Lombok依赖jar包是因为编译时要用它
的注解,为什么说它又类似插件?因为在使用时,eclipse或Inteli] IDEA都需要安装相应的插件,在编译器编译时通过操作AST (抽象
语法树)改变字节码生成,变向的就是说它在改变java语法。

它不像spring的依赖注入或者mybatis的ORM-样是运行时的特性,而是编译时的特性。这里我个人最感觉不爽的地方就是对插件的
依赖!因为Lombok只是省去了一些人工生成代码的麻烦,但IDE都有快捷键来协助生成getter/setter等方法,也非常方便。

知乎上有位大神发表过对Lombok的一-些看法:
这是一一种低级趣味的插件,不建议使用。JAVA发展到今天,各种插件层出不穷,如何甄别各种插件的优劣?能从架构上优化你的设计的,能提高应用程序性能的,实现高度封装可扩展的…像lombok这种, 像这种插件,已经不仅仅是插件了,改变了你如何编写源码,事实上,少去了的代码,
你写上去又如何?如果JAVA家族到处充斥这样的东西, 那只不过是一坨披着金属颜色的屎,迟早会被其它的语言取代。

12.多对一处理


MyBatis学习笔记整理详细_第3张图片

  • 多个学生,对应一个老师
  • 对于学生而言,关联。。多个学生关联一个老师
  • 对于老师而言,集合。。一个老师有很多学生

SQL语句:

CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');

CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

测试环境搭建

  1. 导入lombok
  2. 新建实体类Teacher,Student
  3. 建立Mapper接口
  4. 建立Mapper.xml文件
  5. 在核心配置文件中绑定注册Mapper接口或文件
  6. 测试查询是否成功!

按照查询嵌套处理


<select id="getStudent" resultMap="StudentTeacher">
select * from student
select>

<result property="id" column="id"/>



resultMap>

    select * from blog where 1=1
    
        and title=#{title}
    
    
        and author=#{author}
    

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员挑选的 Blog)。

  <select id="queryChoose" resultType="blog" parameterType="map">
        select * from blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                when>
                <when test="author != null">
                    and author = #{author}
                when>
                <otherwise>
                    and views = #{views}
                otherwise>
            choose>
        where>
    select>

trim、where、set

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

    <select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from blog
        <where>
            <if test="title != null">
                title=#{title}
            if>
            <if test="author != null">
                and author=#{author}
            if>
        where>
    select>

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:


  update Author
    
      username=#{username},
      password=#{password},
      email=#{email},
      bio=#{bio}
    
  where id=#{id}

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

总结

所谓动态sql,本质是sql,只是在sql层面增加了逻辑代码。

SQL片段


有时候,我们会抽取一些代码,方便复用!

  1. 使用SQL标签抽取公共部分

        <sql id="ift">
            <if test="title != null">
                title=#{title}
            </if>
            <if test="author != null">
                and author=#{author}
            </if>
        </sql>
    
  2. 在需要使用的地方使用include引用

       <select id="queryBlogIF" parameterType="map" resultType="blog">
            select * from blog
            <where>
                <include refid="ift"></include>
            </where>
        </select>
    

注意事项:

  • 最好基于单表来定义SQL片段
  • 不要存在where标签

Foreach


select * from user where 1=1 and 
(id=1 or id=2 or id=3)

  <foreach item="id" collection="ids"
      open="(" separator="or" close=")">
        #{id}
  </foreach>

MyBatis学习笔记整理详细_第4张图片

    <select id="queryBlogForeach" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>
    //查询第1-2-3号记录的博客
    List<Blog> queryBlogForeach(Map map);

    @Test
    public void queryBlogForeach(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Map map = new HashMap();

        ArrayList<Integer> list = new ArrayList();

        list.add(1);
        list.add(2);
        list.add(3);

        map.put("ids",list);

        List<Blog> blogs = mapper.queryBlogForeach(map);

        for (Blog blog : blogs) {
            System.out.println(blog);
        }

        sqlSession.close();
    }

总结:

动态·sql就是在拼接sql语句,我们只需要保证sql的正确性。按照sql的格式去排列组合就可以。

建议:

  • 先在mysql中写出完整的sql,再去对应修改成动态sql。

15.缓存


15.1简介

查询	: 连接数据库	,耗资源	,优化
一次查询结果,暂存在可以取到的资源-->内存	:	缓存
我们再次查询相同数据,直接走缓存,不用走数据库。

1.什么是缓存[Cache ]?
。存在内存中的临时数据。
。将用户经常查询的数据放在缓存(内存) 中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查
询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2.为什么使用缓存?
。减少和数据库的交互次数,减少系统开销,提高系统效率。

3.什么样的数据能使用缓存?
。经常查询并且不经常改变的数据。【可以使用缓存】

15.2、Mybatis缓存

●MyBatis包含一个非常强大的查询缓存特性, 它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效

●MyBatis系统中默认定义了两级缓存: 一级缓存二级缓存
默认情况下,只有一 级缓存开启。 (SqISession级别的缓存, 也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义 了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

15.3一级缓存


●- -级缓存也叫本地缓存: SqlSession
。与数据库同-次会话期间查询到的数据会放在本地缓存中。
。以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

测试步骤:

  1. 开启日志

        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        settings>
    
  2. 测试在一个Session中查询两次

       @Test
        public void te(){
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
    
            User user1 = mapper.queryUserById(2);
            System.out.println(user1);
    
            System.out.println("===================");
            User user2 = mapper.queryUserById(2);
            System.out.println(user2);
    
            System.out.println(user1==user2);
    
            sqlSession.close();
        }
    
  3. 查看日志输出

    Opening JDBC Connection
    Created connection 22805895.
    ==>  Preparing: select * from user WHERE id = ? 
    ==> Parameters: 2(Integer)
    <==    Columns: id, name, pwd
    <==        Row: 2, 小朋友, 333
    <==      Total: 1
    User(id=2, name=小朋友, pwd=333)
    ===================
    User(id=2, name=小朋友, pwd=333)
    true
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15bfd87]
    Returned connection 22805895 to pool.
    

缓存失效的情况:

  1. 查询不同东西

  2. 增删改操作,可能会改变原来的数据,必定会刷新缓存!

    Opening JDBC Connection
    Created connection 1475491159.
    ==>  Preparing: select * from user WHERE id = ? 
    ==> Parameters: 2(Integer)
    <==    Columns: id, name, pwd
    <==        Row: 2, 小朋友, 333
    <==      Total: 1
    User(id=2, name=小朋友, pwd=333)
    ==>  Preparing: update user set name = ?,pwd=? where id=?; 
    ==> Parameters: 测试(String), 23123(String), 3(Integer)
    <==    Updates: 1
    ===================
    ==>  Preparing: select * from user WHERE id = ? 
    ==> Parameters: 2(Integer)
    <==    Columns: id, name, pwd
    <==        Row: 2, 小朋友, 333
    <==      Total: 1
    User(id=2, name=小朋友, pwd=333)
    false
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@57f23557]
    Returned connection 1475491159 to pool.
    
  3. 查询不同的Mapper.xml

  4. 手动清除缓存

        @Test
        public void te(){
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
    
            User user1 = mapper.queryUserById(2);
            System.out.println(user1);
    
    //        mapper.updateUser(new User(3,"测试","23123"));
            //手动清理缓存
            sqlSession.clearCache();
    
            System.out.println("===================");
            User user2 = mapper.queryUserById(2);
            System.out.println(user2);
    
            System.out.println(user1==user2);
    
            sqlSession.close();
        }
    

小结:一级缓存默认开启,只在依次Session中有效!

15.4二级缓存


●二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

●基于namespace级别的缓存,-一个名称空间,对应一个二 _级缓存;

●工作机制
。-一个会话查询一 条数据,这个数据就会被放在当前会话的一级缓存中;
。如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,- -级缓存中的
数据被保存到二级缓存中;
。新的会话查询信息,就可以从二级缓存中获取内容;
。不同的mapper查出的数据会放在自己对应的缓存(map) 中;

步骤:

  1. 开启全局缓存

            <setting name="cacheEnabled" value="true"/>
    
  2. 在要使用二级缓存的Mapper中开启

        <cache/>
    

    也可以自定义参数

        <cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"
        />
    
  3. 测试

    1.问题:我们需要将实体类序列化!否则就会报错!
    Caused by: java. io. NotSerializableException: com. kuang. pojo.user

    Opening JDBC Connection
    Created connection 450003680.
    ==>  Preparing: select * from user WHERE id = ? 
    ==> Parameters: 2(Integer)
    <==    Columns: id, name, pwd
    <==        Row: 2, 小朋友, 333
    <==      Total: 1
    User(id=2, name=小朋友, pwd=333)
    ===================
    Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0
    User(id=2, name=小朋友, pwd=333)
    true
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1ad282e0]
    Returned connection 450003680 to pool.
    

    小结:

    • 只要开启二级缓存,在同一个Mapper.xml下有效!
    • 所有数据都会先放在一级缓存中
    • 只有当会话提交或者关闭,才会提交到二级缓存

15.5缓存原理图


MyBatis学习笔记整理详细_第5张图片

自定义缓存:

Ehcache是一种广泛使用的开源java分布式缓存。

要在程序中使用,先要导报到pom.xml文件中

       <dependency>
            <groupId>org.mybatis.cachesgroupId>
            <artifactId>mybatis-ehcacheartifactId>
            <version>1.1.0version>
        dependency>

在Mapper.xml中指定使用的cache

  <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

ehcache.xml配置文件


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
ehcache>

主流缓存:

redis等

小结

| 此笔记是博主在学习了狂神说教程后整理的,如有需要的小伙伴可私信博主,博主分享给你。另附上学习教程视频资源:

链接: https://www.bilibili.com/video/BV1NE411Q7Nx.
如果整理过程中博主有理解不对的地方,欢迎各位小伙伴留言指正,博主一定虚心接受并改正!
如果觉得博主的学习笔记对你有所帮助,可以给博主点一个赞

你可能感兴趣的:(学习笔记)