狂神说Mybatis学习笔记

文章目录

  • 零、Demo项目地址
  • 一、简介
    • 1-什么是MyBatis
    • 2-持久化
    • 3-持久层
    • 4-为什么需要Mybatis
  • 二、Mybatis的第一个程序
    • 1-搭建环境
    • 2-创建一个Model
    • 3-编写代码
    • 4-测试
  • 三、CRUD
    • 1-namespace
    • 2-select
    • 3-delete,update,insert标签
    • 4-万能的Map(扩展)
    • 5-模糊查询(扩展)
  • 四、配置解析(起飞)
    • 1-核心配置文件
    • 2-环境配置 environments
    • 3-属性 properties
    • 4-类型别名 typeAliases
    • 5-设置 Settings
    • 6-其他配置
    • 7-映射器
    • 8-生命周期和作用域
  • 五、解决属性名和字段名不一致的问题
    • 1-方法一:起别名
    • 2-方法二:resultMap
  • 六、日志
    • 1-日志工厂
    • 2-LOG4J
  • 七、分页
    • 2-RowBounds分页(了解)
    • 3-插件分页(了解)
  • 八、使用注解开发
    • 1-面向接口开发
    • 2-使用注解开发
    • 3-注解CURD
  • 九、Lombok(注释UP,懒狗板载)
  • 十、多对一的处理(重要)
    • 1-测试环境搭建
    • 2-按照查询嵌套处理
    • 3-按照结果嵌套处理(这个爽)
  • 十一、一对多处理
    • 1-环境搭建
    • 2-**按照结果嵌套嵌套处理(此乃好方法)**
    • 3-或者按照查询嵌套处理(有点绕)
  • 十二、动态SQL
    • 0-搭建环境
    • 1-IF
    • 2-choose (when, otherwise)
    • 3-trim
    • 4-where
    • 5-set
    • 6-SQL片段
    • 7-foreach
  • 十三、缓存(了解即可)
    • 1-简介
    • 2-MyBatis缓存
    • 3-一级缓存
    • 4-二级缓存
    • 5-缓存原理
    • 6-自定义缓存-ehcache
  • 十四、直接Rush到Spring

零、Demo项目地址

Demo项目地址:https://gitee.com/future-man_1_951770031/mybatis-study

一、简介

1-什么是MyBatis

  • MyBatis 是一款优秀的持久层框架
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。
  • MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
  • 2013年11月迁移到Github
  • Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
  • GitHub : https://github.com/mybatis/mybatis-3

2-持久化

持久化是将程序数据在持久状态和瞬时状态间转换的机制。

  • 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。
  • 持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
  • JDBC就是一种持久化机制。文件IO也是一种持久化机制。
  • 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。

为什么需要持久化服务呢?那是由于内存本身的缺陷引起的

  • 内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
  • 内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。

3-持久层

什么是持久层?

  • 完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】
  • 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
  • 不过这里有一个字需要特别强调,也就是所谓的“层”。对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现.
  • 与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。【说白了就是用来操作数据库存在的!】

4-为什么需要Mybatis

  • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .

  • 传统的jdbc操作 , 有很多重复代码块 .太麻烦比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .

  • MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射

  • 所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别

  • MyBatis的优点

    • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
    • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
    • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
    • 提供xml标签,支持编写动态sql。
  • 最重要的一点,使用的人多!公司需要!

二、Mybatis的第一个程序

思路流程:搭建环境–>导入Mybatis—>编写代码—>测试

1-搭建环境

新建项目:

  1. 实验数据库创建
CREATE DATABASE `mybatis`;

USE `mybatis`;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');
  1. 创建一个普通的maven项目

  2. 删除src目录 (就可以把此工程当做父工程了,然后创建子工程)

  3. 导入maven相关依赖

<dependency>
   <groupId>org.mybatisgroupId>
   <artifactId>mybatisartifactId>
   <version>3.5.2version>
dependency>
<dependency>
   <groupId>mysqlgroupId>
   <artifactId>mysql-connector-javaartifactId>
   <version>5.1.47version>
dependency>
  1. 创建一个Module

2-创建一个Model

  • 编写MyBatis核心配置文件
  • 一般放在src下的main中的resources
  • 查看帮助文档

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.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            dataSource>
        environment>
    environments>
	
    
    <mappers>
        <mapper resource="com/peng/dao/UserMapper.xml"/>
    mappers>
configuration>
  • 编写MyBatis工具类

用于获取sqlSessionFactory,然后再获取sqlSession

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;

public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 有了sqlSessionFactory,就可以获取sqlSession实例
    // 获取SqlSession连接
    public static SqlSession getSession(){
        return sqlSessionFactory.openSession();
    }

}

3-编写代码

  • 实体类

在pojo文件下创建

public class User {

    private int id;  //id
    private String name;   //姓名
    private String pwd;   //密码
    //构造,有参,无参
    public User() {
    }
    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }
    //toString()
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
    //set/get
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}
  • Dao接口
public interface UserDao {
    public List<User> getUserList();
}
  • 接口实现类(由原来的UserDaoImpl转变为一个Mapper配置文件)

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.peng.dao.UserDao">
    
    
    <select id="getUserList" resultType="com.peng.dao.User">
        select * from USER
    select>
mapper>

4-测试

一般来说是在test里边这个绿色的java文件夹里边进行

注意点:经常会遇到如下错误

org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.

MapperRegistry是什么?

核心配置文件中注册mappers

<mappers>
    <mapper resource="com/peng/dao/UserMapper.xml"/>
mappers>

问题:java.lang.ExceptionInInitializerError

Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/peng/dao/UserMapper.xml


<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
        <resource>
            <directory>src/main/resourcesdirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
    resources>
build>

可能会遇到的问题:

  1. 配置文件没有注册
  2. 绑定接口错误
  3. 方法名不对
  4. 返回类型不对
  5. Maven导出资源问题

三、CRUD

1-namespace

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

现在的Dao就改成Mapper要习惯这个改变

2-select

选择,查询语句;

select标签中的属性介绍:

  • id:就是对应的namespace中的方法名;
  • resultType : Sql语句执行的返回值;
  • parameterType : 参数类型;

3-delete,update,insert标签

标签中的属性与select一致,这里直接扔代码,直接理解吧

User实体类中的数据

public interface UserMapper {
//    获取全部用户信息
    public List<User> getUserList();
//    获取指定用户的信息
    User getUserById(int id);
//    增加用户的信息
    int addUser(User user);
//    修改用户的信息
    int updateUser(User user);
//    删除一个用户信息
    int deleteUser(int id);
}

UserMapper.xml中的数据


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.peng.dao.UserMapper">
    <select id="getUserList" resultType="com.peng.pojo.User">
        select * from user
    select>
    <select id="getUserById" parameterType="int" resultType="com.peng.pojo.User">
        select * from user where id=#{id}
    select>
    
    
    
    <insert id="addUser" parameterType="com.peng.pojo.User">
        insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
    insert>
    <update id="updateUser" parameterType="com.peng.pojo.User">
        update user set name = #{name},pwd=#{pwd} where id=#{id}
    update>
    <delete id="deleteUser" parameterType="int">
        delete from user where id=#{id}
    delete>
mapper>

Test类中

package com.peng.dao;

import com.peng.pojo.User;
import com.peng.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
public class UserDaoTest {
    @Test
    public void test(){
        //1.获取SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //2.执行SQL
        // 方式一:getMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭sqlSession
        sqlSession.close();
    }
    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //        指定查询用户的信息
        User user=userMapper.getUserById(1);
        System.out.println(user);
        sqlSession.close();
    }
    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user=new User(4,"123","321");
        mapper.addUser(user);
		//增删改必须要commit这个东西,提交事务
        sqlSession.commit();
        sqlSession.close();
    }
    @Test
    public void updateUser(){
        SqlSession sqlSession=MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user=new User(1,"333","333");
        mapper.updateUser(user);
        //增删改必须要commit这个东西,提交事务
        sqlSession.commit();
        sqlSession.close();
    }
    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUser(1);
        //增删改必须要commit这个东西,提交事务
        sqlSession.commit();
        sqlSession.close();
    }
}

4-万能的Map(扩展)

为什么用Map。假设实体类中的字段太多,我们就可以使用map,如果ParameterType

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

之前的代码都是使用User实体类去进行操作,每次都传一个实体类去操作都会添加很繁琐

而且xml中要对应实体类

但是使用map就不一样了,此乃野路子

UserMapper接口中

//用万能Map插入用户
public void addUser2(Map<String,Object> map);

UserMapper.xml中


<insert id="addUser2" parameterType="map">
    insert into user (id,name,password) values (#{userid},#{username},#{userpassword})
insert>

Test类中

@Test
public void test3(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("userid",4);
    map.put("username","王虎");
    map.put("userpassword",789);
    mapper.addUser2(map); 
    //提交事务
    sqlSession.commit();
    //关闭资源
    sqlSession.close();
}

重要:

Map传递参数,直接在sql中取出key即可! 【parameter=“map”】

对象传递参数,直接在sql中取出对象的属性即可! 【parameter=“Object”】

只有一个基本类型参数的情况下,可以直接在sql中取到(即在标签中的parameterType不填,可以直接函数带参传入)

多个参数用Map , 或者注解!

5-模糊查询(扩展)

模糊查询这么写?

方式一:Java代码执行的时候,传递通配符% %,比较安全

List<User> userList = mapper.getUserLike("%李%");

方式二:在sql拼接中使用通配符(xml中的东西),(存在SQL注入问题)

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

四、配置解析(起飞)

1-核心配置文件

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

2-环境配置 environments

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

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

MyBatis默认的事务管理器就是JDBC ,连接池:POOLED

<transactionManager type="JDBC"/>
            <dataSource type="POOLED">

dataSource的取值有很多种,其中POOLED使用较多。

POOLED-这种数据源的实现利用”池的概念将JDBC连接对象组织起来,避免了创建新的连接实例时所必需的初始化认证时间。这是一种使得并发Web应用快速响应请求的流行处理方式。

3-属性 properties

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

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

创建db.poperties放在resources下,内部内容如下:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=root

在核心配置文件(db.properties)中引入,然后使用**${内容}**的格式进行导入


    <properties resource="db.properties"/>

    <environments default="development">
        <environment id="development">
			
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            dataSource>
        environment>
    environments>
  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置
  • 如果两个文件有同一个字段,优先使用外部配置文件的

4-类型别名 typeAliases

  • 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置.

  • 意在降低冗余的全限定类名书写

  • 在resultType和parameterType这里可以使用别名

    1. 使用标签指定一个实体类起别名
    
    <typeAliases>
        <typeAlias type="com.kuang.pojo.User" alias="User"/>
    typeAliases>
    
    1. 指定一个包起别名,将里面的实体类全部起别名

    也可以指定一个包,每一个在包 com.peng.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 com.peng.pojo.User 的别名为 user别名为其注解值。见下面的例子:

    <typeAliases>
        <package name="com.peng.pojo"/>
    typeAliases>
    

    以后在实体类的mapper.xml中就可以直接使用别名了,而不是写一大串

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

    如果实体类十分多,建议用第二种扫描包的方式。

    第一种可以DIY别名,第二种不行,如果非要改,需要在实体上增加注解。

    1. 使用注解对一个类起别名
    @Alias("fuck")
    //别名就是fuck
    public class Author {
        ...
    }
    

    5-设置 Settings

    有很多,目前只记住这一个

    image-20210311090424974

image-20210311090454684

在核心配置文件中写入:

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

6-其他配置

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins 插件(github上去找)
    • mybatis-generator-core:可以根据数据库的名字,自动生成一个sql完成增删改查
    • mybatis-plus:使用之后可以使得mybatis效率翻倍
    • 通用mapper

7-映射器

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

注意之前我们在mybatis-config核心配置文件中注册的

方式一:【推荐使用】


<mappers>
    <mapper resource="com/peng/dao/UserMapper.xml"/>
mappers>

方式二:使用class文件绑定注册


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

方式二注意点:

  • 接口和他的Mapper配置文件必须同名,如UserMapper接口和UserMapper.xml
  • 接口和他的Mapper配置文件必须在同一个包下

方式三:使用包扫描进行注入

<mappers>
    <package name="com.peng.dao"/>
mappers>

方式三注意点:

  • 接口和他的Mapper配置文件必须同名,如UserMapper接口和UserMapper.xml
  • 接口和他的Mapper配置文件必须在同一个包下
  • 保证UserMapper接口和UserMapper.XML改为一致!并且放在同一个包下

8-生命周期和作用域

声明周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  • 一旦创建了SqlSessionFactory,就不再需要它了
  • 所以一般写在局部变量

SqlSessionFactory:

  • 说白了就可以想象为:数据库连接池
  • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建一个实例
  • 因此SqlSessionFactory的最佳作用域是应用作用域(ApplocationContext)
  • 最简单的就是使用单例模式或静态单例模式。

SqlSession:

  • 连接到连接池的一个请求

  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

  • 用完之后需要赶紧关闭,否则资源被占用!

    狂神说Mybatis学习笔记_第1张图片

这里的每一个Mapper就代表一个具体的业务

五、解决属性名和字段名不一致的问题

1-方法一:起别名

实体类与mapping.xml的属性不一致

数据库中的字段

狂神说Mybatis学习笔记_第2张图片

新建一个项目,拷贝之前的,测试实体类字段不一致的情况

狂神说Mybatis学习笔记_第3张图片

出现问题,出现null的情况

狂神说Mybatis学习笔记_第4张图片

// select * from user where id = #{id}
// 类型处理器
// select id,name,pwd from user where id = #{id}

解决方法:

  • 起别名,在SQL中,pwd as password
<select id="getUserById" resultType="com.kuang.pojo.User">
    select id,name,pwd as password from USER where id = #{id}
select>

2-方法二:resultMap

结果集映射,此乃解决方法二

id name pwd

id name password


<resultMap id="UserMap" type="User">
    
    <result column="id" property="id">result>
    <result column="name" property="name">result>
    <result column="pwd" property="password">result>
resultMap>
<select id="getUserList" resultMap="UserMap">
   
    select * from USER
select>
  • resultMap元素是 MyBatis 中最重要最强大的元素。
  • ResultMap的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • ResultMap的优秀之处——你完全可以不用显式地配置它们。
  • 如果这个世界总是这么简单就好了。

六、日志

1-日志工厂

如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手!

曾经:sout、debug

现在:日志工厂

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

在MyBatis中具体使用哪一个日志实现,在设置中设定

image-20210311155827579

STDOUT_LOGGING

在核心配置文件中设置:

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

狂神说Mybatis学习笔记_第5张图片

2-LOG4J

什么是Log4j?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件;
  • 我们也可以控制每一条日志的输出格式;
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程;
  • 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
  1. 先导入log4j的包
<dependency>
    <groupId>log4jgroupId>
    <artifactId>log4jartifactId>
    <version>1.2.17version>
dependency>
  1. 创建log4j.properties文件

在resources文件夹下面创建log4j.properties,直接百度搜配置也行,这里列出部分

#将等级为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/rzp.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.sq1.PreparedStatement=DEBUG
  1. 配置settings为log4j实现

  2. 测试运行

Log4j简单使用

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

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

Logger logger = Logger.getLogger(UserDaoTest.class);
  1. 日志级别
logger.info("info:测试log4j");
logger.debug("debug:测试log4j");
logger.error("error:测试log4j");

完整代码:

//参数为当前类的class对象
public class UserDaoTest {
    static Logger logger = Logger.getLogger(UserDaoTest.class);
    @Test
    public void testLog4j(){
        logger.info("info:进入了testLog4j");
        logger.debug("debug:进入了testLog4j");
        logger.error("error:进入了testLog4j");
    }
}

七、分页

思考:为什么分页?

  • 减少数据的处理量
语法:SELECT * from user limit startIndex,pageSize 
startIndex表示从第几个开始查
pageSize表示每页显示几个,
如果为:SELECT * from user limit 3表示查三条

使用MyBatis实现分页,核心SQL

  1. 接口
//分页
List<User> getUserByLimit(Map<String,Integer> map);
  1. Mapper.xml

<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
    select * from user limit #{startIndex},#{pageSize}
select>
  1. 测试
@Test
public void getUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",1);
    map.put("pageSize",2);
    List<User> list = mapper.getUserByLimit(map);
    for (User user : list) {
        System.out.println(user);
    }
}

2-RowBounds分页(了解)

不再使用SQL实现分页

  1. 接口
//分页2
List<User> getUserByRowBounds();
  1. mapper.xml

<select id="getUserByRowBounds">
    select * from user limit #{startIndex},#{pageSize}
select>
  1. 测试
public void getUserByRowBounds(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //RowBounds实现
    RowBounds rowBounds = new RowBounds(1, 2);
    //通过Java代码层面实现分页
    List<User> userList = sqlSession.selectList("com.kaung.dao.UserMapper.getUserByRowBounds", null, rowBounds);
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

3-插件分页(了解)

狂神说Mybatis学习笔记_第6张图片

八、使用注解开发

1-面向接口开发

三个面向区别

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

2-使用注解开发

简单的可以使用,复杂的就别去用了

  1. 注解在接口上实现
//直接将sql写在方法这里,干掉了Mapper.xml里的简单SQL语句,但是还是用得少
@Select("select * from user")
List<User> getUsers();
  1. 需要在核心配置文件中绑定接口
<mappers>
    <mapper class="com.kuang.dao.UserMapper"/>
mappers>
  1. 测试

    本质:反射机制实现

    底层:动态代理

MyBatis详细执行流程

狂神说Mybatis学习笔记_第7张图片

3-注解CURD

注意:openSession()方法可以设置参数,当参数为true的时候可以自动提交事务,此时可以不用再去写sqlSession.commit();

public static SqlSession getSqlSession(){
    return sqlSessionFactory.openSession(true);
}
  • 需要在核心配置文件中去注册(UserMapper接口)
//方法存在多个参数,所有的参数前面必须加上@Param("id")注解
//@Param里uid的与注解里的uid联系
@Delete("delete from user where id = ${uid} and name=${name}")
int deleteUser(@Param("uid") int id,@Param("name") String name);

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

@Update("update user set name=#{name} , pwd=#{password} where id = #{id}")
int updateUser(User user);

@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);

关于@Param( )注解

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以忽略,但是建议大家都加上
  • 我们在SQL中引用的就是我们这里的@Param()中设定的属性名

**#{} 和 ${}**是有区别的

一般使用#{}可以最大程度防止sql注入,能用#{}尽量用

  • #{}方式能够很大程度防止sql注入。
  • $方式无法防止Sql注入。
  • $方式一般用于传入数据库对象,例如传入表名.
  • 一般能用#就用$.

九、Lombok(注释UP,懒狗板载)

Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。

使用步骤:

  1. 在IDEA中安装Lombok插件

    狂神说Mybatis学习笔记_第8张图片

  2. 在项目中导入lombok的jar包

<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <version>1.18.10version>
    <scope>providedscope>
dependency>
  1. 加入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
@val

常用的注解:

  • @data使用最多,使用@data时,会自动的生成无参构造、get、set、toString、hashcode、equals方法,无需再在实体类中重写一大堆

  • @AllArgsConstructor生成有参构造

  • @NoArgsConstructor生成无参构造

4.在实体类中加注解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}

十、多对一的处理(重要)

多个学生一个老师;

对于学生这边而言,称之为关联,多个学生关联一个老师【多对一】,每个Student类里边有一个Teacher类作为属性

对于老师而言,称之为集合,一个老师有很多学生【一对多】,Teacher类里边有一个List的集合

alter table student ADD CONSTRAINT fk_tid foreign key (tid) references teacher(id)

狂神说Mybatis学习笔记_第9张图片

物理外键进行连接,通过tid关联老师id

1-测试环境搭建

  1. 导入lombok
  2. 新建实体类Teacher,Student

Student类:

@Data
public class Student {

    private int id;
    private String name;
    //学生需要关联一个老师!
    
    private Teacher teacher;

}

Teacher类:

@Data
public class Teacher {
    private int id;
    private String name;
}
  1. 建立Mapper接口

  2. 建立Mapper.xml文件

  3. 在核心配置文件中绑定注册我们的Mapper接口或者文件 【方式很多,随心选】

  4. 测试查询是否能够成功

2-按照查询嵌套处理

就是连表查,其实要写sql很快就能解决,但是现在使用框架所以需要按照框架的结构来,要改变思路,也相当于是一种解耦的妥协吧

子查询就是类似于下方,使用查询出的东西再作为条件,再查询:

select id,name,tid from student where tid=(select id from …)


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

<resultMap id="StudentTeacher" type="student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    
    
    
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
resultMap>

<select id="getTeacher" resultType="Teacher">
    select * from teacher where id=#{tid}
select>

3-按照结果嵌套处理(这个爽)

即sql查出来之后我们在xml中去处理

联表查询:直接查询两个表


<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid,s.name sname,t.name tname
    from student s,teacher t
    where s.tid=t.id
select>

<resultMap id="StudentTeacher2" type="Student">
    
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    
    <association property="teacher" javaType="teacher">
        <result property="name" column="tname"/>
    association>
resultMap>

回顾Mysql多对一查询方式:

  • 子查询 (按照查询嵌套)
  • 联表查询 (按照结果嵌套)

十一、一对多处理

一个老师多个学生;

对于老师而言,就是一对多的关系;

1-环境搭建

实体类

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}
@Data
public class Teacher {
    private int id;
    private String name;

    //一个老师拥有多个学生
    private List<Student> students;
}

注:Student里边的tid与老师的id对应

2-按照结果嵌套嵌套处理(此乃好方法)


<select id="getTeacher" resultMap="StudentTeacher">
    SELECT s.id sid, s.name sname,t.name tname,t.id tid FROM student s, teacher t
    WHERE s.tid = t.id AND tid = #{tid}
select>
<resultMap id="StudentTeacher" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    
    <collection property="students" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    collection>
resultMap>

3-或者按照查询嵌套处理(有点绕)

<select id="getTeacher2" resultMap="TeacherStudent2">
    select * from teacher where id=#{tid}
select>
<resultMap id="TeacherStudent2" type="Teacher" >
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    
    <collection property="students" javaType="ArrayList" ofType="student"
                select="getStudentByTeacherId" column="id">
    collection>
resultMap>
<select id="getStudentByTeacherId" resultType="student">
    select * from student where tid=#{tid}
select>

小结:

  1. 关联 - association 【多对一】
  2. 集合 - collection 【一对多】
  3. javaType & ofType
    • JavaType:用来指定实体类中的类型,java代码中是啥类型
    • ofType:用来指定映射到List或者集合中的pojo类型,比如泛型中的约束类型

注意点:

  • 保证 SQL的可读性,尽量保证通俗易懂
  • 注意一对多和多对一,属性名和字段的问题
  • 如果问题不好排查错误,可以使用日志,建议使用Log4j

面试高频

  • Mysql引擎
  • InnoDB底层原理
  • 索引
  • 索引优化

十二、动态SQL

什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句

所谓的动态SQL,本质上还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

0-搭建环境

CREATE TABLE `mybatis`.`blog`  (
  `id` varchar(255) NOT NULL AUTO_INCREMENT COMMENT '博客id',
  `title` varchar(30) NOT NULL COMMENT '博客标题',
  `author` varchar(30) NOT NULL COMMENT '博客作者',
  `create_time` datetime(0) NOT NULL COMMENT '创建时间',
  `views` int(30) NOT NULL COMMENT '浏览量',
  PRIMARY KEY (`id`)
)

创建一个基础工程

  1. 导包
  2. 编写配置文件
  3. 编写实体类
@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;// 此处属性名和字段名不一致
    private int views;
}
  1. 编写实体类对应Mapper接口和Mapper.xml文件

1-IF

传入map操作,很nice

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

2-choose (when, otherwise)

类似于switch,满足一个就走了,同时满足多个也走一个。只会选择其中一个去实现

<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from mybatis.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>

3-trim

定制化,其实where和set标签的功能都是这样定制出来的

where:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
trim>

set

<trim prefix="SET" suffixOverrides=",">
  ...
trim>

4-where

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

where解决拼接SQL时where和and同时拼接的情况,如:

错误示例:select * from mybatis.blog where and title = #{title}

按照以前的老方法,拼接语句的有时候会出现where and遇到一块的情况,此时sql语句是有问题的。

此时where标签就会自动的进行操作,拼接的语句有or或者and就会自动去除。

在外边包一层where标签即可

<where>
    <if test="title != null">
        title = #{title}
    if>
    <if test="author != null">
        and author = #{author}
    if>
where>

5-set

与where类似,在我们写update语句的时候会有很多逗号,当末尾出现逗号的时候就会出错

update mybatis.blog set title = #{title},author = #{author},

此时set标签的作用就和where类似,如果末尾有逗号,会将末尾的逗号取消掉


    update mybatis.blog
    
        
            title = #{title},
        
        
            author = #{author}
        
    
    where id = #{id}

6-SQL片段

有的时候,我们可能会将一些功能的部分抽取出来,方便代码的复用!

  1. 使用SQL标签抽取公共部分可
<sql id="if-title-author">
    <if test="title!=null">
        title = #{title}
    if>
    <if test="author!=null">
        and author = #{author}
    if>
sql>
  1. 在需要使用的地方使用Include标签引用即可
<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog
    <where>
        
        <include refid="if-title-author">include>
    where>
select>

注意事项:

  • 最好基于单表来定义SQL片段,比如只查一个表的,就是SQL片段不要做太复杂的事情
  • 不要存在where标签

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

建议:

  • 先在Mysql中写出完整的SQL,再对应的去修改成我们的动态SQL实现通用即可

7-foreach

foreach元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item) 和索引(index) 变量。

它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        
        
        
        
        
        
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id = #{id}
        foreach>
        
        
    where>
select>
select * from user where 1=1 and
<foreach item="id" collection="ids open="(" separator="or" close=")">
       #{id}
 foreach>
      (id=1 or id=2 or id=3)

java代码:

@Test
    public void queryBlogForEach(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map = new HashMap();
        //我们在这里创建了一个名为ids的集合
		//创建一个ArrayList作为map中的key
        ArrayList<Integer> ids = new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);
        ids.add(3);

        map.put("ids",ids);
        List<Blog> blogs = mapper.queryBlogForeach(map);

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

        sqlSession.close();
    }

十三、缓存(了解即可)

1-简介

查询 : 连接数据库,耗资源

比如有一次查询的结果,给他暂存一个可以直接取到的地方! --> 内存,这个就叫缓存

我们再次查询的相同数据的时候,直接走缓存,不走数据库了

  1. 什么是缓存[Cache]?

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

    • 减少和数据库的交互次数,减少系统开销,提高系统效率
  3. 什么样的数据可以使用缓存?

    • 经常查询并且不经常改变的数据 【可以使用缓存】

2-MyBatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,缓存可以极大的提高查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
  • 默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存。

3-一级缓存

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

在sqlSession范围内:在Close之前与创建之前的查询都会被缓存起来

测试步骤:

  1. 开启日志
  2. 测试在一个Session中查询两次相同记录
    @Test
    public void test1() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);

        System.out.println("=====================================");

        User user2 =  mapper.getUserById(1);
        System.out.println(user2 == user);
    }
  1. 查看日志输出

狂神说Mybatis学习笔记_第10张图片

缓存失效的情况:

  1. 查询不同的东西
  2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存
  3. 查询不同的Mapper.xml
  4. 手动清理缓存
sqlSession.clearCache();

4-二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
  • 工作机制:
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存(Sqlsession)中
    • 如果会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
    • 新的会话(新的sqlSession)查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查询出的数据会放在自己对应的缓存(map)中

一级缓存开启(SqlSession级别的缓存,也称为本地缓存)

  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存。

步骤:

  1. 开启全局缓存,在核心配置文件中(默认开启的,但是为了代码的可读性,还是写一下,提示一下)

    
    <setting name="cacheEnabled" value="true"/>
    
  2. 在Mapper.xml中使用缓存

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

    1. 问题:我们需要将实体类序列化,直接implement Serializable,否则就会报错

小结:

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

5-缓存原理

狂神说Mybatis学习笔记_第11张图片

此乃查询顺序。查询数据库后又将数据保存到以及缓存中

注意:

  • 只有查询才有缓存,根据数据是否需要缓存(修改是否频繁选择是否开启)useCache=“true”
<select id="getUserById" resultType="user" useCache="true">
    select * from user where id = #{id}
select>

6-自定义缓存-ehcache

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存

操作步骤:

  1. 导包
<dependency>
    <groupId>org.mybatis.cachesgroupId>
    <artifactId>mybatis-ehcacheartifactId>
    <version>1.2.1version>
dependency>
  1. 在mapper中指定使用我们的ehcache缓存实现
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  1. 然后在resources文件夹下创建一个xml文件进行配置ehcache的相关信息ehcache.xml,自己网上去找吧,这里示例一下:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">     
    
    <diskStore path="java.io.tmpdir"/>     
    
         
         
    <defaultCache     
            maxElementsInMemory="10000"    
            eternal="false"    
            timeToIdleSeconds="120"    
            timeToLiveSeconds="120"    
            overflowToDisk="true"    
            maxElementsOnDisk="10000000"    
            diskPersistent="false"    
            diskExpiryThreadIntervalSeconds="120"    
            memoryStoreEvictionPolicy="LRU"    
            />     
ehcache>    

这里了解即可,以后使用Redis来做缓存,以后缓存以什么key-value的形式之类的去实现

``xml

     

     
     
     
```

这里了解即可,以后使用Redis来做缓存,以后缓存以什么key-value的形式之类的去实现

十四、直接Rush到Spring

你可能感兴趣的:(java,开发语言,mybatis,数据库,mysql)