框架-Mybatis的完整学习

本文详细介绍了MyBatis持久层框架的使用,包括配置、映射文件、SqlSession工厂、DAO接口、XML配置、CRUD操作、日志配置、分页查询以及动态SQL等内容。通过实例演示了MyBatis如何简化JDBC代码,实现数据持久化的高效处理。此外,还讨论了一级缓存和二级缓存的概念,以及如何自定义缓存策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mybatis

官方参考文档:(https://mybatis.org/mybatis-3/index.html)

  • mybatis是一款持久层框架
  • 支持定制化SQL,存储过程以及高级映射
  • mybatis避免了几乎所有的JDBC代码,手动设置参数记忆获取结果集
  • 可以使用XML或注解配置和映射原生类型、接口和Java的pojo为数据库中的记录。

如何获得Mybatis?

  • 从GitHub网站上获取:(https://github.com/mybatis/mybatis-3/releases)

  • maven仓库:

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

数据持久化:将程序的数据在持久状态和瞬时状态转化的过程

内存:断带即失

持久层:

  • Dao层
  • Service层y
  • Servlet层

为什么需要mybatis?

  • 传统的JDBC代码太复杂,简化

  • 简单,灵活

1、第一个mybatis程序

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

1.1、搭建数据库

CREATE DATABASE `mybatis`;
USE `mybatis`;

CREATE TABLE `user`(
 `id` INT(20) NOT NULL,
 `name` VARCHAR(30) DEFAULT NULL,
 `pwd` VARCHAR(30) DEFAULT NULL,
 PRIMARY KEY(`id`)
)ENGINE INNODB DEFAULT CHARSET=utf8;

INSERT INTO `user`(`id`,`name`,`pwd`) VALUES
(1,'张三','123456'),
(2,'李四','123456'),
(3,'王五','123756')

1.2、创建项目,导入jar包

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
    	<!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

1.3、mybatis核心配置文件

写在Resources文件下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>s
    <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/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456789"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

1.从xml构建SqlSessionFactory

2.从 SqlSessionFactory 获取 SqlSession

获取 SqlSession 的实例。

SqlSession 绝对包含对数据库执行 SQL 命令所需的所有方法。可以直接针对 SqlSession 实例执行映射的 SQL 语句

//SqlSessionFactory -- SqlSession
//第一步配置三步代码
public class MybatisUtils {
    public static SqlSessionFactory 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 的实例,通过openSession()方法
    // SqlSession 绝对包含对数据库执行 SQL 命令所需的所有方法
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

1.4 编写代码

  • Dao接口
public interface UserDao {
    List<User> getUserList();
}
  • 接口实现类由原来的UserDaoImpl转换为一个Mapper配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.liu.dao.UserDao">
    <!--查询语句,id对于接口方法的名字,返回的结果写泛型里的东西,写路径-->
    <select id="getUserList" resultType="com.liu.pojo.User">
        select * from mybatis.user
    </select>
</mapper>

1.5、测试

注意点:错误:类型接口dao在mapper注册中未知

org.apache.ibatis.binding.BindingException: Type interface com.liu.dao.UserDao is not known to the MapperRegistry.

资源过滤问题:在target目录下没有生成资源文件,由于约定大于配置

java.lang.ExceptionInInitializerError

解决在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>

出现一字节的utf-8序列的字节1无效,是因为web文件中写了中文注释:

解决:将所有xml文件中的utf-8改为utf8

在xml文件中出现sql dialect is not configured

解决:在浅黄色区域按alt+enter,将generic SQL改为mysql即可查询到表

核心配置文件的资源路径下必须用/

在控制台输出查询语句时不能正常显示:

解决:可能是实体类创建时候没有toString方法

2、CRUD

namespace

namespace中的包名要和Dao/mapper接口的包名保持一致

select

选择,查询语句:

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

流程:

第一步:在UserMapper接口里写抽象方法

public interface UserMapper {
    //查询全部用户
    List<User> getUserList();

    //根据id查询用户
    User getUserById(int id);

    //insert一个用户
    int addUser(User user);

    //修改用户
    int updateUser(User user);

    //根据id delete一个用户
    int deleteById(int id);
}

第二步,在UserMapper.xml文件里编写sql语句

通过#{}取传入的值,参数类型和返回结果类型里写包名全称

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.dao.UserMapper">

    <select id="getUserList" resultType="com.liu.pojo.User">
        select * from mybatis.user
    </select>

    <select id="getUserById" parameterType="int" resultType="com.liu.pojo.User">
        select * from mybatis.user where id = #{id}
    </select>

    <insert id="addUser" parameterType="com.liu.pojo.User">
        insert into mybatis.user(id, name, pwd) VALUES (#{id},#{name},#{pwd});
    </insert>

    <update id="updateUser" parameterType="com.liu.pojo.User">
        update mybatis.user
            set name=#{name},pwd=#{pwd}
        where id = #{id};
    </update>

    <delete id="deleteById" parameterType="int">
        delete from mybatis.user where id=#{id};
    </delete>
</mapper>

第三步,测试,注意:增删改需要提交事务

//测试类写在test文件下,和上面文件一一对应
public class UserMapperTest {

    @Test
    public void test(){

        //获得sqlSession对象,
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行sql,getMapper接口类型,返回接口类型就可以使用接口方法
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭sqlSession
        sqlSession.close();
    }

    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);
        sqlSession.close();
    }

    //增删改需要提交事务
    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        int res = mapper.addUser(new User(4, "泽泽", "123678"));
        if (res>0){
            System.out.println("插入成功");
        }
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }

    //更新
    @Test
    public void updateUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        int req = mapper.updateUser(new User(4, "天天", "123123"));
        if (req>0){
            System.out.println("修改成功");
        }

        sqlSession.commit();
        sqlSession.close();
    }

    //删除用户
    @Test
    public void deleteById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int i = mapper.deleteById(4);
        if (i>0){
            System.out.println("删除成功");
        }
        sqlSession.commit();
        sqlSession.close();
    }

}

2.1、万能的Map

假设实体类或者数据库中的表,字段或者参数过多,应当考虑map!

int addUser2(Map<String,Object> map);
<insert id="addUser2" parameterType="map">
        insert into mybatis.user(id, name, pwd) VALUES (#{idUser},#{nameUser},#{pwdUser});
    </insert>
public void addUser2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String,Object> map = new HashMap<>();
        map.put("idUser",4);
        map.put("nameUser","kaka");
        int res = mapper.addUser2(map);
        if (res>0){
            System.out.println("插入成功");
        }
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }

map传递参数,直接在sql中取出key即可

对象传递参数,直接在sql中取对象的属性即可

只有一个基本类型的时候可以直接在sql中获取!

2.2 模糊查询

//模糊查询
    List<User> getUserLike(String value);
<select id="getUserLike" resultType="com.liu.pojo.User">
        select * from mybatis.user where name like #{vaule}
    </select>
<!-- select * from mybatis.user where name like #"%"{vaule}"%"  会造成sql注入问题,用户输入1 or 1=1-->
//模糊查询字段后面加 %查的字%
    @Test
    public void getUserLike(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserLike("%李%");

        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.commit();
        sqlSession.close();
    }

3、配置解析

3.1 核心配置文件

  • mybatis-config.xml

在这里插入图片描述

3.2 环境变量(environments)

虽然您可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一个环境。

因此,如果要连接到两个数据库,则需要创建两个 SqlSessionFactory 实例,每个实例对应一个。对于三个数据库,您需要三个实例,依此类推。

有两种 TransactionManager事务管理器:

  • JDBC – 此配置仅直接使用 JDBC 提交和回滚工具。它依赖于从数据源检索到的连接来管理事务的范围。
  • Managed– 此配置几乎不执行任何操作。它从不提交或回滚连接。

数据源:连接数据库

  • 有三种内置数据源类型(即 type=“[UNPOOLED|POOLED|JNDI]“):

  • UNPOOLED – 此数据源实现只是在每次请求连接时打开和关闭连接。

  • POOLED – 此数据源实现将 JDBC 连接对象池化,外部响应更快

  • JNDI – 此数据源的实现旨在与容器(如 EJB 或应用程序服务器)一起使用,这些容器可以集中或外部配置数据源,并在 JNDI 上下文中放置对它的引用

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

3.3 属性(properties)

属性外部设置可动态替换,可以在典型的 Java 属性文件(db.properties)实例中进行配置,也可以通过属性元素的子元素传入。

编写配置文件:

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

在这里插入图片描述

在核心配置文件中引入外部配置文件,如果两个外部文件中有同一个字段,优先使用外部配置文件

 <properties resource="db.properties"/>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

3.4 类型别名(typeAliases)

类型别名只是 Java 类型的较短名称。它仅与 XML 配置相关,并且只是为了减少完全限定类名的冗余类型而存在。

第一种方式:

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

第二种方式:指定一个包名,mybatis会在包名下搜索JavaBean,用实体类的首字母小写代替的类名代替别名

<typeAliases>
        <typeAlias type="com.liu.pojo"/>
    </typeAliases>

扫描包的情况下,或者通过实体类上增加注解名来代替别名

@Alias("author")

常用内置别名:

_byte字节
_long
_short
_intint
_integerint
_double
_float
_boolean布尔
字符串字符串
字节字节
int整数
整数整数
布尔布尔
日期日期
十进制大十进制
大十进制大十进制
对象对象
地图地图
哈希映射哈希映射
列表列表
数组列表阵列列表
收集收集
迭 代迭 代

3.5 设置(settings)

在这里插入图片描述

比如数据库中的字段last_name对应实体类中的lastName,这个会自动帮助你生成!

日志实现:

在这里插入图片描述

开启缓存和懒加载:

在这里插入图片描述

其他配置:

plugins:

  • mybatis-generator-core
  • mybatis-plus
  • 通用mapper

3.6 映射器(mappers)

未注册会报错!!

MapperRegistry:注册绑定Mapper文件,每写一个dao实现类就需要绑定

方式一(推荐):使用相对于类路径的资源引用,无论文件在哪都可以执行

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

方式二:使用映射器接口实现类的完全限定类名,通过class

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

方式三:通过包名实现

  • 接口和Mapper配置文件必须在同一个包下且必须同名
<mappers>
  <package name="com.liu.dao.UserMapper"/>
</mappers>

3.7 生命周期和作用域

在这里插入图片描述

SqlSessionFactoryBuilder:

  • 一旦创建就不需要它了
  • 局部变量

SqlSessionFactory

  • 数据库连接池
  • 一旦创建就一直存在,不能多次重建它
  • 全局作用域

SqlSession

  • 连接到连接池的一个请求
  • 关闭请求,会回收供下一个使用
  • 方法或者请求作用域

3.8 解决数据库与字段不一致的问题

当数据库的字段与实体类的字段不一致的时候,会查出null值

解决方法:

  • 别名:将sql查询的字段取别名
  • resultMap结果映射集

3.resultMap标签

<resultMap id="UserMap" type="User">
        <!--column数据库中的字段,property实体类中的属性-->
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pwd" property="password"/>
    </resultMap>
    <select id="getUserById" resultMap="UserMap">
        select * from mybatis.user where id = #{id}
    </select>

4、 日志

4.1 日志工厂

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

曾经:debug,sout

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

  • SLF4J

  • LOG4J(掌握)

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING(掌握)

  • NO_LOGGING

在Mybatis中具体使用哪个在核心配置文件中设置中设定!

<settings>
       <!--标准的日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

打印日志:

在这里插入图片描述

4.2 LOG4J

控制日志输送的目的地是控制台,文件,GUI组件

控制每一天日志的输出格式

通过一个配置文件来灵活的进行配置,不需要修改应用的代码

1.导入log4j的包

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>

2.在resources文件下建立log4j.properties文件

#输出日志文件到console和file目的地
log4j.rootLogger=DEBUG,console,file

# 控制台(console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

# 文件输出的相关设置(file)
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.File=D:/logs/log.log4j
log4j.appender.file.MaxFileSize=10mb
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的使用

在这里插入图片描述

别导错包:import org.apache.log4j.Logger;

//测试类写在test文件下,和上面文件一一对应
public class UserMapperTest {
  static Logger Logger = org.apache.log4j.Logger.getLogger(UserMapperTest.class);
    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        Logger.info("测试,进入getUserById方法");//提示信息
        Logger.debug("debug:进入了特色testLog4j");//bug信息
        Logger.error("error:进入了特色testLog4j");//错误信息
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);
        sqlSession.close();
    }
}

5、分页

语法:SELECT * FROM user limit 开始查询到的地方,每一页显示的量;

只有一个参数,代表从第一个查到这个参数为止!

bug:每一页的显示量是-1,可以查到最后一个数据

//分页
List<User> getUserLimit(Map<String,Integer> map);
<select id="getUserLimit" parameterType="map" resultType="User">
    select * from mybatis.user limit #{startIndex},#{PageSize}
</select>
@Test
    public void getUserLimit(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);//从接口获取
        HashMap<String, Integer> map = new HashMap<String,Integer>();
        map.put("startIndex",0);
        map.put("PageSize",2);
        List<User> userLimit = mapper.getUserLimit(map);
        for (User user : userLimit) {
            System.out.println(user);
        }

        sqlSession.close();
    }

5.1、RowBounds类

1.接口

//RowBounds
List<User> getUserRowBounds();

2.mapper.xml

<select id="getUserRowBounds" resultType="User">
    select * from mybatis.user
</select>

3.测试

 @Test
    public void getUserRowBounds(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //RowBounds实现
        RowBounds rowBounds = new RowBounds(1,2);
        //通过java代码层面实现分类
        List<User> userList = sqlSession.selectList("com.liu.dao.UserMapper.getUserRowBounds",null,rowBounds);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

6、使用注解开发

6.1、底层与核心本质

核心本质:通过反射来实现的,反射可以获取到所有的参数及返回类型

底层:动态代理模式:

在这里插入图片描述

  1. 静态代理实现较简单,代理类在编译期生成,效率高。缺点是会生成大量的代理类。需要代理类和真实对象实现同一个接口
  2. JDK动态代理不要求代理类和委托类实现同一个接口,但是委托类需要实现接口,代理类需要实现InvocationHandler接口。
  3. 动态代理要求代理类InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。

动态代理模式:

class HelloServiceDynamicProxy {
 
    private HelloService helloService;
    public HelloServiceDynamicProxy(HelloService helloService) {
        this.helloService = helloService;
    }
 
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("Before say hello...");
                Object ret = method.invoke(helloService, args);
                System.out.println("After say hello...");
                return ret;
            }
        });
    }
}

6.2、面向接口编程

真正开发中使用面向接口编程,根本原因:解耦,定义(规范,约束)与实现(名实分离的原则)的分离!

简单的select查询可以用注解,复杂的还是要用xml来配置,比如数据库的字段与实例类的字段不同,需要用到resultMap结果集映射就必须得用xml实现

1.接口加注解

@Select("select * from mybatis.user")
List<User> getUser();

2.测试

public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> user = mapper.getUser();
        for (User user1 : user) {
            System.out.println(user1);
        }
        sqlSession.close();
    }

7、Mybatis的执行流程

在这里插入图片描述

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

通过在openSession里设置为true,就不需要手动提交事务了

7.1、增删改

方法存在多个参数,所有基本类型参数前面必须加上Param注解,Param里的参数就是#{}取的值,引用类型(比如User类)不需要加Param

面试题:#{}与${}区别:

#{}可以防止sql注入的问题,${}不行,两者就像preStatemt和Statement的区别

1.接口

@Select("select * from mybatis.user")
    List<User> getUser();

    //方法存在多个参数,所有基本类型参数前面必须加上Param注解,Param里的参数就是#{}取的值
    //引用类型(比如User类)不需要加Param
    @Select("select * from mybatis.user where id = #{id}")
    User getUserById(@Param("id") int id);

    //增
    @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{pwd})")
    int addUser(User user);

    //修改
    @Update("update user set name = #{name},pwd=#{pwd} where id = #{id}")
    int updateUser(User user);

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

(必须在核心配置文件中配置映射文件)

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

2.测试

 @Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        List<User> user = mapper.getUser();
//        for (User user1 : user) {
//            System.out.println(user1);
//        }
        User userById = mapper.getUserById(1);
        System.out.println(userById);
        sqlSession.close();

    }

    @Test
    public void test2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        int i = mapper.addUser(new User(5, "周周", "12323431"));
//        int i = mapper.updateUser(new User(5, "依依", "123231"));
        int i = mapper.deleteUserById(5);
        if (i>0){
            System.out.println("删除成功!");
        }
        sqlSession.close();
    }

8、Lombok

再也不用写get/set方法!在实体类上加注解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;

}

1.下载lombok

在setting的plugins下载!

2.导入jar包

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>

@Data://无参构造,get/set,tostring,hashcode,equals
@AllArgsConstructor //有参构造
@NoArgsConstructor //无参构造

9、多对一的处理

多张表关联一个表!

association:一个复杂类型的关联,许多结果将包装成这种类型

  • 嵌套结果映射-关联本身可以是一个resultMap元素,或者从别处引用一个

在这里插入图片描述

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.导入lombok

2.新建实体类Teacher,Student

3.建立Mapper接口

4.建立Mapper.xml文件,保证在同一个包名下面

5.配置核心配置文件mappers注册绑定Mapper接口

6.测试

按照查询嵌套处理

直接各自查询出两张表,再通过子查询的方式

注意点:

1.resultType里面对应的是实体类的名字也就是pojo

2.查询的id就是接口方法名字

3.老师表是一个对象需要用到结果集映射的association

4.javaType是指定的属性名

思路:

1.查询所有学生信息

2.根据查询出来的学生的tid,寻找对应的老师

接口方法:

//查询所有的学生信息以及对应的老师的信息
    public List<Student> getStudent();

xml配置:

property是实体类中的名字,column是数据库中的名字

<mapper namespace="com.liu.dao.StudentMapper">
    
    <select id="getStudent" resultMap="StudentTeacher">
        select * from mybatis.student;
    </select>
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--复杂的属性 对象用association 老师是对象-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from mybatis.teacher where id = #{id};
    </select>

</mapper>

测试:

@Test
    public void testStudent(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentList = mapper.getStudent();
        for (Student student : studentList) {
            System.out.println(student);
        }
        sqlSession.close();
    }

按照结果嵌套处理

联表查询

 <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid,s.name sname,t.name tname
        from mybatis.student s,mybatis.teacher t
        where s.tid = t.id;
    </select>

    <resultMap id="StudentTeacher2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>

10、一对多的处理

比如:一个老师对应多个学生!

list集合要用ofType类型

实体类:

@Data
public class Teacher {
    private int id;
    private String name;

    //一个老师拥有多个学生
    private List<Student> students;
}
@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}

查询出来的结果:

Teacher(id=1, name=秦老师, students=[Student(id=1, name=小明, tid=1), Student(id=2, name=小红, tid=1), Student(id=3, name=小张, tid=1), Student(id=4, name=小李, tid=1), Student(id=5, name=小王, tid=1)])

按结果查询

接口配置:

//获取指定老师下的所有学生及老师的信息
    Teacher getTeacher(@Param("tid") int id);

xml配置:

<select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from mybatis.student s,mybatis.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>

测试:

public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);
        sqlSession.close();
    }

按子查询方式

@Data
public class Teacher {
    private int id;
    private String name;

    //一个老师拥有多个学生
    private List<Student> students;
}

javaType是用来指定实体类中属性的类型

offType用来指定映射到List集合中的pojo类型,泛型中的约束类型!

接口:

Teacher getTeacher2(@Param("tid") int id);

xml配置:

<select id="getTeacher2" resultMap="TeacherStudent2">
        select  * from mybatis.teacher where id = #{tid};
    </select>
    <resultMap id="TeacherStudent2" type="Teacher">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId"/>
    </resultMap>

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

测试:

public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher2(1);
        System.out.println(teacher);
        sqlSession.close();
    }

11、动态sql

动态sql就是根据不同的条件生成不同的sql语句,也就是拼接sql语句,保证sql的正确性即可

字段不一致,解决:在核心配置文件的setting下的mapUnderscoreToCamelCase驼峰命名

动态sql和JSTL类似于XML的文本处理器

CREATE TABLE `blog`(
 `id` VARCHAR(50) NOT NULL COMMENT '博客id',
 `title` VARCHAR(100) NOT NULL,
 `author` VARCHAR(30) NOT NULL,
 `create_time` DATETIME NOT NULL,
 `views`INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8

1.导包

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

2.写工具类和pojo实体类

@Data
public class Blog {
    private int id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

3.写接口和对应的Mapper

<mapper namespace="com.liu.dao.BlogMapper">
    
    <insert id="addBolg" parameterType="blog">
        insert into mybatis.blog(id, title, author, create_time, views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>

</mapper>

4.在核心配置文件中进行映射配置

如果运行测试时不成功,在maven里面clean一下!

<mappers>
    <mapper class="com.liu.dao.BlogMapper"/>
</mappers>

if

正常的sql语句:

<select id="queryBlogIF" parameterType="map">
    select * from mybatis.blog where title = #{title} and author = #{author};
</select>

用if:

<select id="queryBlogIF" parameterType="map" resultType="Blog">
        select * from mybatis.blog where 1=1
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </select>
 @Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap hashMap = new HashMap();
        hashMap.put("title","sql学习");
        hashMap.put("author","liuxiang");
        List<Blog> blogs = mapper.queryBlogIF(hashMap);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }

choose(when,ohterwise)

类似于switch语句!

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

trim(where,set)

where标签会自动判断是不是第一个and,是第一个就自动去除,不是第一个就不取除!什么都不传就自动去除where标签!

set会自动去除逗号!

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

测试:

@Test
    public void updateBlog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap hashMap = new HashMap();
        hashMap.put("title","sql学习22");
        hashMap.put("author","liuxiang");
        hashMap.put("id","9faf250d90df4c258dbdfbf59a210d94");
//        hashMap.put("views",9999);
        mapper.updateBlog(hashMap);
        sqlSession.close();
    }

所谓的动态sql本质还是sql语句,只是在sql层面执行一个逻辑代码!

foreach

SQL片段:实现代码复用

  • 最好基于单表来定义sql片段
  • 不要存在where标签
<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

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

foreach:

接口:

//查询1-3号的博客
    List<Blog> queryBlogForeach(Map map);

xml配置:

<!--
    原来sql:
    select * from mybatis.blog where 1=1 and (id=1 or id=2 or id=3)
    -->
    <select id="queryBlogForeach" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>

测试:

 @Test
    public void queryBlogForeach(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        ArrayList<Integer> ids = new ArrayList<>();
        ids.add(1);
        ids.add(2);
        map.put("ids",ids);

        List<Blog> blogs = mapper.queryBlogForeach(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
        sqlSession.close();
    }

结果:

12、缓存

12.1、简介

查询:连接数据库,耗资源!

一次查询的结果,暂存在可以取到的地方—缓存,再次查询数据的时候直接走缓存,不用访问数据库!

在这里插入图片描述

读写分离,主从复制!!

有专门的数据库供读,专门的数据库供写!

1.什么是缓存?

  • 存在内存中的临时数据
  • 将用户经常查询的数据放在缓存中,用户去查询数据就不用从磁盘上(关系型数据库)查询,从缓存中查询。从而提高了查询效率,解决高并发的性能问题!

2.为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率

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

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

12.2、Mybatis缓存

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

  • 默认情况下,只有一级缓存开启(sqlSession级别的缓存,也称为本地缓存)
  • 二级缓存需要手动开启和配置,是基于namespace级别的缓存
  • 为了提高扩展性,mybatis定义了缓存接口Cache,可以通过Cache接口来定义二级缓存
public interface Cache {
    String getId();

    void putObject(Object var1, Object var2);

    Object getObject(Object var1);

    Object removeObject(Object var1);

    void clear();

    int getSize();

    default ReadWriteLock getReadWriteLock() {
        return null;
    }
}

12.3、一级缓存

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

1.开启日志

2.测试在一个Session中查询两次相同的记录

3.查看日志输出

在这里插入图片描述

缓存失效的情况

1.查询不同的东西

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

走了两次查询!

在这里插入图片描述

3.查询不同的Mapper.xml

4.手动清理缓存

@Test
    public void queryUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserById(1);
        System.out.println(user);
//        mapper.updateUser(new User(2,"kaka","bbb"));
        sqlSession.clearCache();//手动清理缓存
        System.out.println("===============");
        User user2 = mapper.queryUserById(1);
        System.out.println(user2);

        System.out.println(user==user2);
        sqlSession.close();
    }

12.4、二级缓存

开启全局缓存:在核心配置文件中的setting下配置:

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

要启用二级缓存,只需要在要使用的Mapper映射文件中添加一行:

<cache
     eviction="FIFO" 
     flushInterval="60000"
     size="512"
     readOnly="true"/>
<!--输入输出流  60s刷新一次 最大缓存数512 -->

在这里插入图片描述

  • 二级缓存也叫全局缓存,一级缓存作用域太低了
  • 基于namespace级别的缓存,一个名称空间对应一个二级缓存
  • 工作机制:
    • 一个会话查询一条数据,这个数据就会放在当前会话的一级缓存中
    • 如果当前会话关闭了这个会话对应的一级缓存就没了,需要的是一级缓存关闭了,数据被保存在二级缓存中
    • 新的会话查询信息就可以从二级缓存中获取内容
    • 不同的mapper查出的数据会放在字节对应的缓存(map)中
<cache
     eviction="FIFO"
     flushInterval="60000"
     size="512"
     readOnly="true"/>
    <select id="queryUserById" resultType="User" useCache="true">
        select * from mybatis.user where id = #{id}
    </select>

测试:

@Test
    public void queryUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        SqlSession sqlSession2 = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserById(1);
        System.out.println(user);
        sqlSession.close();//第一个session关闭了,将数据存在二级缓存中,只用查一次数据库

        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2 = mapper2.queryUserById(1);
        System.out.println(user2);

        System.out.println(user==user2);

        sqlSession2.close();
    }

结果:

第一个session关闭了,将数据存在二级缓存中,只用查一次数据库

在这里插入图片描述

问题:无策略的时候,需要将实体类序列化,不然会报错!

public class User implements Serializable {
    private int id;
    private String name;
    private String pwd;
}

12.5、缓存原理

缓存顺序:

1.先看二级缓存

2.再看一级缓存

3.查询数据库

在这里插入图片描述

12.6、自定义缓存-ehcache

1.导包

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值