Mybatis自学IDEA版(2020.5)

Mybatis-5.11

环境(我的版本):

  • JDK11
  • Mysql5.5
  • mave3.6.3

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

1、简介

1.1、什么是Mybatis

Mybatis自学IDEA版(2020.5)_第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仓库

    
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatisartifactId>
        <version>3.5.4version>
    dependency>
    
  • Github : https://github.com/tuguangquan/mybatis

  • 中文文档 : https://mybatis.org/mybatis-3/zh/index.html

1.2、持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
  • 内存:断电即失
  • 数据库(Jdbc),io文件持久化
  • 生活:冷藏,罐头

为什么需要持久化

  • 有一些对象,不能让他丢掉

  • 内存太贵了

1.3、持久层

Dao层、Service层、Controller层…

  • 完成持久化工作的代码块
  • 层界限十分明显

1.4、为什么需要Mybatis

  • 帮助程序猿将数据存入到数据库中

  • 方便

  • 传统JDBC代码太复杂了,需要简化,所以出现了框架,自动化

  • 更容易上手(但是技术没有高低之分,即使没有这个技术也可以)

  • 优点:

    • 简单易学
    • 灵活
    • sql和代码的分离,提高了可维护性
    • 提供映射标签,支持对象与数据库的orm字段关系映射
    • 提供对象关系映射标签,支持对象关系组件维护
    • 提供xml标签,支持编写动态sql

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

2、第一个Mybatis程序

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

2.1、搭建环境

搭建数据库

INSERT INTO `user` VALUES (1, 'ryan', '2018-2-27 17:47:08', 'male', '广州');
INSERT INTO `user` VALUES (2, 'victim', '2018-3-2 15:09:37', 'female', '深圳');
INSERT INTO `user` VALUES (3, 'ada', '2018-3-4 11:34:34', 'female', '深圳');
INSERT INTO `user` VALUES (4, 'lucky', '2018-3-4 12:04:06', 'female', '深圳');
INSERT INTO `user` VALUES (5, 'hellen', '2018-3-7 17:37:26', 'female', '深圳');
INSERT INTO `user` VALUES (6, 'richard', '2018-3-8 11:44:00', 'male', '广州');

新建项目

1、新建一个普通的Maven项目

2、删除src目录(父工程)

3、导入依赖和配置初始化资源路径

 <dependencies>
        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.4.5version>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.8version>
        dependency>
        
        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.12version>
        dependency>
       
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.11version>
        dependency>
dependencies>
<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.xmlinclude>
                <include>**/*.propertiesinclude>
            includes>
        resource>
        <resource>
            <directory>src/main/resourcesdirectory>
            <includes>
                <include>**/*.xmlinclude>
                <include>**/*.propertiesinclude>
            includes>
        resource>
    resources>
build>

2.2、创建一个模块

  • 编写mybatis的核心配置文件

    
    
    
    <configuration>
        
        <environments default="mysql">
            
            <environment id="mysql">
                
                <transactionManager type="JDBC">transactionManager>
                
                <dataSource type="POOLED">
                    
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                    <property name="username" value="root"/>
                    <property name="password" value="1227"/>
                dataSource>
            environment>
        environments>
        
        <mappers>
            
            
            <mapper resource="com/ryan/dao/UserMapper.xml"/>
        mappers>
    configuration>
    
  • 编写mybatis工具类

    package com.ryan.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 {
                //加载文件
                String resource = "mybatis-config.xml";
                InputStream is = Resources.getResourceAsStream(resource);
                //获取SqlSessionFactory对象
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
        // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
        public static SqlSession getSqlSession(){
            //获得sqlSession
            return sqlSessionFactory.openSession();
        }
    }
    

2.3、编写代码

  • 实体类

    package com.ryan.pojo;
    
    import java.util.Date;
    
    public class User {
        private int id;
        private String username;
        private Date birthday;
        private String gender;
        private String address;
    
        public User() {
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getGender() {
            return gender;
        }
    
        public void setGender(String gender) {
            this.gender = gender;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", gender='" + gender + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    
  • 接口

    package com.ryan.dao;
    
    import com.ryan.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        List getUserList();
    }
    
  • 接口实现类,由原来的impl转变为Mapper的配置文件

    
    
    
    
    
        
    
    
    

2.4、测试

记住这个错误

  • org.apache.ibatis.binding.BindingException: Type interface com.ryan.dao.UserMapper is not known to the MapperRegistry.

    • 那是因为你忘了在mybatis-config。xml中配置以下mapper注册(MapperRegistry)
    
    <mappers>
            
            <mapper resource="com/ryan/dao/UserMapper.xml"/>
    mappers>
    
  • junit测试

    package com.ryan.test.dao;
    
    import com.ryan.dao.UserMapper;
    import com.ryan.pojo.User;
    import com.ryan.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.List;
    
    public class UserMapperTest {
        @Test
        public void test1(){
            //1.获取sqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            //2.执行sql语句,获取Mapper对象(相当于获取实现类对象)
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            //3.调用方法获取数据
            List<User> userList = mapper.getUserList();
            //4.遍历结果集
            for (User user : userList) {
                System.out.println(user);
            }
            //5.关闭资源
            sqlSession.close();
        }
    }
    

    可能会遇到的问题:

    1. 配置文件没有注册

      1. 绑定接口错误
      2. 方法名不对
      3. 返回类型不对
      4. Maven导出资源不对

3、CRUD

1、namespace

namespace中的包名必须和Dao/mapper接口的包名一致

2、select

选择,查询语句

  • id:就是对应的namespace中的方法名
  • resultType:sql语句的返回值
  • parameter:参数类型

3、Insert

  1. 编写接口

    //增
        int insertUser(User user);
    
  2. 编写对应的mapper中的sql语句

    <!--不同的sql语句,标签可能不一样,一定要注意-->
        <insert id="insertUser" parameterType="com.ryan.pojo.User">
            insert into user values (#{id}, #{username}, #{birthday}, #{gender}, #{address})
        </insert>
    
  3. 测试

     @Test
        public void test3(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int i = mapper.insertUser(new User(24, "kobe", new Date(), "male", "洛杉矶"));
            System.out.println(i);
            //增删改,都别忘了提交事务,然后再关闭资源
            sqlSession.commit();
            sqlSession.close();
        }
    

4、update

<update id="updateUser" parameterType="com.ryan.pojo.User">
    update user set username=#{username}, address=#{address} where id=#{id}
</update>

5、delete

<delete id="deleteUser" parameterType="int">
    delete from user where id=#{id}
</delete>

注意:

  • 增删改需要提交事务

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

insert例子

//演示以下使用map传参
int insertUser2(Map<String, Object> map);
<insert id="insertUser2" parameterType="map">
    insert into user(username, gender) values(#{haha}, #{hehe})
</insert>
@Test
public void test6(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //提供一个map集合
    Map<String, Object> map = new HashMap<>();
    map.put("haha", "jordan");
    map.put("hehe", "male");
    int i = mapper.insertUser2(map);
    System.out.println(i);
    sqlSession.commit();
    sqlSession.close();
}

Map传递参数,直接在sql中取出key即可![parameteType=“map”]

对象传递参数,直接在sql中取对象的属性即可![parameterType=“Object”]

只有一个基本类型参数的情况下,可以直接在sql中取到!

多个参数用Map,或者注解

8、思考题

模糊查询怎么写?

1. java代码执行的时候,传递通配符

<select id="getUserLike" resultType="com.ryan.pojo.User">
    select * from user where username like #{value}
select>
@Test
public void test7(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //在java代码中使用通配符
    //List userList = mapper.getUserLike("%a%");
    List<User> userList = mapper.getUserLike("%a%");
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

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

<select id="getUserLike" resultType="com.ryan.pojo.User">
    SELECT * FROM user WHERE username like "%"#{value}"%"
select>

4、配置解析

1、核心配置文件

  • mybatis-config.xml

  • mybatis的配置文件包含了会深深影响mybatis行为的设置和属性信息(目前掌握其中几个就行)

    configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
    environment(环境变量)
    transactionManager(事务管理器)
    dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)
    

    2、环境配置/变量(environments/environment)

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

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

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

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

    3、属性(properties)

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

    1. 在核心配置文件中写一个properties标签,不过要注意properties标签的位置,必须放在第一个,否则会报错

      <properties resource="db.properties"/>
      
    2. 编写一个配置文件:db.properties

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

      如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:

      • 首先读取在 properties 元素体内指定的属性。
      • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
      • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

      因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。

4、类型别名(typeAliases)

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

<typeAliases>
    <typeAlias type="com.ryan.pojo.User" alias="User" />
typeAliases>

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

在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。(当然首字母大写也可以运行,但是建议使用小写)

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

5、设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。(主要掌握以下三个)
在这里插入图片描述
在这里插入图片描述

6、其他配置(了解即可)

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • [plugins(插件)]

7、映射器(mappers)

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

方式一:resource


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

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


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

注意点:

  1. UserMapper接口和UserMapper.xml文件必须同名

    1. UserMapper接口和UserMapper.xml文件必须在同一个包下

方法三:将包内的映射器接口实现全部注册为映射器


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

注意点:

1. UserMapper接口和UserMapper.xml文件必须同名
2. UserMapper接口和UserMapper.xml需要在同一个包下或者在resources目录下创建和java相同路径的包下(比较麻烦)

8、练习时间

  • 将数据配置文件外部引入
  • 实体类别名
  • 保证UserMapper接口和UserMapper.xml改为一直,并且放在同一个包下

作用域(Scope)和生命周期

Mybatis自学IDEA版(2020.5)_第2张图片

理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。

SqlSessionFactoryBuilder

  • 一旦创建了 SqlSessionFactory,就不再需要它了。
  • 局部变量

SqlSessionFactory

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

SqlSession

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

Mybatis自学IDEA版(2020.5)_第3张图片

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

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

1、方案一

数据库中的字段

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-evrLAqyu-1589527644833)(C:\Users\86131\AppData\Roaming\Typora\typora-user-images\image-20200513102102365.png)]

实体类中的字段

public class User {
    private int id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}

gender和sex是两个不一样的字段,导致查询结果中此字段的值为null

User{id=2, username='victim', birthday=Fri Mar 02 15:09:37 CST 2018, sex='null', address='深圳'}

那怎么解决这个问题呢?

  • 在sql语句中给gender取别名为sex(gender as sex)当然这种方法没什么意义,下面学习以下resultMap

2、resultMap

结果集映射

gender
sex

<resultMap id="userMap" type="user">
    
    <result column="gender" property="sex" />
resultMap>


<select id="getUserById" resultMap="userMap">
    select * from user where id=#{id}
select>
  • resultMap 元素是 MyBatis 中最重要最强大的元素。
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • ResultMap最优秀的地方在于,虽然你已经对他相当了解了,但是根本就不需要显示他们(只映射不一样的)
  • 如果世界总是这么简单就好了

6、日志

6.1、日志工厂

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

曾经:sout、debug

现在:日志工厂
在这里插入图片描述

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

1、STDOUT_LOGGING :

标准日志工厂实现

  • 首先在核心配置文件中配置我们的日志
  • 注意name和value都不要写错了,不要出现空格
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
settings>

Mybatis自学IDEA版(2020.5)_第4张图片

6.2、LOG4J

什么是LOG4J?

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

    
    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>
    
  2. 导入配置文件log4j.properties,放到resources目录下

    #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    log4j.rootLogger=DEBUG,console,file
    
    #控制台输出的相关设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    
    #文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/ryan.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的使用
    Mybatis自学IDEA版(2020.5)_第5张图片

简单使用

  1. 在要使用Log4j的类中,导入包

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

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

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

7、分页

思考:为什么要分页

  • 减少数据的处理量,提高客户体验

7.1使用Limit分页

语法:select * from user limit startIndex, pageSize

使用mybatis实现分页,核心就在sql

  1. 接口

    //分页查询
    List<User> getUserListByLimit(Map<String, Integer> map);
    
  2. Mapper.xml

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

    @Test
    public void test3(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Integer> map = new HashMap<>();
        map.put("startIndex", 1);
        map.put("pageSize", 3);
        List<User> userList = mapper.getUserListByLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
    

7.2、RowBounds分页(了解下)

不再使用sql实现分页

  1. 接口

    //RowBounds,也就是java代码实现分页
    List<User> getUserListByRowBounds();
    
  2. Mapper

    
    <select id="getUserListByRowBounds" resultMap="userMap">
        select * from user
    select>
    
  3. 测试

    @Test
    public void test4(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        RowBounds rowBounds = new RowBounds(0, 2);
        List<User> userList = 	sqlSession.selectList("com.ryan.dao.UserMapper.getUserListByRowBounds", null, rowBounds);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
    

7.3、分页插件(有兴趣的可以自行了解下)

Mybatis自学IDEA版(2020.5)_第6张图片

8、使用注解开发

8.1、面向接口编程

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

关于接口的理解

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

三个面向区别

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

8.2、使用注解开发

  1. 注解在接口上实现
  2. 需要在核心配置文件中绑定接口
  3. 测试

本质:反射机制实现

底层:动态代理

Mybatis详细的执行流程
Mybatis自学IDEA版(2020.5)_第7张图片

8.3、使用注解的CRUD

首先,我们可以在工具类中设置自动提交事务(虽然不是很建议)

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

接口

//使用注解执行sql语句,注意:注解是放在方法上面的
public interface UserMapper {
    @Select("select * from user")
    List<User> getUsers();

    //查,方法中如果有基本类型的参数记得加上@Param注解(多个必须每个都加,单个可以不加,但是建议都写上)
    @Select("select * from user where id=#{id}")
    User getUser(@Param("id")int id);

    //增
    @Insert("insert into user values(#{id},#{username},#{birthday},#{sex},#{address})")
    //注意上面传的值主要以实体类的属性名称为准,例如性别要写成sex。而不是数据库中的gender,否则会报错
    int addUser(User user);

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

    //改,改的方法参数直接传一个实体类
    @Update("update user set username=#{username}, gender=#{sex} where id=#{id}")
    //注意上面的字段名称是和数据库一一对应,但是穿的值的名称要和实体类的属性名称保持一致
    int updateUser(User user);
}

测试:略

注意:我们必须要将接口注册绑定到我们的核心配置文件中

关于@Param()注解

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

关于#{}和${}的区别

  • #{}是预编译处理,$ {}是字符串替换。
  • MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
  • 使用 #{} 可以有效的防止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

使用步骤:

  1. 在idea设置中,添加插件

  2. 导入jar包

  3. 在实体类上添加注解即可,比较常用的是这三个注解

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    

10、多对一处理

多对一:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFPAZ6TU-1589527644839)(C:\Users\86131\AppData\Roaming\Typora\typora-user-images\image-20200514085412873.png)]

  • 多个学生,对应一个老师
  • 对于学生这边而言,关联——多个学生关联一个老师【多对一】
  • 对于老师而言,集合——一个老师,有很多学生【一对多】
    Mybatis自学IDEA版(2020.5)_第8张图片

1、环境搭建测试

  1. 导入lombok

  2. 创建数据库:teacher和student两张表

  3. 编写两个实体类:Teacher和Student

    package com.ryan.pojo;
    
    import lombok.Data;
    
    @Data
    public class Teacher {
        private int id;
        private String name;
    }
    
    package com.ryan.pojo;
    
    import lombok.Data;
    
    @Data
    public class Student {
        private int id;
        private String name;
        private Teacher teacher;
    }
    
  4. 编写两个接口:StudentMapper和TeacherMapper,注意一定是一个实体类对应一个Mapper

  5. 编写对应的两个Mapper.xml配置文件

  6. 测试

注意点:别忘了在mybaits核心配置文件中注册mapper

2、子查询

  • 先查studnet的结果,并且映射结果集

  • 在映射结果集中,如果有java对象则使用association

  • 在association标签中添加子查询

  • 最后编写子查询

    
    <select id="getStudents" resultMap="studentTeacher">
        select * from student
    select>
    
    <resultMap id="studentTeacher" type="student">
        <result property="id" column="id"/>
        <result property="name" column="name" />
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher" />
    resultMap>
    
    <select id="getTeacher" resultType="teacher">
        select * from teacher where id = #{id}
    select>
    

3、联表查询

  • 直接使用多表查询,并且映射结果集

  • 在映射结果集中,如果有java对象则使用association

- 在association标签中,嵌套映射结果集

  ```xml
  <select id="getStudents2" resultMap="studentTeacher2">
      select s.id sid, s.name sname, t.id, t.name tname from student s left join teacher t on s.tid=t.id
  </select>
  <resultMap id="studentTeacher2" type="student">
      <result property="id" column="sid"/>
      <result property="name" column="sname"/>
  
      
      
  
```

11、一对多处理

1、联表查询

  • 直接使用多表查询,并映射结果集

  • 在映射结果集中, 如有集合则使用collection标签

  • 在collection标签中,嵌套映射结果集

    
    <select id="getTeacherById" resultMap="teacherStudent">
        select s.id sid, s.name sname, t.id tid, t.name tname
        from student s,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"/>
        
        <collection property="students" ofType="student" >
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        collection>
    resultMap>
    

2、子查询

  • 先使用单表查询,并且映射结果集

  • 在映射结果集中, 如有集合则使用collection标签

  • 在collection标签中,再嵌套查询语句

    
    <select id="getTeacherById2" resultMap="teacherStudent2">
        select * from teacher where id=#{tid}
    select>
    
    <resultMap id="teacherStudent2" type="teacher">
        
        
        <collection property="students" javaType="ArrayList" ofType="student" column="id" select="getStudentByTeacherId"/>
    resultMap>
    
    <select id="getStudentByTeacherId" resultType="student">
        select * from student where tid=#{id}
    select>
    

3、小结

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

注意点:

  • 保证sql的可读性,尽量保证通俗易懂
  • 注意一对多和多对一中,属性名和字段的问题
  • 如果问题不好排查错误,可以使用日志,建议使用log4j
  • 尽量避免出现慢sql的情况

面试高频:

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

12、动态SQL

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

利用动态sql这一特性可以彻底摆脱手动拼接字符串的痛苦

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

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

环境搭建及测试

  1. 创建数据库

    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
    
  2. 编写实体类,注意其中创建时间的属性和数据库中的字段不一样,需要设置核心配置文件

    package com.ryan.pojo;
    
    import lombok.Data;
    
    import java.util.Date;
    @Data
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime;//注意,这里的创建时间属性和数据中的字段不一样,需要到核心配置文件中设置
        private int views;
    }
    
    
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    
  3. 编写接口和mapper.xml文件

    package com.ryan.dao;
    import java.util.Map;
    
    public interface BlogMapper {
        int addBlog(Map<String, Object> map);
    }
    
    
    
    <mapper namespace="com.ryan.dao.BlogMapper">
        <insert id="addBlog" parameterType="blog">
            insert into blog values(#{id},#{title},#{author},#{createTime},#{views})
        insert>
    mapper>
    
  4. 测试

IF

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

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

trim (where, set)

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

    <select id="queryBlogWhere" 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="updateBlogSet" parameterType="map">
            update blog
            <set>
                <if test="title != null">
                    title=#{title},
                if>
                <if test="author !=null">
                    author=#{author}
                if>
            set>
            where id = #{id}
        update>
    

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

SQL片段

有的时候,我们可能会将一些重复的,通用的部分抽取取来,方便复用

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

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

    <select id="queryBlogIF" parameterType="map" resultType="blog">
            select * from blog where 1=1
            <include refid="if-title-author">include>
        select>
    

注意点:抽取的公共部分建议基于简单的逻辑判断,不要在sql片段中添加where标签

foreach

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

  • 它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符

  • 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach

  • 当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。

  • 当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

    //foreach list
        List<Blog> queryBlogForeach(@Param("ids") List<Integer> list);
    
    <select id="queryBlogForeach" parameterType="list" resultType="blog">
            select * from mybatis.blog
            where id in
            <foreach collection="ids" item="id" index="index"
                     open="(" close=")" separator=",">
                #{id}
            foreach>
        select>
    
     @Test
        public void test5(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            List<Integer> ids = new ArrayList<>();
            ids.add(1);
            ids.add(2);
            List<Blog> blogs = mapper.queryBlogForeach(ids);
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
    

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

13、缓存

13.1、简介

  1. 为什么要使用缓存?

    因为如果我们每查询一些数据的时候,都要连接数据读写,非常耗费资源,影响性能,而使用缓存的话,可以直接从缓存中读写数据,不需要连接数据库,缓解高并发的压力

  2. 什么样的数据能使用缓存?

    经常查询并且不经常改变的数据

13.2、Mybatis缓存

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

13.3、一级缓存

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

测试:

  • 搭建环境

  • 编写实体类、接口、mapper

  • 测试

    @Test
        public void test(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user1 = mapper.getUserById(2);
            System.out.println(user1);
    
            System.out.println("--------------------------------------");
    
            User user2 = mapper.getUserById(2);
            System.out.println(user2);
    
            sqlSession.close();
        }
    
  • 结果:查询一样的数据,只走了一次sql,说明一级缓存是默认开启着的,作用域是从开启sqlSession到关闭sqlSession之间

    Mybatis自学IDEA版(2020.5)_第9张图片

那么一级缓存什么时候失效呢?

  1. 查询不同的数据时

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

  3. 查询不同的mapper.xml

  4. 手动清理缓存

    sqlSession.clearCache();
    

小结:一级缓存默认开支,作用域是sqlSession开启到关闭,一级缓存相当于一个Map

13.4、二级缓存

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

步骤:

  1. 在核心配置文件中开启二级缓存

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

    
        <cache/>
    
  3. 在实体类中实现序列化接口,为什么?

    1. 缓存机制:将查询结果保存到内存中
    2. 内存饱满,需要移出时,MyBatis就会自动将内存中的内容进行移除,但是文件很重要,不能,此时就需要进行序列化,以文件的形式将内容从内存保存到硬盘上,一个内容保存成文件的读写,必须实现序列化。
  4. 测试:两次会话

    @Test
    public void test2(){
        SqlSession sqlSession1 = MybatisUtils.getSqlSession();
        SqlSession sqlSession2 = MybatisUtils.getSqlSession();
    
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        User user1 = mapper1.getUserById(5);
        System.out.println(user1);
        sqlSession1.close();
    
        System.out.println("--------------------------------------");
    
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2 = mapper2.getUserById(5);
        System.out.println(user2);
        sqlSession2.close();
    
    }
    
  5. 结果:第二次查询直接从缓存中查询了
    Mybatis自学IDEA版(2020.5)_第10张图片

小结:

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

13.5、缓存原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pwsXUH32-1589527644842)(C:\Users\86131\AppData\Roaming\Typora\typora-user-images\image-20200515124923585.png)]

13.6、自定义缓存(了解下即可)

最后推荐:
这是我在b站学习的,视频推荐狂神说的,通俗易懂

你可能感兴趣的:(Mybatis自学IDEA版(2020.5))