Mybatis学习笔记

环境:

  • JDK 1.8
  • Mysql 5.7(Mysql 8.0)
  • maven 3.6.1
  • IDEA

回顾:

  • JDBC
  • Mysql
  • Java基础
  • Maven
  • Junit

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


1、简介

Mybatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录

Mybatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了Google Code,并且改名为Mybatis。2013年11月迁移到Github

如何获得Mybatis

  • Maven仓库
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    
  • Github:https://github.com/mybatis/mybatis-3/releases
  • 中文文档:https://mybatis.org/mybatis-3/zh/index.html

持久化

  • 数据持久化:持久化就是将程序的数据在持久状态和瞬时状态转化的过程
    • 瞬时状态:类似于内存,断电即失
    • 持久状态:只要不删库,数据可以一直在
  • 数据库(JDBC),IO文件持久化
  • 生活:冷藏、罐头、…
  • 持久化的原因:有一些对象,不能让他丢掉(类似生活中的微信、支付宝余额,王者的点卷,…),主要原因:内存太贵了

持久层

  • Dao层、Service层、Controller层
    • 完成持久化工作的代码块
    • 层是界限十分明显

为什么需要Mybatis

  • 方便
  • 传统的JDBC代码太复杂了;因为简化,所以框架,自动化
  • 帮助程序猿将数据存入到数据库中
  • 不用Mybatis也可以。更容易上手

优点:

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

最重要的一点:使用的人多!!!


2、第一个Mybatis程序

思路:搭建环境 -> 导入Mybatis -> 编写代码 -> 测试

搭建环境

  • 搭建数据库
    • 建库:
      CREATE DATABASE 'mybatis';
      USE 'mybatis';
      
    • 建表:
      CREATE TABLE 'user'(
      	'id' INT(20) NOT NULL PRIMARY KEY,
      	'name' VARCHAR(30) DEFAULT NULL,
      	'pwd' VARCHAR(30) DEFAULT NULL
      )ENGINE=INNODB DEFAULT CHARSET=utf8;
      
    • 插入数据:
      INSERT INTO 'user' ('id', 'name', 'pwd') VALUES
      (1, 'root', '123456'),
      (2, 'huake', '123'),
      (3, 'zya', '123456')
      

数据库的几种引擎:

  • MyISAM:默认的MySQL插件式存储引擎,它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。注意,通过更改STORAGE_ENGINE配置变量,能够方便地更改MySQL服务器的默认存储引擎。
  • InnoDB:用于事务处理应用程序,具有众多特性,包括ACID事务支持。(提供行级锁)
  • BDB:可替代InnoDB的事务引擎,支持COMMIT、ROLLBACK和其他事务特性。
  • Memory:将所有数据保存在RAM中,在需要快速查找引用和其他类似数据的环境下,可提供极快的访问。
  • Merge:允许MySQL DBA或开发人员将一系列等同的MyISAM表以逻辑方式组合在一起,并作为1个对象引用它们。对于诸如数据仓储等VLDB环境十分适合。
  • Archive:为大量很少引用的历史、归档、或安全审计信息的存储和检索提供了完美的解决方案。
  • Federated:能够将多个分离的MySQL服务器链接起来,从多个物理服务器创建一个逻辑数据库。十分适合于分布式环境或数据集市环境。
  • Cluster/NDB:MySQL的簇式数据库引擎,尤其适合于具有高性能查找要求的应用程序,这类查找需求还要求具有最高的正常工作时间和可用性。
  • Other:其他存储引擎包括CSV(引用由逗号隔开的用作数据库表的文件),Blackhole(用于临时禁止对数据库的应用程序输入),以及Example引擎(可为快速创建定制的插件式存储引擎提供帮助)。

一般来说不使用事务的话,请使用MyISAM引擎,使用事务的话,一般使用InnoDB

新建项目:

  • 1、新建一个普通的maven项目
  • 2、删除src目录
  • 3、导入maven依赖
    <!-- 父工程 -->
    <groupId>org.example</groupId>
    <artifactId>Mybatis_demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <!-- 导入依赖 -->
    <dependencies>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
    
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    
        <!-- mybatis -->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
    </dependencies>
    

创建一个模块:

  • 编写mybatis的核心配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    
    <!-- configuration: 核心配置文件 -->
    <configuration>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&amp;serverTimezone=GMT%2B8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="zya11230318"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    
  • 编写mybatis的工具类

    package com.zya.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.*;
    import lombok.NoArgsConstructor;
    import org.springframework.beans.factory.annotation.Autowired;
    
    @AllArgsConstructor
    @NoArgsConstructor
    // 实体类
    @Data
    public class User {
    
        private int id;
        private String name;
        private String pwd;
    
    }
    
  • Dao接口

    package com.zya.dao;
    
    import com.zya.pojo.User;
    
    import java.util.List;
    
    public interface UserDao {
        List<User> getUserList();
    }
    
  • 接口实现类由原来的UserDaoImpl转变为一个Mapper配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- namespace: 绑定一个对应的Dao/Mapper接口 -->
    <mapper namespace="com.zya.dao.UserDao">
    
        <!-- select: 查询语句 -->
        <!-- 绑定接口方法 -->
        <select id="getUserList" resultType="com.zya.pojo.User">
            select * from mybatis.userone;
        </select>
    </mapper>
    
  • pom.xml

    <!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
    

测试
注意点:org.apache.ibatis.binding.BindingException: Type interface com.zya.dao.UserDao is not known to the MapperRegistry.

import com.zya.dao.UserDao;
import com.zya.pojo.User;
import com.zya.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MyTest {
    @Test
    public void test(){
        // 第一步: 获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        // 方式一: getMapper
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        List<User> userList = mapper.getUserList();

        // 方式二 不推荐使用
//        List<User> userList = sqlSession.selectList("com.zya.dao.UserDao.getUserList");


        for (User user:userList) {
            System.out.println(user);
        }
        // 关闭SqlSession
        sqlSession.close();
    }
}

MapperRegistry是什么?

  • 核心配置文件中注册mappers
  • junit测试

在这里插入图片描述

可能遇到的问题:

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

3、CRUD

1、namespace

  • namespace中的包名要和Dao/Mapper接口的包名一致
  • 选择、查询语句
  • id:就是对应的namespace中的方法名
  • resultType:Sql语句执行的返回值!
  • parameterType:参数类型

2、select、insert、update、delete

  • 编写接口

    package com.zya.dao;
    
    import com.zya.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        // 查询全部用户
        List<User> getUserList();
        // 根据id查询用户
        User getUserById(int id);
        // 插入用户
        int addUser(User user);
        // 修改用户
        int updateUser(User user);
        // 删除一个用户
        int deleteUser(int id);
    }
    
  • 编写对应的mapper中sql语句

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- namespace: 绑定一个对应的Dao/Mapper接口 -->
    <mapper namespace="com.zya.dao.UserMapper">
        <!-- select: 查询语句 -->
        <!-- 绑定接口方法 -->
        <select id="getUserList" resultType="com.zya.pojo.User">
            select * from mybatis.userone;
        </select>
        <select id="getUserById" resultType="com.zya.pojo.User" parameterType="int">
            select * from mybatis.userone where id=#{id};
        </select>
        <!-- 对象中的属性, 可以直接取出来 -->
        <insert id="addUser" parameterType="com.zya.pojo.User">
            insert into mybatis.userone (id, name, pwd) values (#{id}, #{name}, #{pwd})
        </insert>
        <update id="updateUser" parameterType="com.zya.pojo.User">
            update mybatis.userone set name=#{name},pwd=#{pwd} where id=#{id};
        </update>
        <delete id="deleteUser" parameterType="int">
            delete from mybatis.userone where id=#{id};
        </delete>
    </mapper>
    
  • 测试

    import com.zya.dao.UserMapper;
    import com.zya.pojo.User;
    import com.zya.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.List;
    
    public class MyTest {
        @Test
        public void test(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 方式一: getMapper
                List<User> userList = sqlSession.getMapper(UserMapper.class).getUserList();
                // 方式二:
    //        List<User> userList = sqlSession.selectList("com.zya.dao.UserMapper.getUserList");
                for (User user:userList) {
                    System.out.println(user);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    
        @Test
        public void test01(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 方式一: getMapper
                User user = sqlSession.getMapper(UserMapper.class).getUserById(2);
                System.out.println(user);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    
        // 增删改需要提交事务
        @Test
        public void test02(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 方式一: getMapper
                int res = sqlSession.getMapper(UserMapper.class).addUser(new User(5, "admin", "123456"));
                // 提价事务
                sqlSession.commit();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    
        @Test
        public void test03(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 方式一: getMapper
                int res = sqlSession.getMapper(UserMapper.class).updateUser(new User(5, "admin", "123"));
                // 提价事务
                sqlSession.commit();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    
        @Test
        public void test04(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 方式一: getMapper
                int res = sqlSession.getMapper(UserMapper.class).deleteUser(5);
                // 提价事务
                sqlSession.commit();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    }
    

注意点:增删改需要提交事务!!!


分析错误

  • 标签不要匹配错
  • resource绑定mapper,需要使用路径
  • 程序配置文件必须符合规范!
  • NullPointException,没有注册到资源!
  • 输出的xml文件中存在中文乱码问题
  • maven资源没有导出问题

万能Map

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

dao接口

public interface UserMapper {
    // 万能的Map
    int addUser2(Map<String, Object> map);
}

UserMapper.xml

<!--
    对象中的属性, 可以直接取出来
    传递map中的key
 -->
<insert id="addUser2" parameterType="map">
    insert into mybatis.userone (id, name, pwd) values (#{userId}, #{userName}, #{userPwd});
</insert>

Test.java

@Test
public void test05(){
    // 第一步: 获得SqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    try {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("userId", 5);
        map.put("userName", "admin");
        map.put("userPwd", "123456");
        // 方式一: getMapper
        int res = sqlSession.getMapper(UserMapper.class).addUser2(map);
        // 提价事务
        sqlSession.commit();
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        // 关闭SqlSession
        sqlSession.close();
    }
}
  • Map传递参数,直接在sql中取出key即可【parameterType=“map”】
  • 对象传递参数,直接在sql中取对象的属性即可【parameterType=“object”】
  • 只有一个基本类型参数的情况下,可以直接在sql中取到
  • 多个参数用Map,或者注解

模糊查询:

  • 1、Java代码执行的时候,传递通配符 % %

    List<User> user = sqlSession.getMapper(UserMapper.class).getUserLike("%w%");
    
  • 2、在sql拼接中使用通配符

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

4、配置解析

1、核心配置文件

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

2、环境配置(environments)

  • MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
  • 不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

注意一些关键点:

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

事务管理器(transactionManager):

  • 在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
    • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。(Mybatis默认的事务管理器是JDBC
    • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE
      应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为
      false 来阻止默认的关闭行为。

数据源(dataSource):(Mybatis默认的连接池是POOLED

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):

  • UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
    • driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)
    • url – 这是数据库的 JDBC URL 地址。
    • username – 登录数据库的用户名。
    • password – 登录数据库的密码。
    • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
    • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看java.sql.Connection#setNetworkTimeout() 的 API 文档以获取更多信息

作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:driver.encoding=UTF8 ,这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8 的 encoding 属性给数据库驱动。

  • POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
    • poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
    • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
    • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000
      毫秒(即 20 秒)
    • poolTimeToWait –这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000毫秒(即 20 秒)。
    • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置,作用于每一个尝试从缓存池获取连接的线程。如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance之和。 默认值:3(新增于 3.4.5)
    • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERYSET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
    • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL语句(最好是一个速度非常快的 SQL 语句),默认值:false。
    • poolPingConnectionsNotUsedFor – 配置 poolPingQuery
      的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
  • JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
    • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
    • data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:env.encoding=UTF8,这就会在 InitialContext 实例化时往它的构造方法传递值为 UTF8 的 encoding 属性。

3、属性(properties)

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

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

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>
  • 可以直接引入外部配置文件
  • 可以在其中增加一些属性配置
  • 如果两个文件有同一个字段,优先使用外部配置文件的

编写一个数据库配置文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=GMT%2B8
username=root
password=zya11230318

在xml中,所有的标签都可以规定其顺序

4、类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。
  • 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<!-- 可以给实体类起别名 -->
<typeAliases>
    <typeAlias type="com.zya.pojo.User" alias="User" />
</typeAliases>

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

  • 扫描实体类的包,它的默认别名就为这个类的类名,首字母小写
<!-- 可以给实体类起别名 -->
<typeAliases>
    <typeAlias type="com.zya.pojo.User" />
</typeAliases>

在实体类比较少的时候,使用第一种方式【可以DIY】

如果实体类十分多,建议使用第二种【不可DIY,如果非要改,需要在实体类上增加注解】

@Alias("User")
public class User {
	...
}

5、设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

6、其他配置

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

7、映射器(mappers)

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

既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等

  • 方式一:
    <!-- 使用相对于类路径的资源引用 -->
    <mappers>
      <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
      <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
      <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    </mappers>
    
  • 方式二:
    <!-- 使用完全限定资源定位符(URL) -->
    <mappers>
      <mapper url="file:///var/mappers/AuthorMapper.xml"/>
      <mapper url="file:///var/mappers/BlogMapper.xml"/>
      <mapper url="file:///var/mappers/PostMapper.xml"/>
    </mappers>
    
  • 方式三:使用class绑定注册(注意点如下)
    • 接口和它的Mapper配置文件必须同名
    • 接口和他的Mapper配置文件必须在同一个包下
    <!-- 使用映射器接口实现类的完全限定类名 -->
    <mappers>
      <mapper class="org.mybatis.builder.AuthorMapper"/>
      <mapper class="org.mybatis.builder.BlogMapper"/>
      <mapper class="org.mybatis.builder.PostMapper"/>
    </mappers>
    
  • 方式四:使用扫描包进行注入绑定(注意点如下)
    • 接口和它的Mapper配置文件必须同名
    • 接口和他的Mapper配置文件必须在同一个包下
    <!-- 将包内的映射器接口实现全部注册为映射器 -->
    <mappers>
      <package name="org.mybatis.builder"/>
    </mappers>
    

8、生命周期和作用域
在这里插入图片描述
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  • 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了
  • 局部变量

SqlSessionFactory:

  • 说白了就是:数据库连接池
  • 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • 多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。(会浪费内存资源)
  • 最简单的就是使用单例模式或者静态单例模式

SqlSession:

  • 每个线程都应该有它自己的 SqlSession 实例(连接到连接池的一个请求)
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 用完之后需要赶紧关闭,否则资源被占用(每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它)

在这里插入图片描述
这里面的每一个Mapper,就代表一个具体的业务!


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

数据库中的字段
在这里插入图片描述
新建一个项目,拷贝之前的,测试实体类字段不一致的情况

package com.zya.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.type.Alias;

@AllArgsConstructor
@NoArgsConstructor
// 实体类
@Alias("User")
@Data
public class User {
    private int id;
    private String name;
    private String password;
}

测试出现问题,找不到pwd

select id, name, pwd from mybatis.userone;

在这里插入图片描述
解决方法:

  • 起别名【select id, name, pwd as password from mybatis.userone;】
  • resultMap:结果集映射

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

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

没有一个需要显式配置 ResultMap,这就是 ResultMap 的优秀之处——你完全可以不用显式地配置它们

如果这个世界总是这么简单就好了

id, name, pwd	数据库字段
id, name, password	实体类
<!-- 结果集映射 -->
<resultMap id="UserMap" type="User">
    <!-- column: 数据库中的字段 -->
    <!-- property: 实体类中的字段 -->
    <result column="id" property="id" />
    <result column="name" property="name" />
    <result column="pwd" property="password" />
</resultMap>
<!-- select: 查询语句 -->
<!-- 绑定接口方法 -->
<select id="getUserList" resultMap="UserMap">
    select * from mybatis.userone;
</select>

在这里插入图片描述
在这里插入图片描述


6、日志

6.1、日志工厂

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

  • 曾经:sout、debug
  • 现在:日志工厂
    • SLF4J
    • Apache Commons Logging
    • Log4j 2
    • Log4j (deprecated since 3.5.9)【掌握】
    • JDK logging
    • COMMONS_LOGGING
    • STDOUT_LOGGING【掌握】
    • NO_LOGGING

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

  • STDOUT_LOGGING【标准日志输出】

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
    Opening JDBC Connection
    Created connection 954702563.
    Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38e79ae3]
    ==>  Preparing: select * from mybatis.userone;
    ==> Parameters: 
    <==    Columns: id, name, pwd
    <==        Row: 1, zya, 123456
    <==        Row: 2, huake, 123
    <==        Row: 3, wuda, 123456
    <==        Row: 4, world, 1233
    <==        Row: 5, admin, 123
    <==      Total: 5
    User(id=1, name=zya, password=123456)
    User(id=2, name=huake, password=123)
    User(id=3, name=wuda, password=123456)
    User(id=4, name=world, password=1233)
    User(id=5, name=admin, password=123)
    Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38e79ae3]
    Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38e79ae3]
    Returned connection 954702563 to pool.
    
    Process finished with exit code 0
    

6.2、LOG4J

log4j配置文件详解:https://blog.csdn.net/eagleuniversityeye/article/details/80582140

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码

1、先导入LOG4J的包

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

2、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.A1.Encoding=UTF-8
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/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3、配置log4j为日志的实现

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

4、log4j的使用,直接测试运行刚才的查询
在这里插入图片描述
简单使用:

  • 1、在使用Log4j的类中,导入包 import org.apache.log4j.Logger;
  • 2、日志对象,参数为当前类的class
    static Logger logger = Logger.getLogger(MyTest.class);  // MyTest为相关的类
    
  • 3、日志级别
    logger.info("info:进入了testLog4j");
    logger.debug("debug:进入了testLog4j");
    logger.error("error:进入了testLog4j");
    

7、分页

思考:为什么要分页?

  • 减少数据的处理量

使用Limit分页(从第0页开始查,每次查两个数据)

select * from userone limit 0, 2;

使用Mybatis实现分页,核心SQL

  • 1、接口
    // 分页
    List<User> getUserByLimit(Map<String, Integer> map);
    
  • 2、Mapper.xml
    <select id="getUserByLimit" parameterType="map" resultType="User" resultMap="UserMap">
        select * from userone limit #{startIndex}, #{pageSize};
    </select>
    
  • 3、测试
    @Test
    public void test05(){
        // 第一步: 获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            // 方式一: getMapper
            HashMap<String, Integer> map = new HashMap<String, Integer>();
            map.put("startIndex", 1);
            map.put("pageSize", 2);
            List<User> limitList = sqlSession.getMapper(UserMapper.class).getUserByLimit(map);
            for (User user : limitList) {
                System.out.println(user);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭SqlSession
            sqlSession.close();
        }
    }
    

RowBounds:

关于RowBounds代码解析

public class RowBounds {
    public static final int NO_ROW_OFFSET = 0;
    public static final int NO_ROW_LIMIT = 2147483647;
    public static final RowBounds DEFAULT = new RowBounds();
    private final int offset;	// 从第几页开始
    private final int limit;	// 显示多少条数据
	
	// 无参构造
    public RowBounds() {
        this.offset = 0;
        this.limit = 2147483647;
    }

	// 有参构造
    public RowBounds(int offset, int limit) {
        this.offset = offset;
        this.limit = limit;
    }

    public int getOffset() {
        return this.offset;
    }

    public int getLimit() {
        return this.limit;
    }
}

不在使用SQL实现分页

  • 1、接口
    // 分页2
    List<User> getUserByRowBounds();
    
  • 2、mapper.xml
    <!-- 通过RowBound进行分页查询 -->
    <select id="getUserByRowBounds" resultMap="UserMap">
        select * from userone;
    </select>
    
  • 3、测试
    @Test
    public void test06(){
        // 第一步: 获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            // 通过RowBounds实现
            RowBounds rowBounds = new RowBounds(1, 2);
            // 通过Java代码层面实现分页
            List<User> userList = sqlSession.selectList("com.zya.dao.UserMapper.getUserByRowBounds", null, rowBounds);
            for (User user:userList) {
                System.out.println(user);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭SqlSession
            sqlSession.close();
        }
    }
    

MyBatis 分页插件 PageHelper,了解即可


8、使用注解开发

面向接口编程

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

关于接口的理解

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

三个面向区别

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

使用注解开发:

1、注解在接口上实现

public interface UserMapper {
    @Select("select * from userone")
    List<User> getUsers();
}

2、需要在核心配置文件(mybatis-config.xml)中绑定接口

<!-- 绑定接口 -->
<mappers>
    <mapper class="com.zya.dao.UserMapper" />
</mappers>

3、测试

@Test
public void test01(){
    // 第一步: 获得SqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    try {
        // 底层主要运用反射
        List<User> userList = sqlSession.getMapper(UserMapper.class).getUsers();
        for (User user:userList) {
            System.out.println(user);
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        // 关闭SqlSession
        sqlSession.close();
    }
}

本质:反射机制实现

底层:动态代理!


Mybatis详细的执行流程:(重点!!!)
在这里插入图片描述
CRUD

我们可以在工具类创建的时候实现自动创建事务

  • 1、自动提交事务

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

    在这里插入图片描述

  • 2、编写接口,增加注解

    public interface UserMapper {
    
        @Select("select * from userone")
        List<User> getUsers();
    
        @Select("select * from userone where id=#{id}")
        User getUserById(@Param("id") int id);
    
        @Insert("insert into userone(id, name, pwd) values (#{id}, #{name}, #{pwd})")
        int addUser(User user);
    
        @Update("update userone set name=#{name}, pwd=#{pwd} where id=#{id}")
        int updateUser(User user);
    
        @Delete("delete from userone where id=#{id}")
        int deleteUser(@Param("id") int id);
    }
    
  • 3、测试类【注意:我们必须要讲接口注册绑定到我们的核心配置文件中】

    public class MyTest {
        @Test
        public void test01(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 底层主要运用反射
                List<User> userList = sqlSession.getMapper(UserMapper.class).getUsers();
                for (User user:userList) {
                    System.out.println(user);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    
        @Test
        public void test02(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 底层主要运用反射
                User user = sqlSession.getMapper(UserMapper.class).getUserById(2);
                System.out.println(user);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    
        @Test
        public void test03(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 底层主要运用反射
                sqlSession.getMapper(UserMapper.class).addUser(new User(6, "qh", "321"));
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    
        @Test
        public void test04(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 底层主要运用反射
                sqlSession.getMapper(UserMapper.class).updateUser(new User(6, "beida", "132"));
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    
        @Test
        public void test05(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 底层主要运用反射
                sqlSession.getMapper(UserMapper.class).deleteUser(6);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    }
    

关于@Param()注解

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

#{}和${}的区别

  • 1、#{}是预编译处理,${}是字符串替换。
  • 2、Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
  • 3、Mybatis 在处理 时 , 就 是 把 {}时,就是把 {}替换成变量的值。
  • 4、使用#{}可以有效的防止 SQL 注入,提高系统安全性。

9、Lombok

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.

  • Java library
  • plugs
  • build tools
  • with one annotation your class

使用步骤:

  • 1、在IDEA中安装Lombok插件
  • 2、在项目中导入lombok的jar包
    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
        <scope>compile</scope>
    </dependency>
    
  • 3、注解方法
    @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
    @var
    experimental @var
    @UtilityClass
    @ExtensionMethod (Experimental, activate manually in plugin settings)
    

常用说明:

  • @Data:无参构造,getter,setter,toString,hashCode,equals
  • @AllArgsConstructor:有参构造
  • @NoArgsConstructor:无参构造

10、多对一处理

多对一:

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

SQL:

create table stu(
	id int not null primary key,
	name varchar(255),
	tid int
)charset=utf8

create table teacher(
	id int not null primary key,
	name varchar(255),
	foreign key(id) references stu(tid)
)charset=utf8

测试环境搭建:

  • 1、导入lombok
    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
        <scope>compile</scope>
    </dependency>
    
  • 2、新建实体类 Teacher、Student
    @Data
    public class Student {
        private int id;
        private String name;
    
        // 学生需要关联一个老师
        private Teacher teacher;
    }
    
    @Data
    public class Teacher {
        private int id;
        private String name;
    }
    
  • 3、新建Mapper接口
    public interface TeacherMapper {
    	@Select("select * from teacher where id=#{tid}")
    	Teacher getTeacher(@Param("tid") int id);
    }
    
  • 4、建立Mapper.xml文件
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.zya.dao.TeacherMapper">
    
    </mapper>
    
  • 5、在核心配置文件中绑定注册我们的Mapper接口或者文件
    <mappers>
        <mapper class="com.zya.dao.TeacherMapper" />
        <mapper class="com.zya.dao.StudentMapper" />
    </mappers>
    
  • 6、测试查询是否能够成功!
    @Test
    public void test01(){
        // 第一步: 获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            // 底层主要运用反射
            Teacher teacher = sqlSession.getMapper(TeacherMapper.class).getTeacher(1);
            System.out.println(teacher);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭SqlSession
            sqlSession.close();
        }
    }
    

按照查询嵌套处理:

  • 1、接口

    public interface StudentMapper {
        // 查询所有的学生信息, 以及对应老师的信息
        List<Student> getStudent();
    }
    
  • 2、Mapper.xml文件

    <mapper namespace="com.zya.dao.StudentMapper">
    
        <!--
            思路:
                1、查询所有的学生的信息
                2、根据查询出来的学生的tid, 寻找对应的老师
         -->
        <select id="getStudent" resultMap="StudentTeacher">
            select * from stu;
        </select>
        <resultMap id="StudentTeacher" type="Student">
            <result property="id" column="id" />
            <result property="name" column="name" />
            <!-- 复杂的属性, 我们需要单独处理
                对象: association
                集合: collection
             -->
            <association property="teacher" column="tid" javaType="Teacher" select="getTeacher" />
        </resultMap>
        <select id="getTeacher" resultType="com.zya.pojo.Teacher">
            select * from teacher where id=#{id};
        </select>
    </mapper>
    
  • 3、测试

    @Test
    public void test01(){
        // 第一步: 获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            // 底层主要运用反射
            List<Student> studentList = sqlSession.getMapper(StudentMapper.class).getStudent();
            for (Student student : studentList) {
                System.out.println(student);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭SqlSession
            sqlSession.close();
        }
    }
    

按照结果嵌套处理:

  • 1、接口
    public interface StudentMapper {
        // 查询所有的学生信息, 以及对应老师的信息
        List<Student> getStudent2();
    }
    
  • 2、Mapper.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.zya.dao.StudentMapper">
    
    <!-- ======================按照结果嵌套处理====================== -->
        <select id="getStudent2" resultMap="StudentTeacher2">
            select s.id s_id, s.name s_name, t.name t_name
            from stu s, teacher t
            where s.tid = t.id;
        </select>
        
        <resultMap id="StudentTeacher2" type="Student">
            <result property="id" column="s_id" />
            <result property="name" column="s_name" />
            <association property="teacher" javaType="Teacher">
                <result property="name" column="t_name" />
            </association>
        </resultMap>
        
    </mapper>
    
  • 3、测试
    @Test
        public void test01(){
            // 第一步: 获得SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            try {
                // 底层主要运用反射
                List<Student> studentList = sqlSession.getMapper(StudentMapper.class).getStudent2();
                for (Student student : studentList) {
                    System.out.println(student);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 关闭SqlSession
                sqlSession.close();
            }
        }
    

回顾Mysql 多对一查询方式

  • 子查询
  • 联表查询

11、一对多处理

比如:一个老师拥有多个学生

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

  • 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;
    }
    

按照结果嵌套处理

<select id="getTeacher" resultType="Teacher">
        select * from teacher;
    </select>

    <!-- ====================按照结果嵌套查询==================== -->
    <select id="getTeacher2" resultMap="TeacherStudent">
        select s.id s_id, s.name s_name, t.name t_name, t.id t_id
        from stu s, teacher t
        where s.tid = t.id and t.id = #{tid};
    </select>
    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="t_id" />
        <result property="name" column="t_name" />
        <!--
            javaType: 指定的属性类型
            集合中的泛型信息, 我们使用ofType获取
         -->
        <collection property="students" ofType="Student">
            <result property="id" column="s_id" />
            <result property="name" column="s_name" />
            <result property="tid" column="tid" />
        </collection>
    </resultMap>

按照查询嵌套处理

<!-- ====================按照结果嵌套查询==================== -->
<select id="getTeacher3" resultMap="TeacherStudent2">
    select * from teacher where id=#{tid};
</select>
<resultMap id="TeacherStudent2" type="Teacher">
    <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id" />
</resultMap>

<select id="getStudentByTeacherId" resultType="Student">
    select * from stu where tid=#{tid};
</select>

小结:

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

注意点:

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

12、动态SQL

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

利用动态 SQL,可以彻底摆脱这种痛苦

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

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

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

搭建环境:

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、导包
  • 2、编写配置文件
  • 3、编写实体类
    @Data
    public class Blog {
        private int id;
        private String title;
        private String author;
        private Date createTime;
        private int views;
    }
    
  • 4、编写实体类对应的Mapper接口和Mapper.xml文件

if:使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。

<select id="queryBlog" parameterType="map" resultType="blog">
    select * from blog where 1=1
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="name">
        and name = #{name}
    </if>
</select>

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

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

trim、where、set

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

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

<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title != null">title = #{title},</if>
        <if test="author != null">author = #{author}</if>
    </set>
    <where>
        id = #{id}
    </where>
</update>

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

SQL片段:有的时候,我们可能会将一些功能的部分抽取出来,方便复用

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

注意事项:

  • 最好基于单表来定义SQL片段

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

<!--
    select * from blog where 1=1 and (id=1 or id=2 or id=3)
    我们现在传递一个万能的map, 这map中可以存在一个集合!
-->
<select id="queryBlogForeach" resultType="blog" parameterType="map">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id = #{id}
        </foreach>
    </where>
</select>

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

建议:

  • 现在MySql中写出完整的SQL,再对应的去修改成为我们的动态SQL实现通用即可!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值