34——MyBatis

34——MyBatis

目录

全部内容【mybatis-Total】


环境:

  • JDK 1.8 实际-->java version "9.0.4"
  • MySql 5.7 实际-->VERSION() 5.7.19
  • Maven 3.6.1
  • IDEA 实际-->2019

回顾:

  • JDBC
  • MySql
  • Java基础
  • Maven
  • Junit

SSM框架:配置文件的。最好的方式:看官网文档

1、简介

mybatis-logo (1)

  • MyBatis 是一款优秀的持久层框架

  • 他支持定制化 SQL、存储过程以及高级映射

  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和Java 的 POJO (Plain Old Java Object, 普通老式 Java 对象) 为数据库中的记录。

  • MyBatis本是apache的一个开源项目 iBatis ,2010年这个项目由apache software foundation迁移到了 google code,并且改名为MyBatis。

  • 2013年11月迁移到 Github(全球最大的程序员交流社区)。

  • myBatis 中文帮助文档

获取MyBatis(多种渠道):

  1. Maven仓库mybatis 3.5.2
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
  1. Github 打不开 空着先

持久化

数据持久化

  • 持久化就是将程序的数据在瞬时状态转化成持久状态的过程
  • 内存: 断电即失
  • 就像生活中的食物我们如果不是马上吃的话要放入冰箱冷藏,就像数据如果不马上用的话,那要持久化放起来,以后用

为什么需要持久化

  1. 内存贵
  2. 因为断电即失,所以放在内存里是不安全,不科学的
  3. 因为有些数据不是马上就要用到,或者有些数据需要存储

持久层

dao 层 、Service 层、Controller 层.....

  • 完成持久化工作的代码块
  • 每一层的界限十分明显
  • 目的为了 加强 高内聚低耦合

传统 JDBCD代码复杂,开发效率低,MyBatis 可以帮助我们与数据库交互,它是一个开发框架自动化了一些原本需要我们人工编写的代码。

优点

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。

  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

  • 提供映射标签,支持对象与数据库的ORM字段关系映射。

    对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换 [1] 。从效果上说,它其实是创建了一个可在编程语言里使用的--“虚拟对象数据库”。
    
  • 提供对象关系映射标签,支持对象关系组建维护。

  • 提供xml标签,支持编写动态sql。

2、 第一个 MyBatis程序

myBatis 中文帮助文档

Result-MyBatis_Study.rar

2.1、搭建环境

【搭建数据库】

-- PostScript: 我们用了 `` 代表需要区分大小写
CREATE DATABASE `mybatis`; -- 创建数据库
USE `mybatis`; -- 切换到 mybatis 数据库下

CREATE TABLE `user` ( -- 创建表
`id` INT(20) NOT NULL PRIMARY KEY, --  不为空 主键
`name` VARCHAR(30) DEFAULT NULL, -- 默认 null
`pwd` VARCHAR(30) DEFAULT NULL
) ENGINE = INNODB DEFAULT CHARSET=utf8 -- 引擎 innodb 默认字符集 utf8

INSERT INTO  `user`(`id`,`name`,`pwd`) VALUES -- 插入数据
(1,'张三','123'),
(2,'李四','456'),
(3,'王五','789');

SELECT * FROM `user`;
  1. 新建普通的 Maven 项目 (如果项目名称带 - 注意需要手动修正)

  2. 查看 Maven 配置是否正确
  3. 创建后左下角的 enable Auto-import
  4. 删除 src 目录(把当前项目看做 父工程

  5. pom.xml 导入依赖

<!-- ============== 导入依赖 Start ==============-->
    <dependencies>
        <!--    MyBatis -->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--    Mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--  junit  -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
<!-- =============== 导入依赖 End ===============-->
  1. pom.xml配置标记配置文件
<!-- =============== 可以照搬的死代码 标记资源文件 自动打包导出 Start ===============-->
<!--父子项目时 要放在对应项目中 或者都放也行-->
<build>
    <resources>
        <!-- 标记资源文件 使其跟随项目打包出去 例如: 配置文件不打包到 Target 目录中 死代码 照搬就行-->
        <resource>
            <!-- resource 应该有默认配置 不需要写 但是java 文件下要写 才能生效-->
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>
<!-- =============== 可以照搬的死代码 标记资源文件 自动打包导出 End ===============-->

父工程完毕


2.2、 创建一个模块

  1. 创建 普通 maven 组件
  2. 创建和编写 mybatis 核心配置文件 mybatis-config.xml (idea先连接数据库)

mybatis-config.xml (不知道是不是这个 后来替换的)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://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?serverTimezone=GMT&amp;useUnicode=true&amp;useSSL=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="xxz123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/xiao/mapper/UserMapper.xml"/>
    </mappers>
</configuration>
  1. 编写 mybatis 工具类
package com.xiao.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 {
        // 使用官网代码  Mybatis 获取 sqlSessionFactory || 有其他方法 但是不推荐
        String resource = "mybatis-config.xml"; // 直接读取 resources 中的 xml 配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
    // 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = sqlSessionFactory.openSession(); // sqlSessionFactory.openSession(true); 设置自动提交
        return sqlSession;
    }
}

2.3 编写代码

Structure
#### 2.3.1 实体类

MyBatis在处理映射时,需要创建实体类的实例。由于MyBatis并不了解Java的构造函数细节,所以它只能调用实体类的无参构造函数来创建实例。如果实体类没有无参构造函数,MyBatis将无法实例化该类,从而导致运行时错误。

package com.xiao.pojo;

// 实体类
public class User {
    private  int 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;
    }

    public int getId(){
        return  id;
    }

    public String getName() {
        return name;
    }

    public String getPwd(){
        return pwd;
    }

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

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

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

2.3.2 Dao接口

package com.xiao.Mapper;

import com.xiao.pojo.User;
import java.util.List;

public interface UserMapper {
    List<User> getUserList();
}

2.3.3 接口实现类由原来的 USerDaoImpl 转变为 一个Mapper [UserMapper.xml]

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--    namespace(命名空间): 绑定一个对应的Dao/Mapper接口 用完全限定名(完整路径)   -->
<mapper namespace="com.xiao.mapper.UserMapper">
    <!-- select 查询语句
    实现语句:id: 对应实现接口的方法     getUserList
    结果集: resultType 返回一个  resultMap 返回多个 他是要提供完整路径的  com.xiao.pojo.User-->
    <select id="getUserList" resultType="com.xiao.pojo.User" >
        select * from mybatis.user
    </select>
</mapper>

2.4 Test

约定
package com.xiao.mapper;

import com.xiao.pojo.User;
import com.xiao.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession; // 改名前 的名字 ibatis
import org.junit.Test;

import java.util.List;

// 测试 最好在 Maven 测试文件夹中建立相对于的 路径测试 约定!
public class UserMapperTest {

    @Test
    public void  test(){
        // 第一步: 通过工具类获取SqlSession 对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        /*第二步 获取 UserDao
         * 方式一  getMapper()
         */
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 获取 class 对象
        List<User> userList = userMapper.getUserList();

        for (User user : userList) {
            System.out.println(user.toString());
        }
        
                /*
         方式二: 不推荐
         List<User> userList = sqlSession.selectList("com.xiao.Mapper.UserMapper.getUserList");
         */

        
        // 关闭资源 sqlSession
        sqlSession.close();

    }
    
}

Result

3、 CURD【mybatis-1】

完整CURD文件下载

在上文的环境下操作(部分不一致比如 包名大小写 注意一下即可 )

  1. (一)编写接口 UserMapper
  2. (二)编写实现配置 UserMapper.xml
  3. (三)Test

(一)编写接口 UserMapper

package com.xiao.mapper;

import com.xiao.pojo.User;

import java.util.List;
import java.util.Map;

public interface UserMapper {
    // ================= Read End==============
    // 1. 获取全部用户
    List<User> getUserList();
    // 2. 通过 id 获取用户 我们只要 编写 接口 和 他的 xml 实现即可
    User getUserById(int id);
    // 2.1 通过 模糊查询名字 获取用户 我们只要 编写 接口 和 他的 xml 实现即可
    List<User> getUserByLikeName(String value); // 使用 like 查询
    // ================= Read ==============
    // 3. 添加用户01
    int addUser(User user);

    // 4. 添加用户02 万能的Map
    int addUser2(Map<String,Object> map);

    // 5. 修改用户
    int updateUser(User user);

    // 6.删除用户
    int deleteUser(int id);
}

(二)编写实现配置 UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--    UTF-8 我运行会报错 xml 中的所有 encoding 要改成  UTF8-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--    namespace(命名空间): 绑定一个对应的Dao/Mapper接口 用完全限定名(完整路径)   -->
<mapper namespace="com.xiao.mapper.UserMapper">
<!--   1. UserMapper 接口 ———— 2. UserMapper.xml 实现 ———— 3. Test  -->
 <!-- ================= 1. select 查询语句 =================
       1.  查询所有用户
    实现语句:id 对应实现接口的方法     getUserList
    结果集: resultType 返回一个  resultMap 返回多个 他是要提供完整路径的  com.xiao.pojo.User-->
    <select id="getUserList" resultType="com.xiao.pojo.User" ><!--Idea 连数据库后 写库名 mybatis 如果没连就写表名即可 规定 (因为我们 Url xml 配置中已经写了 库名 但是写库名也没事)-->
        select * from mybatis.user
    </select>
<!--  2. 通过 id 获取用户-->
    <select id="getUserById" parameterType="int"   resultType="com.xiao.pojo.User" >
        select * from mybatis.user where id = #{id}
        <!--通过 #{} 来获取传进来的参数
        parameterType:传递的参数类型-->
    </select>
    <!--  2. 通过 名字 模糊查询获取用户-->
    <select id="getUserByLikeName"    resultType="com.xiao.pojo.User" > <!--当只有一个参数时 可以不写参数类型-->
        select * from mybatis.user where name like #{value}
        <!--通过 #{} 来获取传进来的参数
        parameterType:传递的参数类型-->
    </select>
<!--================= select 查询语句 =================-->

    <!-- 添加用户01 (对象中的属性 可以直接取出) -->
    <insert id="addUser" parameterType="com.xiao.pojo.User">
        insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>

    <!-- 添加用户01 (对象中的属性 可以直接取出) -->
    <insert id="addUser2" parameterType="map">
        insert into mybatis.user(id,name,pwd) values (#{userId},#{userName},#{userPwd})
    </insert><!--使用 map 的话
    1. 如果没有 userPwd 就为 null 非常灵活
    2. 命名可以随意只要两边对应 如果是用实体类 就必须用实体类的名称 (可定制化)-->

    <!-- 修改用户 -->
    <update id="updateUser" parameterType="com.xiao.pojo.User">
        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
    </update>

    <!-- 删除用户 #{id} 在单个参数时好像并不受约束 随便取名都行-->
    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id =#{id}
    </delete>



</mapper>

(三)Test

package com.xao.mapper;

import com.xiao.mapper.UserMapper;
import com.xiao.pojo.User;
import com.xiao.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class UserMapperTest {


    @Test// 获取全部用户
    public void test(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========

        // 调用方法
        List<User> userList = userMapper.getUserList();

        // 输出
        for (User user : userList) {
            System.out.println(user.toString());
        }

        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========
    }
    @Test// 通过 id 获取用户
    public void getUserById(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========
        // 调用方法
        User user = userMapper.getUserById(1);
        // 输出
            System.out.println(user.toString());

        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========
    }

    @Test// 通过 id 获取用户
    public void getUserByLikeName(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========
        // 调用方法
        String value ="李"; // 用户输入
        // 防止 sql 注入 方法一
        List<User> user = userMapper.getUserByLikeName("%"+value+"%");  // 需要防止 sql 注入
        // 防止 sql 注入 方法二 like '%' #{value}'%' 没成功 不演示了
        // 输出
        for (User user1 : user) {
            System.out.println(user1.toString());
        }


        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========
    }

    @Test// 添加用户01  增删改 需要提交事务
    public void addUser(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========
        // 调用方法

        int result = userMapper.addUser(new User (5,"王五","789"));
        // 输出
        System.out.println("受到影响的行数: "+result);

        // 提交事务
        sqlSession.commit();
        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========
    }

    @Test// 添加用户02 使用 万能的 Map  增删改 需要提交事务
    public void addUser2(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========
/* 使用 Map 的好处是 当实体类(表字段) 很长 但是你插入的内容却很少的时候,
  # 如果用实体类的话 需要把构造参数 填满 比如 new User(1,"张三","12346"); 因为我们构造方法是全参
  # Map 的话只要填写 你要插入的 键值对 即可 */
        Map<String,Object> map = new HashMap<>();
        map.put("userId", "100");
        map.put("userName", "项晓忠");
        //map.put("userPwd", "789456");比如我们可以不传密码给他 那么就 是null 如果是 new User 的话 全参 是一定要输密码的 这是他的优点
        // 调用方法
         int result = mapper.addUser2(map);

        // 输出
        System.out.println("受到影响的行数: "+result);

        // 提交事务
        sqlSession.commit();
        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========
    }
    @Test//修改用户
    public void updateUser(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========

        int result = userMapper.updateUser(new User(1, "爹爹", "88888888"));
        sqlSession.commit();
        System.out.println("受到影响的行数: "+result);
        
        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========
    }

    @Test// 删除用户
    public void deleteUser(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========

        int result = userMapper.deleteUser(100);
        sqlSession.commit();
        System.out.println("受到影响的行数: "+result);

        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========
    }

    
    

}

需要留意的点:

  1. 万能的Map (假设我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map!)
  2. 防止 SQL 注入

对象传递参数,直接在 Sql中取出对象的属性 【parameterType=“变量类型名称“】

Map传递参数直接在 Sql 中取出key 【parameterType=“map“】

如果只有一个基本类型参数,可以直接 在 sql 中取 不用设置参数类型

多个参数 用Map 或者注解


4、配置解析【mybatis-2】

配置解析【mybatis-2】

4.1、核心配置文件

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

4.2、环境配置 (environments)

MyBatis 可以配置成适应多种环境 ,但每个 SqlSessionFactory 实例只能选择一种环境

要学会配置使用多套运行环境

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development"><!--    环境可以编写多个,只要设置 default="test" 到你要的环境即可-->
        <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?serverTimezone=GMT&amp;useUnicode=true&amp;useSSL=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="xxz123456"/>
            </dataSource>
        </environment>
<!--        <environment id="test">-->
<!--            <transactionManager type="JDBC"></transactionManager>-->
<!--            <dataSource type="POOLED">-->
<!--            </dataSource>-->
<!--        </environment>-->
    </environments>
    <mappers>
        <mapper resource="com/xiao/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

4.3、属性 (properties)

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

这些属性哦都是可外部配置且可动态替换的, 既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递 【dn.properties】

编写配置文件文件【db.properties】

# 不能以 ; 结尾 # 注释单独一行
driver=com.mysql.jdbc.Driver
# 在properties 中不用 写&amp; 和"" 转义 在 mybatis .xml 中需要
url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useUnicode=true&useSSL=true&characterEncoding=utf8

在核心配置文件中映入 【 mybatis-3-config.xml 】

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties">
    <!--   这里也能写属性但是优先外部属性导入 -->
        <property name="username" value="root"/>
        <property name="password" value="xxz123456"/>
    </properties>

    <environments default="development"><!--    环境可以编写多个,只要设置 default="test" 到你要的环境即可-->
        <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>
    <mappers>
        <mapper resource="com/xiao/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

  • 可以直接引入外部文件

  • 可以在其中增加一些属性配置

  • 如果有两个文件有同一个字段,优先使用外部配置文件的

  • 注意 标签顺序

4.4、 类型别名 (typeAliases)

  • 类型别名是为了 Java 类型 设置一个短的名字,仅用来减少完全限定名的冗余

4.4.1 typeAliases-->typeAlias

mybatis-3-config.xml

<!-- 实体类 别名 -->
<typeAliases>
    <typeAlias type="com.xiao.pojo.User" alias="User"/>
</typeAliases>

UserMapper.xml

<select id="getUserList" resultType="User" ><!--Idea 连数据库后 写库名 mybatis 如果没连就写表名即可 规定 (因为我们 Url xml 配置中已经写了 库名 但是写库名也没事)-->
    select * from mybatis.user
</select>

4.4.2 typeAliases-->package

也可以指定一个包名,MyBatis会在包名下搜索需要的 java Bean(java类) ,比如:

mybatis-3-config.xml

<!-- 实体类 别名 -->
    <typeAliases>
<!--  第一种方法  typeAlias     <typeAlias type="com.xiao.pojo.User" alias="User"/>-->
<!--  第二种方法  package    扫描实体类的包, 他的默认别名就为这个类的类名  这样的话 官方建议 首字母小写 以区分是 通过包 导入的(大写也行)-->
        <package name="com.xiao.pojo"/>
</typeAliases>

通过 package 标签 扫描实体类的包, 他的默认别名就为这个类的类名 这样的话 官方建议 首字母小写 以区分是 通过包 导入的(大写也行)

UserMapper.xml

<select id="getUserList" resultType="user" ><!--Idea 连数据库后 写库名 mybatis 如果没连就写表名即可 规定 (因为我们 Url xml 配置中已经写了 库名 但是写库名也没事)-->
    select * from mybatis.user
</select>

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:

 @Alias("hello") // 扫描包的为 alias 取别名  resultType="hello"
public class User {

    private  int id ;
    private  String name;
    private  String pwd;
    ..................

在实体类比较少的时候,使用第一种方式,如果实体类比较多,建议使用第二种;

4.4.3 内置类型别名 (typeAliases)

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

加 _ 基本类型 不加 就是包装类

别名 映射的类型
_byte byte
_char (since 3.5.10) char
_character (since 3.5.10) char
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
char (since 3.5.10) Character
character (since 3.5.10) Character
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
biginteger BigInteger
object Object
date[] Date[]
decimal[] BigDecimal[]
bigdecimal[] BigDecimal[]
biginteger[] BigInteger[]
object[] Object[]
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

4.5、设置(settings)

设置(settings)

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

但是需要掌握的不多

  <setting name="cacheEnabled" value="true"/> <!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。-->
  <setting name="lazyLoadingEnabled" value="true"/> <!--延迟加载的全局开关-->
  <setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/> <!--加载日志-->

设置名 描述 有效值 默认值
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(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
proxyFactory 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 CGLIB (3.5.10 起废弃) | 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) 一个类型别名或完全限定类名。 未设置
shrinkWhitespacesInSql 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) true | false false
defaultSqlProviderType 指定一个拥有 provider 方法的 sql provider 类 (新增于 3.5.6). 这个类适用于指定 sql provider 注解上的type(或 value) 属性(当这些属性在注解中被忽略时)。 (e.g. @SelectProvider) 类型别名或者全限定名 未设置
nullableOnForEach 为 'foreach' 标签的 'nullable' 属性指定默认值。(新增于 3.5.9) true | false false
argNameBasedConstructorAutoMapping 当应用构造器自动映射时,参数名称被用来搜索要映射的列,而不再依赖列的顺序。(新增于 3.5.10) true | false false

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

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="aggressiveLazyLoading" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="safeResultHandlerEnabled" value="true"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
  <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
  <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
  <setting name="callSettersOnNulls" value="false"/>
  <setting name="returnInstanceForEmptyRow" value="false"/>
  <setting name="logPrefix" value="exampleLogPreFix_"/>
  <setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/>
  <setting name="proxyFactory" value="CGLIB | JAVASSIST"/>
  <setting name="vfsImpl" value="org.mybatis.example.YourselfVfsImpl"/>
  <setting name="useActualParamName" value="true"/>
  <setting name="configurationFactory" value="org.mybatis.example.ConfigurationFactory"/>
</settings>

4.6 其他配置

目前不需要了解

typeHandlers(类型处理器)

objectFactory(对象工厂)

plugins(插件)

​ 1. mybatis-generator-core : 能够帮你自动生成 mybatis 代码的插件,根据你数据库的名字搞定 CURD (但是会有问题)

​ 2. mybatis-plus (第三方插件,与mybatis 互补)

​ 3. 通用mapper


4.7 映射器 (mappers)

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

方式一: 使用 resources 绑定注册【推荐】

<!--每一个 Mapper.xml 都需要在 Mybatis 核心配置文件中注册!-->
<mappers>
    <mapper resource="com/xiao/mapper/UserMapper.xml"/>
</mappers>

可以放在任意位置

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

<!--每一个 Mapper.xml 都需要在 Mybatis 核心配置文件中注册!-->
<mappers>
    <!-- calss 可以用注解 让 map xml 同时生效	-->
        <mapper class="com.xiao.mapper.UserMapper"/>
</mappers>

【注意点】

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

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

<!--每一个 Mapper.xml 都需要在 Mybatis 核心配置文件中注册!-->
<mappers>
   <package name="com.xiao.mapper"/>
</mappers>

4.8 生命周期和作用域(lifecycle&Scope)

Mybatis工作流程

生命周期和作用域,错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  • 一旦创建了 SqlSessionFactory,就不在需要他了
  • 局部变量

SqlSessionFactory:

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

SqlSeesion:

  • 连接到连接池的一个求情
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以他的最佳作用域是请求或方法作用域
  • 用完之后需要赶紧关闭,否则资源被占用!
一个 SqlSession 可以生成多个 Mapper

5、解决属性名和字段名不一致的问题【mybatis-3】

解决属性名和字段名不一致的问题【mybatis-3】

5.1、复现问题

数据库中的字段

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

package com.xiao.pojo;
public class User {
    private  int id ;
    private  String name;
    private  String password;

//    不用构造器就会出现 pwd 为 null 问题
//    public User(int id, String name, String password) {
//        this.id = id;
//        this.name = name;
//        this.password = password;
//    }
    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 getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

测试出现问题
--  select * from mybatis.user where id = #{id}
-- 类型处理器
--  select id,name,pwd from mybatis.user where id = #{id}

他是属性名和字段名对应才能拿到资源。 因为我们字段名和属性不一样 ,所以结果拿不到

  1. 解决方法 取字段取别名 select id,name,pwd as password from mybatis.user where id = #{id} (小孩子做的)

5.2、结果集映射(ResultMap)

结果集映射

id  name pwd // 字段名
id  name password// User 属性名
// 字段和属性名 对不上 我们要想办法对上 所以 推出了 ResultMap 

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiao.mapper.UserMapper">

<!-- 类型别名 (typeAliases) package  <package name="com.xiao.pojo"/> 所以这里  type="user"
    ResultMap
    resultMap="userMap"  对应  <resultMap id="userMap" type="user">
    通过结果集 映射来对应字段名
    -->
<resultMap id="userMap" type="user">
    <result column="id" property="id"/>
    <result column="pwd" property="password"/>
</resultMap>

<select id="getUserById" parameterType="int"   resultMap="userMap" >
<!--  select * from mybatis.user where id = #{id}
          类型处理器 -->
               select * from mybatis.user where id = #{id}
               <!--通过 #{} 来获取传进来的参数
               parameterType:传递的参数类型-->
    </select>
</mapper>
  • resultMap 元素是 MyBatis 中最重要最强大的元素
  • ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述他们的关系就行了
  • ResultMap 最优秀的地方在于,虽然你已经对他相当了解了,但是根本就不需要显式的用到他们(select * from user)
  • 如果时间总是这么简单就好了

6、 日志【mybatis-4】

解决属性名和字段名不一致的问题【mybatis-4】

6.1、 工厂日志

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

之前用 System.out.println() 配合 debug 排错

现在用:日志工厂! 这也是 设置(settings) 中的功能

logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置

需要掌握: LOG4J(3.5.9 起废弃) | LOG4J2 | STDOUT_LOGGING

在 Mybatis 中具体使用哪一个日志实现, 在mybatis 核心配置文件中的设置 设定即可!

    <settings> <!-- 配置日志 注意大小写 和空格 -->
        <!-- 标准的日志工厂实现 不需要依赖 直接用 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
STDOUT_LOGGING
### 6.2、 LOG4J 日志

什么是 LOG4J

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
  • 我们也可以控制每一条日志的输出格式;
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
  • 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
  1. 先导入 LOG4J 依赖
<dependencies>
    <!--log4j mybatis 日志工厂 https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
</dependencies>
  1. log4j.properties 文件
# properties 文件名: log4j.properties (不能错)
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/com.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.appender.file.append=false # 设置为false表示不追加

#日志输出级别
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

  1. 在 mybatis 核心配置文件中配置 log4j 为日志的实现
<settings> <!-- 配置日志 注意大小写 和空格 -->
        <setting name="logImpl" value="LOG4J"/> <!-- 需要相关依赖和 配置文件 log4j.properties -->
</settings>
  1. 测试
Result

6.2.1、 LOG4J 日志 简单使用

package com.xiao.mapper;

import com.xiao.pojo.User;
import com.xiao.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger; // 1. 在需要使用 Log4j 的类中 导入包  import org.apache.log4j.Logger; 需要依赖  注意不要  导错包 ! 	
import org.junit.Test;

public class UserMapperTest {
	// 2. 获取当前类的映射
    static  Logger logger = Logger.getLogger(UserMapperTest.class);
    @Test
    public void test(){
        // 3. 根据不同日志级别输出
        logger.info("info:进入了 test"); 	// 消息级别
        logger.debug("debug:进入了 test");	// debug 级别
        logger.error("error:进入了 test"); // error 级别
    }


}


7、分页【mybatis-5】

分页【mybatis-5】

7.1、使用 Limit 分页

select * from user limit  0,2 -- 开始下标,页面大小

使用mybatis分页 核心SQL

  1. 接口
// 分页
List<User> getUserByLimit(Map<String,Integer> map);
  1. Mapper.xml
<!--  分页 -->
 <select id="getUserByLimit" parameterType="map"   resultType="User" >
     select * from mybatis.user limit #{startIndex},#{pageSize}
 </select>
  1. 测试
    // 1. 在需要使用 Log4j 的类中 导入包  import org.apache.log4j.Logger;
    static  Logger logger = Logger.getLogger(UserMapperTest.class);// 获取当前类的映射

    // 分页实现01  Limit
    @Test
    public  void getUserByLimit(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========

        HashMap<String,Integer> map = new HashMap<>();
        map.put("startIndex",Integer.valueOf(0));
        map.put("pageSize",Integer.valueOf(2));
        List<User> userByLimit = userMapper.getUserByLimit(map);

        for (User user : userByLimit) {
            logger.info( user.toString());
        }

        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========

    }
Result

7.2、使用 RowBounds 分页【了解即可】

不使用 SQL 实现分页

  1. 接口
// 分页02
List<User> getUserByRowBounds();
  1. Mapper.xml
<!--  分页02 -->
<select id="getUserByRowBounds"    resultType="User" >
    select * from mybatis.user
</select>
  1. 测试
// 1. 在需要使用 Log4j 的类中 导入包  import org.apache.log4j.Logger;
static  Logger logger = Logger.getLogger(UserMapperTest.class);// 获取当前类的映射
    
@Test
public void getUserByRowBounds(){
    // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========
    RowBounds rowBounds = new RowBounds(1, 2);
    List<User> userList = sqlSession.selectList("com.xiao.mapper.UserMapper.getUserByRowBounds",null,rowBounds); // 包用 . 路径用 /
    for (User user : userList) {
        logger.info( user.toString());
    }

    // =============  固定尾 关闭资源 Start ===========
    sqlSession.close();
    // =============  固定尾 关闭资源 Start ===========
}
Result

7.3、MyBatis 分页插件 PageHelper 【了解即可】

MyBatis 分页插件 PageHelper 官网文档


8、 使用mybatis注解开发【mybatis-06】

使用注解开发【mybatis-06】


使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

选择何种方式来配置映射,以及是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松地在基于注解和 XML 的语句映射方式间自由移植和切换。

8.1、面向接口编程

  • 学过面向对象编程,也学习过接口,但在真正开发中,很多时候我们会选择面向接口编程

  • 根本原因: 解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好

  • 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;

  • 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各个模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是安装这种思想来编程。

【关于接口的理解】

  • 接口从更深层次的理解,应是定义(规范、约束)与实现(名实分离的原则)的分离

  • 接口本身反应了系统设计人员对系统的抽象理解

  • 接口应有两类:

    第一类是对一个个体的抽象,他可对应为一个抽象体(abstract class);

    第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);

  • 一个体可能有多个抽象面.抽象体和抽象面是有区别的。

【三个面向区别】

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

8.2、使用注解开发

  1. 注解在接口上实现
@Select("select * from user where id = #{id}")
List<User> getUsers(int id);
  1. 需要在核心配置文件中绑定接口
<!-- 绑定接口 -->
    <mappers>
        <mapper class="com.xiao.mapper.UserMapper"/>
    </mappers>
  1. Test
@Test
public  void test(){
    // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========
    List<User> userByLimit = userMapper.getUsers(1);
    for (User user : userByLimit) {
        logger.info( user.toString());
    }
    // =============  固定尾 关闭资源 Start ===========
    sqlSession.close();
    // =============  固定尾 关闭资源 Start ===========
}

8.3、 注解的CURD

  1. mybatis-3-config.xml 在 mybatis 核心配置文件中绑定接口
<!-- 绑定接口 -->
    <mappers>
        <mapper class="com.xiao.mapper.UserMapper"/>
    </mappers>
  1. 接口
package com.xiao.mapper;

import com.xiao.pojo.User;
import org.apache.ibatis.annotations.*;

import java.util.List;
import java.util.Map;

public interface UserMapper {

    // 1. 获取所有用户
    @Select("select * from user")
    List<User> getUsers();

    // 2. 通过id获取用户
    /* 方法存在多个参数(基本类型),所有的参数前面必须加上  @Param("id") 注解 || 以@Param 注解为主 如果不一致会报错找不到参数异常
           如果是 引用类型就不用比如 map User  */
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") int id);

    // 3. 添加用户
    @Insert("insert into  user(id,name,pwd) values(#{id},#{name},#{password})") // 因为我们User类是用 password 这里要跟随
    int addUser(User user);

    // 4. 修改用户
    @Update("update  user set name=#{name} ,pwd=#{password}  where id=#{id} ")
    int updateUser(User user);

    // 5. 删除用户
    @Delete("delete from user where id=#{uid} ")
    int deleteUser(@Param("uid") int id);

}
  1. 测试
package com.xiao.mapper;

import com.xiao.pojo.User;
import com.xiao.utils.MybatisUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;


public class UserMapperTest {
    // 1. 在需要使用 Log4j 的类中 导入包  import org.apache.log4j.Logger;
    static  Logger logger = Logger.getLogger(UserMapperTest.class);// 获取当前类的映射

    @Test
    public  void getUsers(){
        // ============= 固定头 获取 sqlSession 对象 并映射接口 Start ===========
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // ============= 固定头 获取 sqlSession 对象 并映射接口 End ===========

        /* 1. getUsers 获取所有的用户

        List<User> userByLimit = userMapper.getUsers();
        for (User user : userByLimit) {
            logger.info( user.toString());
        }
         2. 通过Id 获取用户

          User user = userMapper.getUserById(1);
        System.out.println(user.toString()); 
        
        3. 添加用户

        int result = userMapper.addUser(new User(100, "xxz", "123"));
        System.out.println("收到影响的行数: "+result);

        4. 修改用户

        int result = userMapper.updateUser(new User(100, "项晓忠", "qwer123"));
        System.out.println("收到影响的行数: "+result);

        5. 删除用户*/
        int result = userMapper.deleteUser(100);
        System.out.println("收到影响的行数: "+result);


        // 因为我们设置了 sqlSessionFactory.openSession(true); 自动提交
        // =============  固定尾 关闭资源 Start ===========
        sqlSession.close();
        // =============  固定尾 关闭资源 Start ===========
    }

}

【PS:我们必须要将注解接口注册到我们的核心配置文件中!】

关于@Param()注解

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

#{} 和 ${} 区别

{} 能最大程度上预防 Sql 注入

${} 是拼接 不能防止 sql 注入


9、Lombok【 mybatis-07】

lombok Demo【 mybatis-07】


【Lombok官网】


Lombok Demo Video
Project Lombok是一个java库,可以自动插入到您的编辑器和构建工具中,为您的java增添趣味。 再也不用编写另一个getter或equals方法了,有了一个注解,你的类就有了一个全功能的构建器,自动记录变量,等等。

【自己理解】

lombok 用来自动生成,get/set ,有参数,和全参构造,以及 toString 和 hashcode,euqals 等等,主要是用来减少代码量,减少一些重复的,经常用到的代码,他不改变你的设计开发结构。

网上说他不是一个好的插件,是因为: 是一个好的工具应该是,从设计结构上帮助人理解和开发的,比如开发模型,比如mybatis 他从 接口 到实现类,改变为 接口 到 xml 配置实现,极大的减少代码量的同时,改变了设计结构,让我们方便理解和开发,这就是一个优秀的工具。但是 lombok 只是帮助我们减少了 代码量,说白了 就是帮助我们偷懒,在整体上是没有变化的,所以不推荐使用。


【lombok 三部曲】

  1. 下载 安装 Lombok 插件
  2. 在项目中导入Lombok 的 jar 包

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.28</version>
        <scope>provided</scope>
    </dependency>
    
    
  3. 直接添加注解即可使用 有不同的作用域比如: 修饰JavaBean(类)、修饰属性、方法参数
    我们可以根据注解的功能和自己的具体实现来选择作用域

【基本注解:】

注解 功能
@ToString 修饰JavaBean,可以动态生成相应的ToString方法,
格式为:User(username=admin, password=123)<br /
@EqualsAndHashCode 修饰JavaBean,默认情况下,会使用所有非静态(non-static)和非瞬态(non-transient)属性来生成equals和hasCode
@Getter 可以修饰JavaBean,也可以修饰属性,生成相应属性的get()方法
@Setter 可以修饰JavaBean,也可以修饰属性,生成相应属性的set()方法
@NonNull 修饰方法参数,自动判断参数是否为null,为nulll则抛出NullPointerException
@NoArgsConstructor 修饰JavaBean,可以动态生成无参构造器方法
@RequiredArgsConstructor 修饰JavaBean,生成private构造方法,使用staticName选项生成指定名称的static方法,与@NonNull联用,指定那些属性是本方法参数
@AllArgsConstructor 修饰JavaBean,可以动态生成全参构造器方法
@Data 修饰JavaBean,集合了@ToString、@EqualsAndHashCode、@Getter/@Setter、@RequiredArgsConstructor的所有特性。
@Value 修饰JavaBean,和@Data类似,区别在于它会把所有成员变量默认定义为private final修饰,并且不会生成set方法
@Slf4j 修饰JavaBean,当项目中使用Slf4j日志架构的时候自动创建log日志实例
@Log4j 修饰JavaBean,当项目中使用Log4j日志架构的时候自动创建log日志实例,需要创建log4j.properties配置文件,推荐使用@Log4j2
@Log4j2 修饰JavaBean,当项目中使用Log4j日志架构的时候自动创建日志实例,不需要配置文件
@Synchronized 修饰方法,自动生成同步代码块锁
@Cleanup 修饰流变量,自动调用close()方法

【高级注解:】

注解 功能
@Accessors(chain=true) 修饰JavaBean,链式风格,在调用set方时,返回当前实例
@Builder 修饰JavaBean,构建者模式,不可与@NoArgsConstructor同时使用
@Delegate 修饰JavaBean,代理模式

10、多对一和一对多的处理【mybatis-08-09】

10.1、查询环境搭建


复杂查询环境搭建【mybatis-08】 ---> 实体类要加无参构造 @NoArgsConstructor ( 因为mybatis 需要无参构造)


多对一或一对多

  • 多个学生,对应一个老师
  • 对于学生这边而言,关联 --> 多个学生关联一个老师【多对一】
  • 对于老师这边而言,集合 --> 一个老师有多个学生关联【一对多】
  1. sql 环境搭建
USE mybatis;
-- 依次执行
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=utf8;

INSERT 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');

SELECT * FROM `teacher`;

SELECT * FROM `student`;

【测试环境搭建】

  1. 导入 lombok (可选可以不用)
  2. 新建实体类 Teacher,Student
  3. 建立Mapper 接口
  4. 建立 Mapper.XML 文件
  5. 在核心配置文件中绑定我们的 Mapper 接口或者文件 ! 【resources class 随心选】
  6. 测试查询是否成功

10.2、多对一的处理

10.2.1、按照查询嵌套处理

按照查询嵌套处理【mybatis-08】(此08非彼08)

Student 实体类 (多个学生对一个老师)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private  int id;
    private  String name;
    private  int tid;

    // 学生需要对应(关联)一个老师
    private Teacher teacher;
}

    <!--=========== 01按照查询嵌套处理 Start ==================================-->
    <!--
        思路:
              1. 查询所有学生信息
              2. 根据查询出来的学生的tid,寻找对应的老师 !
        -->
    <select id="getStudent01" resultMap="StudentTeacher01"><!--注意: resultType 要用完全限定名!!!-->
        select * from student
    </select>
    <resultMap id="StudentTeacher01" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!-- 复杂的属性,我们需要单独处理 对象:  association集合:  collection -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/><!--注意 我们的 tid 为 0 -->

    </resultMap>
    <select id="getTeacher" resultType="Teacher"><!--注意: 完全限定名!!!-->
        select * from teacher where id =#{id} <!-- 因为只有一个参数所以 这里的id可以不用 对应匹配 (最好养成习惯对应起来) -->
    </select>
    <!--=========== 01按照查询嵌套处理 End ==================================-->
Result

10.2.2、按照结果嵌套处理

按照结果嵌套处理【mybatis-08】(此08非彼08)

<!--=========== 02按照结果嵌套处理 Start ==================================-->
<!-- 就是按照查询的结果来处理 最后的数据   -->

<select id="getStudent02" resultMap="StudentTeacher02">
    select s.id sid,s.name sname,t.name tname from student s,teacher t
</select>

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

</resultMap>
<!--=========== 02按照结果嵌套处理 End ==================================-->
Result
---

Test

package com.xiao.mapper;

import com.xiao.pojo.Student;
import com.xiao.pojo.Teacher;
import com.xiao.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MyTest {
    @Test // 测试环境是否正常
    public void getTeacherById(){

        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
//            TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
//            Teacher teacherById = mapper.getTeacherById(1);
//            System.out.println(teacherById.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }


    }

    @Test // 测试 【按照查询嵌套处理】
    public void getStudent01(){
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
            // 按照查询嵌套处理  按照结果嵌套处理
            List<Student> studentList = mapper.getStudent01();
            for (Student student : studentList) {
                System.out.println(student.toString());
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }


    }

    @Test // 测试 【按照结果嵌套处理】
    public void getStudent02(){
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
            // 按照查询嵌套处理  按照结果嵌套处理
            List<Student> studentList = mapper.getStudent02();
            for (Student student : studentList) {
                System.out.println(student.toString());
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }


    }
}

10.3、一对多的处理【mybatis-09】

Demo【mybatis-09】

对于老师这边而言,集合 --> 一个老师有多个学生关联【一对多】

【查询环境修改】

【复制上篇环境 测试成功后继续】

修改 Student 实体类

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

修改 Teacher实体类

@Data
@AllArgsConstructor
public class Teacher {
    private int id;
    private String name;
    private List<Student> students;
}

10.3.1、按照查询嵌套处理

<!-- ===================== 按照查询嵌套处理 Start ============================= -->

<select id="getTeacherById02" resultMap="TeacherStudent02">
    select * from mybatis.teacher where id =#{tid}
</select>

<resultMap id="TeacherStudent02" type="com.xiao.pojo.Teacher">
    <!-- ========= 同名 可以省略不写 Start =========  -->
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!-- ========= 同名 可以省略不写 End =========  -->
    <collection property="students" javaType="ArrayList" ofType="com.xiao.pojo.Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
    select * from mybatis.student where tid =#{id}
</select>

<!-- ===================== 按照查询嵌套处理 Start ============================= -->
Result

10.3.2、按照结果嵌套处理

    <!-- ===================== 按照结果嵌套处理 Start ============================= -->
<!--    resultType resultMap 如果用错会导致 java.lang.NullPointerException 空指针异常 || 因为这里就是取每一个值 所以不用写 javaType -->
    <select id="getTeacherById" resultMap="TeacherStudent" parameterType="_int">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from mybatis.student s,mybatis.teacher t
        where s.tid=t.id and t.id=#{tid}
    </select>

    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
<!--        复杂的属性, 我们需要单独处理 :
            处理对象: association  处理集合: collection
            javaType="" 指定属性的类型
            集合中的泛型信息,我们使用 ofType 获取
            -->
        <!--    collection property="students" ofType="Student" 如果填写错误会导致 java.lang.NullPointerException 空指针异常 -->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
<!-- ===================== 按照结果嵌套处理 End ============================= -->
Result

Test (略同多对一)


【小节】

  1. 关联 - association 【多对一】
  2. 集合 - collection 【一对多】
  3. javaType & ofType
    1. JavaType 用来指定实体类中的属性的类型
    2. ofType 用来指定映射到List或者集合中的 pojo 类型,泛型中的约束类型 !

【注意点】

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

11、 动态 SQL【mybatis-10】

动态SQL就是根据不同的条件生成不同的SQL语句 (例如: 我们之前在 SMBMS 项目中做的 拼接SQL)

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

if
choose (when, otherwise)
trim (where, set)
foreach

11.1、搭建环境【mybatis-10】

动态SQL搭建环境【mybatis-10】

SQL 环境

CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8;

【创建一个基础工程】

  1. 导包(org.mybatis、mysql、junit)

  2. 编写配置文件(mybatis-config.xml)

  3. 编写实体类

    // Java 里面要用  Util 下的 Date 要不然创建不了时间
    import java.util.Date;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Blog {
        private  String id;
        private  String title;
        private  String author;
        private Date createTime; // sql 字段名: create_time 字段名 属性名 不一致
        private  int views;
    }
    
    
  4. 编写实体类对应的 Mapper 接口 和 Mapper.xml 文件

    • Mapper 接口

      package com.xiao.mapper;
      
      import com.xiao.pojo.Blog;
      
      import java.util.List;
      
      @SuppressWarnings("all")
      public interface BlogMapper {
      
          // 测试 环境是否正常
          List<Blog> getBlogs();
      
          // 条件博客
          int  addBlog(Blog blog);
      }
      
      
    • Mapper.xml

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <!--记得绑定 接口
      然后 这个记得注册在核心配置文件中-->
      <mapper namespace="com.xiao.mapper.BlogMapper">
      
      <!-- 测试 环境是否正常-->
      <select id="getBlogs" resultType="com.xiao.pojo.Blog">
          select * from mybatis.blog
      </select>
          <!-- 添加 blog
          因为这里是插入 要读取我们实体列的值 所以这里写实体类的 属性名 createTime
          -->
      <insert id="addBlog" parameterType="com.xiao.pojo.Blog">
          insert into mybatis.blog(id, title, author, create_time, views) values (#{id}, #{title}, #{author}, #{createTime},#{views});
      </insert>
      </mapper>
      
  5. Test

    package com.xiao.mapper;
    
    import com.xiao.pojo.Blog;
    import com.xiao.utils.IDUtils;
    import com.xiao.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.Date;
    import java.util.List;
    
    public class MyTest {
    
        @Test// 测试 环境是否正常
        public void getBlogs() {
            SqlSession sqlSession = null;
            try {
                sqlSession = MybatisUtils.getSqlSession();
                BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
                List<Blog> blogs = mapper.getBlogs();
                for (Blog blog : blogs) {
                    System.out.println(blog.toString());
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                sqlSession.close();
            }
    
        }
    
    
        @Test
        public void addBlog() {
            SqlSession sqlSession = null;
            try {
                sqlSession = MybatisUtils.getSqlSession();
                BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
                Blog blog = new Blog();
                blog.setId(IDUtils.getId());
                blog.setTitle("Mybatis");
                blog.setAuthor("狂神说");
                blog.setCreateTime(new Date());
                blog.setViews(9999);
    
                mapper.addBlog(blog);
    
                blog.setId(IDUtils.getId());
                blog.setTitle("Java");
                mapper.addBlog(blog);
    
                blog.setId(IDUtils.getId());
                blog.setTitle("Spring");
                mapper.addBlog(blog);
    
                blog.setId(IDUtils.getId());
                blog.setTitle("微服务");
                mapper.addBlog(blog);
                // 他这里是  用了一个对象 其中的 几个属性 不变 改变其中的几个属性 而已 所以我们可以看到多条数据
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                sqlSession.close();
            }
    
        }
    
    
    }
    
    
  6. Result

11.2、If

    <!-- ==【动态SQL】If Start == -->
    <select id="queryBlogByIf" parameterType="map" resultType="com.xiao.pojo.Blog">
        select * from mybatis.blog where 1=1
        <if test="title != null" > <!-- 测试时传入参数: map.put("title","%"+"Mybatis"+"%");-->
            and title like #{title}
        </if>
        <if test="author != null">
            and author like #{author}
        </if>
    </select>
    <!-- ==【动态SQL】If End== -->

if 会判断是否传入参数 如果有传入 则添加对应语句 否则不会添加

11.3、choose(when ,otherwise)

    <!-- ==【动态SQL】choose when otherwise where Start == -->
    <select id="queryBlogByChoose" parameterType="map" resultType="com.xiao.pojo.Blog">
        select * from mybatis.blog
        <where>
<!--  有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
when (满足的情况下)
otherwise (如果都不满足的情况下)
最后只会选择一种情况执行
-->
            <choose>
                <when test="title != null">
                    and title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views =#{views}
                </otherwise>
            </choose>
        </where>
    </select>
    <!-- ==【动态SQL】choose when otherwise where End == -->

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

11.4、trim(where,set)

  • Where
<!-- ==【动态SQL】If where Start == -->
<select id="queryBlogByIf" parameterType="map" resultType="com.xiao.pojo.Blog">
    select * from mybatis.blog <!-- where 1=1 -->
    <where>
        <!-- where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。
        而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。-->
        <if test="title != null" > <!-- 测试时传入参数: map.put("title","%"+"Mybatis"+"%");-->
            and title like #{title}
        </if>
        <if test="author != null">
            and author like #{author}
        </if>
    </where>
</select>
<!-- ==【动态SQL】If where End== -->

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

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
<!-- 存在元素则 前缀是 where  如果前缀不是 where 会替换成 where 否则为空
 存在元素则 后缀是 and 或者 or 如果不是会替换 如果不存在下一个元素 则为空-->
    ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

  • set
    <!-- ==【动态SQL】set  Start== -->
    <update id="updateBlog" parameterType="map">
            update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author},
        </if>
    </set>
    where    id = #{id}   
    </update>
    <!-- ==【动态SQL】set   End== -->

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

或者,你可以通过使用trim元素来达到同样的效果:

<trim prefix="SET" suffixOverrides=",">
<!-- 存在元素则 前缀是 set  如果前缀不是 set 会替换成 set 否则为空
 存在元素则 后缀是 ,  如果不是会替换 如果不存在下一个元素 则为空-->
  ...
</trim>

所谓动态SQL 实际还是一个 SQL,只是我们可以在SQL层面,去执行一个逻辑代码

11.5、SQL 片段

    <!-- ==【动态SQL】SQL片段   Start== -->
    <!-- 1. 声明SQL脚本 -->
<sql id="updateBlog02" >
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author},
        </if>
    </set>
    where    id = #{id}
</sql>
    <update id="updateBlog02" parameterType="map">
<include refid="updateBlog02"></include> <!-- 2. 引用脚本 -->
    </update>
    <!-- ==【动态SQL】SQL片段   End== -->

可以实现 SQL 脚本的复用

11.6、Foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

环境 修改 UUID的 id 修改为 1-4
    <!-- ==【动态SQL】Foreach   Start== -->
    <!--   select * from mybatis.blog where 1=1 and (id=1 or id = 2 or id = 3)
    我们用万能的Map 传递一个集合
    -->
<select id="queryBlogForeach" resultType="com.xiao.pojo.Blog">
    select * from mybatis.blog
    <where>
        <foreach collection="ids" item="MyId" open="(" close=")" separator="or">
<!--            collection : 要比遍历的集合
                item: 遍历出的每一项
                open: 以什么开头
                close: 以什么结尾
                separator: 分隔符是什么-->
            id=#{MyId} <!-- id = 每一项的item值-->
        </foreach>
    </where>

</select>
    <!-- ==【动态SQL】Foreach   End== -->

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

Test【mybatis-10】完整版

【mybatis-10】完整版

Test

package com.xiao.mapper;

import com.xiao.pojo.Blog;
import com.xiao.utils.IDUtils;
import com.xiao.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;

import java.util.*;

public class MyTest {
// ================ 测试 环境是否正常 Start ================
    @Test// 测试 环境是否正常 【查询所有Blog】
    public void getBlogs() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            List<Blog> blogs = mapper.getBlogs();
            for (Blog blog : blogs) {
                System.out.println(blog.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }

    }
// ================ 测试 环境是否正常 End ================
// ================ 添加 blog Start ================
    @Test// 添加所有 Log
    public void addBlog() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

            Blog blog = new Blog();
            blog.setId(IDUtils.getId());
            blog.setTitle("Mybatis");
            blog.setAuthor("狂神说");
            blog.setCreateTime(new Date());
            blog.setViews(9999);

            mapper.addBlog(blog);

            blog.setId(IDUtils.getId());
            blog.setTitle("Java");
            mapper.addBlog(blog);

            blog.setId(IDUtils.getId());
            blog.setTitle("Spring");
            mapper.addBlog(blog);

            blog.setId(IDUtils.getId());
            blog.setTitle("微服务");
            mapper.addBlog(blog);
            // 他这里是  用了一个对象 其中的 几个属性 不变 改变其中的几个属性 而已 所以我们可以看到多条数据
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }

    }
// ================ 添加 blog End ================
// ================ 删除所有 blog Start ==============
    @Test//删除所有 blog
    public  void deleteBlog(){
        SqlSession sqlSession =null;
        try {
            Logger logger = Logger.getLogger(BlogMapper.class);

            sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            int i = mapper.deleteBlog();
            logger.info("受影响的行数:    "+i);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }


    }
// ================ 删除所有 blog End ================


    // ==================【动态SQL】Start ==================
    @Test// 【动态SQL】IF 查询博客
    public void queryBlogByIf(){
         Logger logger = Logger.getLogger(BlogMapper.class);
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtils.getSqlSession();

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

            HashMap<String,String> map = new HashMap<String, String>();

            map.put("title","%"+"Mybatis"+"%");
            map.put("author","%"+"狂神说"+"%");
            List<Blog> blogs = mapper.queryBlogByIf(map);
            for (Blog blog : blogs) {
                logger.info(blog.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }


    }

    @Test// 【动态SQL】choose when otherwise where Start
    public void queryBlogByChoose(){
        Logger logger = Logger.getLogger(BlogMapper.class);
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            HashMap<String,String> map = new HashMap<String,String>();

            map.put("author","狂神说");
            map.put("Mybatis","9999");
            map.put("views","9999");
            List<Blog> blogs = mapper.queryBlogByChoose(map);
            for (Blog blog : blogs) {
                logger.info(blog.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
    }

    @Test// 【动态SQL】Set
    public void updateBlog(){
        SqlSession sqlSession=null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            HashMap<String,String> map = new HashMap<String, String>();
            map.put("title", "孙子兵法");
            map.put("author", "项晓忠");
            map.put("id", "b2ff271194be4f26bd09a4e266dad44c");
            mapper.updateBlog(map);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
    }
    @Test// 【动态SQL】 sql片段
    public void updateBlog02(){
        SqlSession sqlSession=null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            HashMap<String,String> map = new HashMap<String, String>();
            map.put("title", "孙子兵法");
            map.put("author", "项晓忠");
            map.put("id", "b2ff271194be4f26bd09a4e266dad44c");
            mapper.updateBlog02(map);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
    }

    @Test // Foreach
    public void queryBlogForeach(){
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            HashMap map = new HashMap();

            ArrayList<Integer> arrayList = new ArrayList();
            arrayList.add(1);
            arrayList.add(2);
            arrayList.add(3);

            map.put("ids", arrayList);
            List<Blog> blogs = mapper.queryBlogForeach(map);
            for (Blog blog : blogs) {
                System.out.println(blog.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }

    }

    // ==================【动态SQL】End ==================
}

12、缓存【了解】

12.1、简介

查询  : 连接数据库  耗资源 
		一次查询的结果,给他暂存到一个可以直接取到的地方! --> 内存 : 缓存
		我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
  1. 什么是缓存【cache】
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询提供查询效率,解决了高并发系统的性能问题
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率
  3. 什么样的数据能使用缓存
    • 经常查询并且不经常改变的数据

12.2、Mybatis缓存

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

我们可以指定一些语句 不参与缓存【一般不会这样做】

<!-- 查询所有用户【测试环境】-->
<select id="getTotalUser" resultType="com.xiao.pojo.User" parameterType="_int" flushCache="true">
    select * from mybatis.user
<where>

12.3、一级缓存【mybatis-11】

关于一级缓存【mybatis-11】


  • 一级缓存也叫本地缓存
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
    • 默认开启并关不了
    • 【一级缓存---作用域】 (从 getSqlSession() 到 sqlSession.close())
【在同一个 sqlSession下查询相同的内容(默认存在一级缓存)】

【缓存失效的情况】

相同的sqlSession下 查询不同的内容 不存在 一级缓存
在中间执行修改操作(增删改)后 哪怕不是修改查询对象,也会清除缓存 !!!
手动删除缓存也会导致 缓存失效 (sqlSession.clearCache())

12.4、二级缓存【mybatis-12】

关于二级缓存【mybatis-12】

12.4.1、二级缓存简介

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

默认情况下,只启用了本地的会话缓存(一级缓存),它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件(不是指核心配置文件)中添加一行:

<cache/>

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。

这些属性可以通过 cache 元素的属性来修改。比如:

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

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

默认的清除策略是 LRU。

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。


【使用步骤】

  1. 核心配置文件中打开缓存(虽然默认是开启的 但是我们显式的定义一下)

        <settings> <!-- 配置日志 注意大小写 和空格 -->
            <setting name="cacheEnabled" value="true"/> <!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。-->
        </settings>
    
  2. 在 xml 文件中配置

    • 一般配置
    <cache/>
    
    • 自定义配置
        <!--      缓存
        策略 FIFO 先进先出
        刷新间隔: 每隔 60 秒刷新,
        最多可以存储结果对象或列表的 512 个引用
        只读-->
        <cache
                eviction="FIFO"
                flushInterval="60000"
                size="512"
                readOnly="true"/> <!-- 开启二级缓存-->
    
在同一个 nameScape 中(同一个 xml 文件) 当关闭sqlSession 后会丢入到二级缓存中 !!!

【小节】

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

12.4.5、缓存原理
  • 用户查询顺序 【二级缓存 --> 一级缓存 -- > 数据库】

image-20231028184754413


12.5、自定义缓存 ehcache【mybatis-13】

烂尾版(没讲完 也没懂做了什么 先不用深究 并不重要)【mybatis-13】

Ehcache 是一种广泛使用的开源 Java 分布式缓存。 主要面向通用缓存
  1. 导入依赖
<!--    EhCache 二级缓存    -->
        <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
  1. 在 mapper.xml 中指定我们的ehcache 缓存实现

        <!-- Ehcache -->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    
  2. 编写xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false"><!--         alt+Enter 点击第一个选择 解决爆红问题-->
    <!--
       diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
       user.home – 用户主目录
       user.dir  – 用户当前工作目录
       java.io.tmpdir – 默认临时文件路径
     -->
    <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"/>
    <!--
       defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <!--
      name:缓存名称。
      maxElementsInMemory:缓存最大数目
      maxElementsOnDisk:硬盘最大缓存个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      overflowToDisk:是否保存到磁盘,当系统宕机时
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。
      memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
      FIFO,first in first out,这个是大家最熟的,先进先出。
      LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
      LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->

</ehcache>

练习题 将之前做的 SMBMS 项目中的 关于数据库操作的 全都改造一遍 !

SMBMS_Mybatis

需要注意的地方

  1. Java 和 Resources 下只要建立对应的目录 最后加载时就会加载到一起!!!
  2. mapper.xml 配置文件 参考核心配置文件 配置
  3. sql语句不高亮问题 (鼠标悬浮代码后选择 注入语言 选择对应语言即可)
  4. 读错从下往上读,(因为最下面的报错是最先报错的)

常见错误

  1. org.apache.ibatis.binding.BindingException: Type interface com.xiao.mapper.UserMapper is not known to the MapperRegistry.
<!--没有注册 Mapper src/main/resources/mybatis-config.xml
解决方案: 每一个 Mapper.xml 都需要在MyBatis核心配置文件中注册!!!
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
-->
  1. java.lang.ExceptionInInitializerError
	at com.xiao.mapper.UserMapperTest.test(UserMapperTest.java:16) ... 略
Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error building SqlSession.
### The error may exist in com/xiao/mapper/UserMapper.xml // 找不到 该资源
// 确实在 Target 包下没有这个 配置文件
// 解决方案: 参考 javaweb课程中 Maven 配置文件无法被导出或者生效的问题    
  1. 文件 中文乱码 在 Settings-->Editor-->file Encodings(Global/Project Encoding) 中设置为 UTF-8
    方案一 推荐
    方案二(如下) 不推荐
### Error building SqlSession.
### Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.  Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效。
  1. 增删改需要 提交事务

  2. 路径问题 . 和 \ 的问题 如果是路径或资源名 用 \ 如果是包类用 . resource 绑定mapper 是用路径

  3. xml中标签语句不要匹配错了 select 对应select、insert 对应 insert 要一 一对应

  4. 读取不到 xml 资源问题 在父子工程中的 pom.xml 中配置 pom.xml配置标记配置文件

  5. java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing 运行时找不到所需的类文件 1. 解决版本冲突
    2. 检查依赖


    检查依赖 删除冲突版本
  6. .log 日志不能打开 设置文件属性 .log 为 Text 类型 使 Idea 能够识别(再重启 Idea 试试)
  7. Module "mybatis" must not contain source root "E:\study\MyEmptyProject\mybatis\mybatis-05\src\test\java". The root already belongs to module "mybatis-05"(父模块下加载了子模块内容异常) 解决方法 删除父模块下的子模块内容
  8. xml不能以 注释开头 Error building SqlSession.
    The error may exist in com/xiao/mapper/TeacherMapper.java (best guess)
    Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper
    Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.
    Cause: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 6; 不允许有匹配 "[xX][mM][lL]" 的处理指令目标。

    xml不能以 注释开头
  9. mybatis 的实体类没有无参构造异常 ### SQL: select * from student
    ### Cause: java.lang.IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
    解决方法: 添加无参构造即可
  10. java.lang.NullPointerException(空指针异常) 解决方法: 在xml中 绑定 接口即可
    collection property="students" ofType="Student"
    resultType resultMap 如果用错会导致
    如果填写错误会导致 空指针异常
  11. org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: com.xiao.pojo.User (实体类没有序列化 解决方法: 实体类 实现 序列化接口即可 implements Serializable

  12. It's likely that neither a Result Type nor a Result Map was specified.(很可能既没有指定结果类型也没有指定结果映射 就是 xml 中没有指定 映射结果类型 resultType="com.xiao.pojo.Blog")

  13. Cannot instantiate test(s): java.lang.SecurityException: Prohibited package name: java.com.xiao.mapper
    (不能实例化 包名为 java.com.xiao.mapper)
    修改或删除报警内容 即可
  14. Could not find parameter map
    错把parameterType写成了parameterMap  正确格式parameterType="map"  
    
  15. no instance(s) of type variable(s) exist so that ProviderDao conforms to BaseDao 查绑定 1. 核心配置是否绑定 2. xml 中namespace 是否管理
  16. // 直接写到 类变量时,会报错 ,写完整是不会报错  在方法里可以getLogger() 暂记
    import org.apache.log4j.Logger; // 包不要导错
    Logger logger =  Logger.getLogger(BillDao.class);
    
  17. Caused by: java.lang.ClassNotFoundException: Cannot find class: ProviderDao(找不到类异常,整个项目都异常,它是运行时校验你的整个项目代码 所以和你运行那行代码没有联系)
  18. mybatis 配置文件网址爆红 mybatis 配置文件网址爆红 http://mybatis.org/dtd/mybatis-3-config.dtd
    问题
    解决方案
    file->Settings->Languages & Frameworks->Schemas and DTDs->下部分添加 url
  19. Illegal hex characters in escape (%) pattern - Error at index 0 in: "us" 是否把 & 写成了 %

更新项目结构

posted @ 2023-11-13 14:38  项晓忠  阅读(52)  评论(0)    收藏  举报