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会话提交,或者关闭的时候,才会提交到二级缓存中
    • 用户查询数据,先看二级缓存有没有,再看一级缓存,最后看数据库

    自定义缓存

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

  • 相关阅读:
    HDOJ 1207 汉诺塔II
    [转]写代码的小女孩
    POJ Subway tree systems
    HDOJ 3555 Bomb (数位DP)
    POJ 1636 Prison rearrangement (DP)
    POJ 1015 Jury Compromise (DP)
    UVA 10003
    UVA 103 Stacking Boxes
    HDOJ 3530 Subsequence
    第三百六十二、三天 how can I 坚持
  • 原文地址:https://www.cnblogs.com/10134dz/p/13550541.html
Copyright © 2011-2022 走看看