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

自定义缓存

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

posted @ 2020-08-23 20:27  DJ同学  阅读(130)  评论(0编辑  收藏  举报