zoukankan      html  css  js  c++  java
  • Mybatis

    环境

    • jdk1.8
    • Mysql 5.7
    • maven 3.5.4
    • IDEA 2019.2

    储备知识

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

    Mybatis官网文档(中文)

    简介

    image-20200815194427281

    什么是Mybatis

    • MyBatis 是一款优秀的持久层框架
    • 它支持自定义SQL、存储过程以及高级映射
    • MyBatis 免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作
    //设置参数
    ...
    pst.setInt(1,4);
    pst.setString(2,"DJ");
    ...
    //获取结果集
    ...
    ResultSet rs = statement.executeQuery(sql);
    ...
    
    • MyBatis 可以通过简单的 XML或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
    xml就是可扩展标记语言,在Java中一般是作为配置文件。
    
    • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis
    • 2013年11月迁移到Github。

    如何获得Mybatis?

    • 方法一:从github获取 链接

    image-20200815221657576

    • 方法二【推荐】:maven仓库获取 链接

    • 方法三:官网

    持久化

    数据持久化

    • 数据持久化就是将数据从内存中的数据模型转化为存储模型
    • 内存:断电即失
    • 比如:数据库(jdbc)io支持持久化
    • 生活例子:冷藏、罐头

    为什么需要持久化?

    • 有一些对象,不能让他丢掉
    • 内存太贵了

    持久层

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

    为什么需要Mybatis

    • 方便
    • 传统的JDBC代码太复杂了,简化,框架
    • 将sql和代码解耦(sql文件就是放在xml,而之前的JDBC则是将两者合在一起)
    • 容易上手

    第一个Mybatis程序

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

    步骤

    1. 在数据库创建一个my_school,执行sql文件

    2. 创建一个普通的maven项目

      1. 删除src目录
      2. 导入依赖
      3. 需要在idea中下载lombok插件
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>
    
    1. 创建一个模块
      1. 子模块中存在
    <parent>
          <artifactId>mybatis-01</artifactId>
          <groupId>com.jmu</groupId>
          <version>1.0-SNAPSHOT</version>
    </parent>
    
    1. 父模块中存在
    <modules>
        <module>mybatis-1</module>
    </modules>
    
    • 编写mybatis的核心配置文件mybatis-config.xml
      • 注意点:用&amp;表示&
    <?xml version="1.0" encoding="UTF8" ?>
    <!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"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/my_school?serverTimezone=GMT&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    
    • 编写mybatis的工具类

      SqlSessionFactoryBuilder 一旦创建了 SqlSessionFactory,就不再需要它了 局部变量
      SqlSessionFactory SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在 应用作用域,比如:使用单例模式或者静态单例模式。
      SqlSession 每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它(sqlSession.close()) 请求或方法作用域,用完关闭

      SqlSession openSession(boolean var1);设置成true,就可以实现自动提交事务了,就不需要在增删改中手动写commit

    package com.jmu.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;
    
    public class MybatisUtil {
        private static SqlSessionFactory sqlSessionFactory;
        //从 XML 中构建 SqlSessionFactory
        static {
            try {
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //从 SqlSessionFactory 中获取 SqlSession
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    
    1. 编写代码
    • 实体类Stu
      • 和数据库中的字段一一对应
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Stu {
        private String sno;
        private String sname;
        private int age;
        private boolean sex;
        private int mno;
    }
    
    • DAO接口
    package com.jmu.dao;
    
    import com.jmu.pojo.Stu;
    
    import java.util.List;
    
    public interface StuDao {
        List<Stu> getAllStu();
        Stu getStuByName(String name);
    }
    
    
    • 实现DAO接口

    由原来的Impl文件转化为mapper.xml文件

    渐渐的将××DAO.java改成××Mapper.java

    <?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">
    <mapper namespace="com.jmu.dao.StuDao">
        <select id="getAllStu" resultType="com.jmu.pojo.Stu">
            select * from stu where sname like #{input_name}
        </select>
        <select id="getStuByName" resultType="com.jmu.pojo.Stu">
            select * from stu
        </select>
    </mapper>
    
    1. 测试

    test/java/com/jmu/dao/StuTest,在这个地方是为了规范

    package com.jmu.dao;
    
    import com.jmu.pojo.Stu;
    import com.jmu.utils.MybatisUtil;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.List;
    
    public class StuTest {
        @Test
        public void getUserList(){
            //第一步 获得sqlSession对象
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            //第二步 执行SQL getMapper
            StuDao stuMapper=sqlSession.getMapper(StuDao.class);
            System.out.println(stuMapper.getStuByName("小一"));
            List<Stu> all = stuMapper.getAllStu();
            for(Stu one:all){
                System.out.println(one);
            }
            //第三步 关闭sqlSession
            sqlSession.close();
        }
    }
    
    1. 测试结果
      注意的是:你的测试结果可能会出现报错,继续向下看
    Stu(sno=2020001, sname=小一, age=18, sex=false, mno=1)
    Stu(sno=2020001, sname=小一, age=18, sex=false, mno=1)
    Stu(sno=20200010, sname=小十, age=20, sex=false, mno=3)
    Stu(sno=20200011, sname=小快, age=19, sex=false, mno=3)
    Stu(sno=20200012, sname=小冬, age=21, sex=false, mno=3)
    Stu(sno=20200013, sname=小宇, age=19, sex=false, mno=0)
    Stu(sno=20200014, sname=小点, age=19, sex=false, mno=4)
    Stu(sno=20200015, sname=彭杰, age=21, sex=false, mno=4)
    Stu(sno=20200016, sname=彭小杰, age=21, sex=false, mno=4)
    Stu(sno=2020002, sname=小二, age=18, sex=true, mno=1)
    Stu(sno=2020003, sname=小四, age=18, sex=true, mno=1)
    Stu(sno=2020004, sname=小五, age=18, sex=true, mno=1)
    Stu(sno=2020005, sname=小六, age=18, sex=false, mno=2)
    Stu(sno=2020006, sname=小七, age=18, sex=true, mno=2)
    Stu(sno=2020007, sname=小八, age=18, sex=false, mno=2)
    Stu(sno=2020008, sname=小九, age=18, sex=true, mno=2)
    Stu(sno=2020009, sname=小十, age=19, sex=false, mno=3)
    

    碰到的问题

    报错一

    Type interface com.jmu.dao.UserDao is not known to the MapperRegistry.
    

    每一个***Mapper.xml都需要到mybatis-config.xml中注册。加到configuration中

    <mappers>
            <mapper resource="com/jmu/dao/StuMapper.xml"/>
    </mappers>
    

    报错二

    Could not find resource com/jmu/dao/UserMapper.xml
    

    Maven的资源过滤问题

    需要在pom.xml加入以下代码(在父项目添加即可,如果还不行的话,就在子module的pom.xml也添加)

    <build>
        <resources>
            <resource>
                <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>
    

    报错三

    1 字节的 UTF-8 序列的字节 1 无效。
    

    把xml的encoding属性值UTF-8改为UTF8==,Mapper.xml文件和mybatis-config.xml都需要改动

    CRUD

    namespace

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

    select

    • id:就是对应的namespace的方法名
    • resultType:sql语句执行的返回值
    • parameterType:参数

    {}与${}的区别

    MyBatis中使用parameterType向SQL语句传参,parameterType后的类型可以是基本类型int,String,HashMap和java自定义类型。

    #{}
        #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号

          例如:order by #{parameterName}
          假设传入参数是"Smith"
          会解析成:order by "Smith"

    ${}
        $将传入的数据直接显示生成在sql中。

          例如:order by #{parameterName}
          假设传入参数是"Smith"
          会解析成:order by Smith
    实践总结:

    1. #方式能够很大程度防止sql注入,$方式无法防止Sql注入。从安全性上考虑,能使用#尽量使用#来传参,因为这样可以有效防止SQL注入的问题。
    2. 当传入的是字段名或者表名的时候使用${}

    模糊查询

    1. 在java代码的时候,传递通配符%张%
    List<User> userList = mapper.getUserLike("%李%");
    
    1. 在sql拼接中使用like '%${value}%'
    select * from user where name like "%"#{value}"%" 
    

    增删改需要提交事务 sqlSession.commit();

    insert

    <insert id="insertUser" parameterType="com.jmu.pojo.User" >
        insert into user(username, password, age) values(#{username},#{password},#{age})
    </insert>
    
    @Test
    public void insert(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        UserDao userMapper = sqlSession.getMapper(UserDao.class);
        int n=userMapper.insertUser(new User("admin","123",12));
        System.out.println(n);
        sqlSession.commit();
        sqlSession.close();
    }
    

    update

    和insert基本一样

    万能的Map

    假设,我们的实体类,或者数据库中的表,字段或者参数过多。或者联表查询结果

    插入数据

    <insert id="insertUser2" parameterType="map" >
        insert into user(username, password, age) values(#{name},#{pwd},#{age})
    </insert>
    
    @Test
    public void insert(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        UserDao userMapper = sqlSession.getMapper(UserDao.class);
        Map<String,Object> map = new HashMap<String, Object>();
        map.put("name","zhangsan");
        map.put("pwd","mima");
        map.put("age",18);
        int n=userMapper.insertUser2(map);
        System.out.println(n);
        sqlSession.commit();
        sqlSession.close();
    }
    

    联表查询

    <select id="getUserInfo" resultType="map" parameterType="int">
        select * from user,address where user.auto_id=address.user_id and address.user_id=#{user_id}
    </select>
    
    @Test
    public void get(){
        SqlSession sqlSession=MybatisUtil.getSqlSession();
        UserDao userMapper = sqlSession.getMapper(UserDao.class);
        Map<String,Object> map=userMapper.getUserInfo(1);
        //遍历key value
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
    }
    
    传递类型 说明 例子
    Map传递参数 直接在 sql中取出Key即可 parameter="map"
    对象传递参数 直接在sql中取出对象的属性即可 parameter="com.jmu.pojo.××"
    只有一个基本类型参数 可以直接在sql中取到 parameter="int“ 也可以不写
    多个参数传递 使用Map或者注解

    配置解析

    核心配置文件

    configuration(配置)

    环境配置(environments)

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

    所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:

    • 每个数据库对应一个 SqlSessionFactory 实例

    Mybatis有2种事务管理器(JDBC|MANAGED)默认的事务管理器是JDBC

    默认数据源类型是POOLED(池)

    属性(properties)

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

    这些属性可以在外部进行配置,并可以进行动态替换。

    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
    

    编写好db.properties

    注意这里的&不使用&amp;

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

    报错:mybatis核心配置文件,需要注意标签配置顺序

    The content of element type "configuration" must match "(properties?,settin....
    
    • 可以直接引入配置文件
    <properties resource="db.properties" />
    
    • 也可以在内部引入属性配置
    <properties resource="org/mybatis/example/config.properties">
      <property name="username" value="dev_user"/>
      <property name="password" value="F2Fa3!33TYyg"/>
    </properties>
    
    • 但是外部引入的优先级大于内部引入

    类型别名(typeAliases)

    • 设置一个缩写名字
    • 意在降低冗余的全限定类名书写。例如:
    <typeAliases>
        <typeAlias type="com.jmu.pojo.User" alias="User" />
    </typeAliases>
    

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

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

    每一个在包 com.jmu.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 com.jmu.pojo.User 的别名为 user

    • 在实体类比较少的时候,可以使用第一种
    • 在实体类比较多的时候,可以使用第二种
    • 第一种可以 diy别名,第二种如果非要改别名,可以加注解
    @Alias("author")
    public class Author {
        ...
    }
    

    设置(settings)

    设置名 描述 有效值 默认值
    cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
    lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
    logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
    mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False

    映射器(mappers)

    方式一【推荐使用】:使用相对于类路径的资源引用
    注意是斜杆,不是点

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

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

    • 注意点
      • 接口和Mapper配置文件必须同名
      • 接口和Mapper配置文件必须在同一个包下
    <mappers>
        <mapper class="com.jmu.dao.UserMapper" />
    </mappers>
    

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

    解决方法:

    • 起别名(as)
      数据库字段 as 属性名
    select password as pwd ,username as name,age from user where auto_id=#{auto_id}
    
    • resultMap(注意我们去掉看resultType
      • 结果映射
      • column 数据库列 property 属性名
    <resultMap id="UserMap" type="User">
        <result column="username" property="name"/>
        <result column="password" property="pwd"/>
    </resultMap>
    <select id="getUserList" resultMap="UserMap">
        select * from user
    </select>
    

    日志

    日志工厂

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

    曾经:sout debug

    现在:日志工厂

    logImpl 【掌握】LOG4J |STDOUT_LOGGING(标准日志输出)

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

    Log4J

    什么是Log4J

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

    直接使用

    1. 先导入log4j的包
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
    1. 在resources中新建log4j.properties
    #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    log4j.rootLogger=DEBUG,console,file
    
    #控制台输出的相关设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    
    #文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/jmu.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
    
    1. 配置log4j为日志实现
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    
    1. 直接测试运行

    image-20200816185734997

    输出到文件

    1. 在要使用Log4J的类中,导入import org.apache.log4j.Logger;
      1. 注意:包不要导错了
    2. 日志对象,参数为当前类的class
    static Logger logger = Logger.getLogger(UserTest.class);
    
    1. 日志级别
    logger.info("info:进入testLog4j方法");
    logger.debug("debug:进入testLog4j方法");
    logger.error("error:进入testLog4j方法");
    

    分页

    思考:为什么要分页?

    • 减少数据的处理量

    使用limit分页

    select * from user limit startIndex,pageSize;
    

    使用Mybatis实现分页,核心SQL

    1. 接口
    //分页
    List<User> getUserByLimit(Map<String,Integer> map);
    
    1. Mapper.xml
    <select id="getUserByLimit" parameterType="map" resultType="User">
        select * from demo.user limit #{startIndex},#{pageSize}
    </select>
    
    1. 测试
    @Test
    public void getUserByLimit(){
        Map<String,Integer> map = new HashMap<String, Integer>();
        map.put("startIndex",1);
        map.put("pageSize",2);
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> all = userMapper.getUserByLimit(map);
        for (User user : all) {
            System.out.println(user);
        }
    }
    

    使用注解开发

    面向接口编程

    我们之前学过面向对象编程,但是真正的开发中,很多时候,我们会选择面向接口编程

    根本原因:解耦

    使用注解开发

    映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。比如

    package org.mybatis.example;
    import org.apache.ibatis.annotations.Select;
    public interface BlogMapper {
      @Select("SELECT * FROM blog WHERE id = #{id}")
      Blog selectBlog(int id);
    }
    

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

    步骤

    1. 注解在接口上实现
      1. 注意:方法存在有多个参数,所有参数一定要加@Param,取的参数主要是根据@Param里面的
    @Select("select *  from user where auto_id = #{auto_id}")
    User getUserById(@Param("auto_id") int id);
    
    1. 需要在核心配置文件中绑定接口
    <mappers>
        <mapper class="com.jmu.dao.UserMapper" />
    </mappers>
    

    关于@param()注解

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

    复杂查询

    数据库准备

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

    多对一处理

    多个学生对应一个老师

    测试环境搭建

    1. 新建实体类 Teacher Student
    private int id;
    private String name;
    
    //学生需要关联一个老师
    private Teacher teacher;
    
    1. 建立Mapper接口
    2. 建立Mapper.xml文件

    可以在resources中新建com.jmu.dao中放Student.xml和TeacherMapper.xml

    这样处理,在最后生成的时候会变成

    image-20200816221632015

    1. 在核心配置文件中添加
    <mappers>
        <mapper resource="com/jmu/dao/StudentMapper.xml" />
        <mapper resource="com/jmu/dao/TeacherMapper.xml" />
    </mappers>
    

    5.测试查询是否成功

    按照查询嵌套处理

    • 思路
      • 先查询出所有的学生
      • 根据学生的tid查询出对应的老师
    • 复杂属性我们需要单独处理
      • 对象 association
      • 集合 collection
    <mapper namespace="com.jmu.dao.StudentMapper">
        <resultMap id="StudentTeacher" type="Student">
            <result column="id" property="id"></result>
            <result column="name" property="name"></result>
            <association column="tid" property="teacher" javaType="Teacher" select="getTeacher"/>
        </resultMap>
        <select id="getStudent" resultMap="StudentTeacher">
            select * from student
        </select>
        <select id="getTeacher" resultType="Teacher">
            select * from teacher where id = #{tid}
        </select>
    </mapper>
    

    按照结果嵌套处理

    <resultMap id="StudentTeacher2" type="Student">
        <result column="sid" property="id"></result>
        <result column="sname" property="name"></result>
        <association property="teacher" javaType="Teacher" >
            <result column="tname" property="name" />
        </association>
    
    </resultMap>
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid,
        s.name sname,
        t.name tname
        from student s,teacher t
        where s.tid=t.id;
    </select>
    

    总结:Mysql多对一

    • 联表查询
    • 子查询

    一对多处理

    一个老师拥有多个学生

    测试环境搭建

    1. 编写实体类
    public class Student {
        private int id;
        private String name;
        private int tid;
        ....
    }
    public class Teacher {
        private int id;
        private String name;
        //一个老师教多个学生
        private List<Student> students;
        ....
    }
    

    按结果 嵌套处理

    • javaType用来指定实体类中的属性的类型
    • ofType 用来指定映射到List或者集合中pojo类型
    <resultMap id="TeacherStudent" type="Teacher">
        <result column="tid" property="id" />
        <result column="tname" property="name" />
        <collection  property="students" ofType="Student">
            <result column="sid" property="id"></result>
            <result column="sname" property="name"></result>
        </collection>
    </resultMap>
    <select id="getTeacher" resultMap="TeacherStudent">
        select t.id tid,
        t.name tname,
        s.id sid,
        s.name sname
        from student s,teacher t
        where s.tid=t.id;
    </select>
    

    面试高频

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

    动态SQL

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

    if
    choose
    trim
    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
    

    项目搭建

    public class Blog {
        private int id;
        private String title;
        private String author;
        private Date create_time;
        private int views;
    	...	
    }
    

    if

    test中的title"%"#{title}"%"是后端传过来的map的String,不是数据库的。即

    map.put("title1","标题");
    <when test="title1 != null">
        title like "%"#{title1}"%"
    </when>
    
    <select id="getBlogIf" parameterType="map" resultType="Blog">
        select * from blog where 1=1
        <if test="title != null">
            and title like "%"#{title}"%"
        </if>
        <if test="views != null">
            and views = #{views}
        </if>
    </select>
    

    choose、when、otherwise

    有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。switch-case

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

    trim、where、set

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

    select * from blog
    <where>
        <if test="title != null">
            and title like "%"#{title}"%"
        </if>
        <if test="views != null">
            and views = #{views}
        </if>
    </where>
    

    set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号

    <update id="updateAuthorIfNecessary">
      update Author
        <set>
          <if test="username != null">username=#{username},</if>
          <if test="password != null">password=#{password},</if>
          <if test="email != null">email=#{email},</if>
          <if test="bio != null">bio=#{bio}</if>
        </set>
      where id=#{id}
    </update>
    

    foreach

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

    <select id="selectPostIn" resultType="domain.blog.Post">
      SELECT *
      FROM POST P
      WHERE ID in
      <foreach item="item" index="index" collection="list"
          open="(" separator="," close=")">
            #{item}
      </foreach>
    </select>
    

    缓存(了解 )

    1. 什么是缓存?
      • 存在内存中的临时数据
      • 将用户经常查询的数据放在缓存(内存)中,用户去查询就不用从磁盘上(关系数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题
    2. 为什么使用缓存?
      • 减少和数据库的交互次数,减少系统开销,提高系统效率
    3. 什么样的数据能够使用缓存?
      • 经常并且不经常改变的数据

    Mybatis缓存

    Mybatis系统默认定义两级缓存:一级缓存和二级缓存

    • 默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)

    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存

    • 为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存

    一级缓存

    又称本地缓存:SqlSession

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

    测试步骤

    1. 开启日志
    2. 测试在一个SqlSession中查询两次相同的记录
    @Test
    public void test(){
    	SqlSession sqlSession = MybatisUtils getsqlSession();
    	UserMapper mapper =sqlSession getMapper(UserMapper class);
    	User user = mapper.queryUserById(1);
    	System.out.println(user):
    	System.out.println("======================");
    	User user2 = mapper.queryUserById(1);
    	System.out.println(user2):
    	System.out.println("======================");
        System. out. println(user==user2);//返回true,说明两个对象地址引用是相同的
    	sqlSession.close();
    }
    //观察日志发现只是走了一次sql
    

    二级缓存

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

    要启用全局的二级缓存,只需要在你的 SQL 映射文件xml中添加一行<cache/>

    需要开启全局缓存(在setting中)

    设置名 描述 有效值 默认值
    cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
    <mapper namespace="com.jmu.dao.BlogMapper">
        <cache/>
    ......
    

    测试

    开启两个sqlSession,一个执行完之后,将sqlSession关闭,另一个再去查询,看到SQL执行一次

    小结:

    • 只要开启了二级缓存,在同一个Mapper下就有效
    • 所有的数据都会先放在一级缓存中
    • 只有当Sqlseesion会话提交,或者关闭的时候,才会提交到二级缓存中
    • 用户查询数据,先看二级缓存有没有,再看一级缓存,最后看数据库

    自定义缓存

    除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。

  • 相关阅读:
    Spring-Lesson2
    Spring-Lesson1
    三十九:WEB漏洞-XXE&XML之利用检测绕过全解
    三十八:WEB漏洞-反序列化之PHP&JAVA全解(下)
    三十七:WEB漏洞-反序列化之PHP&JAVA全解(上)
    三十六:WEB漏洞-逻辑越权之验证码与Token接口
    cookie,session,token傻傻分不清
    三十五:WEB漏洞-逻辑漏洞之找回机制及接口安全
    三十四:WEB漏洞之登录脆弱及支付篡改
    三十三:WEB漏洞-逻辑越权之水平垂直越权
  • 原文地址:https://www.cnblogs.com/10134dz/p/13550541.html
Copyright © 2011-2022 走看看