【SSM框架-MyBatis】MyBatis的基础知识、代理模式、ORM、缓存以及架构

本文详细介绍了MyBatis框架中的代理模式,包括静态代理和动态代理,特别是JDK动态代理的实现。同时,文章还探讨了MyBatis的基本概念,如动态SQL、事务管理和缓存机制,以及ORM在MyBatis中的应用。通过实例展示了如何在项目中配置和使用MyBatis,包括添加依赖、创建配置文件以及数据库操作。

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

MyBatis

代理模式

  • 代理模式的作用:

    • 控制目标对象的访问

    • 增强功能

静态代理

  • 静态代理的特点

    • 目标对象和代理对象实现同一个业务接口

    • 目标对象必须实现接口

    • 代理对象再程序运行前就已经存在

    • 能够灵活的进行目标对象的切换,却无法进行功能的灵活处理(动态代理的主要解决此问题)

  • 静态代理的实现:静态代理要求目标对象和代理对象实现同一个业务接口,代理对象中的核心功能是由目标对象来完成,代理对象负责增强功能

  • 目标对象:实现业务接口中的功能

  • 代理对象:完成除主业务之外的其他业务(实现业务接口,但业务功能必须由目标对象亲自实现)

  • 在测试时,有接口和实现类,必须使用接口指向实现类(规范)

面向接口编程

  • 类中的成员变量设计为接口,方法的形参设计为接口,方法的返回值设计为接口,调用时接口指向实现类

动态代理

  • 代理对象在程序中运行的过程中动态在内存中构建,可以灵活的进行业务功能的切换

JDK动态代理

  • JDK动态代理的特点

    • 目标对象必须实现业务接口

    • 代理对象不需要实现业务接口

    • 动态代理的对象在程序运行前不存在,在程序运行时动态的在内存中构建

    • 动态代理灵活地进行业务功能的切换

    • 本类中的方法(非接口中的方法)是不能被代理的,并不能将Proxy类型的数据转换成实现类的数据(会报出异常,类转换异常ClassCastException)

  • JDK动态代理用到的类和接口

    • 是使用现有的工具类完成JDK的动态实现

    • Proxy类

      • 是java.lang.reflect.Proxy包下的类,使用Proxy.newProxyInstance()方法专门用来生成动态代理对象

      • public static Object newProxyInstance(ClassLoader loader,//类加载器
                                Class<?>[] interfaces,//目标对象实现的所有接口
                                InvocationHandler h//类似于Agent代理对象的功能,代理的功能和目标对象的业务功能调用在这 )
                throws IllegalArgumentException
        {...}
    • Method类

      • 反射使用的类,用来进行目标对象的方法的反射调用

      • Method对象接受我们正在调用的方法,sing(),show()

      • method.invoke();-->手工调用目标方法

    • InvocationHandler接口

      • 实现代理和业务功能的,调用时使用匿名内部实现

  • 在代码中进行动态代理的实现

    • public class ProxyFactory {
          //类中的成员变量设计为接口,目标对象
          Service service;
      ​
          //传入目标对象
          public ProxyFactory(Service service) {
              this.service = service;
          }
      ​
          //返回代理对象
          public Object getService(){
              return Proxy.newProxyInstance(
                      //ClassLoader loader,类加载器,完成目标对象的加载
                      service.getClass().getClassLoader(),
                      //Class<?>[] interfaces,目标对象实现所有接口
                      service.getClass().getInterfaces(),
                      //InvocationHandler h);实现代理功能的接口,我们使用的是匿名内部实现
                      new InvocationHandler() {
                          @Override
                          public Object invoke(
                                  //创建代理对象
                                  Object proxy,
                                  //method就是目标方法sing(),show()
                                  Method method,
                                  //目标方法的参数
                                  Object[] args) throws Throwable {
      ​
                              //代理功能
                              System.out.println("预定时间...");
                              //代理功能
                              System.out.println("预定场地...");
                              //主业务功能
                              //无论传入的什么方法,都会进行调用
                              Object invoke = method.invoke(service, args);
                              //代理功能
                              System.out.println("结算费用...");
                              //目标方法的返回值
                              return invoke;
                          }
                      }
              );
          }
      }

CGLib动态代理(子类代理)

  • 通过动态的在内存中构建子类对象,重写父类的方法进行代理功能的增强,若目标对象没有实现接口,则只能通过CGLib子类代理来进行功能的增强

  • 子类代理是通过对象字节码框架ASM来实现的

  • 后期可以通过spring框架来轻松实现

  • 工具类Enhancer en = new Enhancer()完成子类代理对象的创建

  • en.setSuperclass(service.getClass())设置父类

  • en.setCallback(this(这里指的是成员变量));设置回调函数

  • en.create();创建子类对象

  • 注意:

    • 被代理的类不能被final修饰

    • 目标对象的方法如果为final/static,那么就不会拦截,即不会执行目标对象额外的业务方法

MyBatis框架

框架概述

三层架构

  • 在项目的开发中,遵循的一种形式模式:分为三层

  • 界面层:用来接受客户端的输入,调用业务逻辑层进行功能处理,返回结果给客户端,过去的servlet就是界面层的功能

  • 业务逻辑层:用来进行整个项目的业务逻辑处理,向上为界面层提供处理结果,向下对数据访问层索要数据

  • 数据访问层:专门用来进行数据库的增删改查操作,向上为业务逻辑层提供数据

  • 各层之间的调用顺序是固定的,不允许跨层访问

  • 界面层-------->业务逻辑层------>数据访问层

  • 界面层<-------业务逻辑层<------数据访问层

MyBatis框架入门

添加框架的步骤:

  • 添加依赖

  • 添加配置文件

具体步骤:

  • 创建数据库,新建表

  • 新建maven项目,选quickstart模板

  • 修改目录,添加缺失的目录,修改目录属性

  • 修改pom.xml文件,添加MyBatis的依赖,添加mysql依赖

    • <!--mybatis依赖-->
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
      </dependency>
      <!--mysql依赖-->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
      </dependency>
  • 修改pom.xml文件,添加资源文件指定

    • <!--添加资源文件的指定-->
        <build>
          <resources>
      ​
            <resource>
              <directory>src/main/java</directory>
              <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
              </includes>
            </resource>
      ​
            <resource>
              <directory>src/main/resources</directory>
              <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
              </includes>
            </resource>
      ​
          </resources>
        </build>
  • 在idea中添加数据库的可视化

  • 添加jdbc.properties属性文件(数据库的配置)

  • 添加SqlMapConfig.xml文件(系统核心配置文件),MyBatis的核心配置文件

    • 头文档文件位置

    • <?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>
      <!--
          读取属性文件
          属性:
              resource:从resource目录下寻找指定名称的properties文件
              url:使用绝对路径寻找加载文件
      -->
          <properties resource="jdbc.properties"></properties>
      <!--
          SqlMapConfig.xml文件配置
          配置数据库环境变量(数据库连接配置)
          default:在environments标签中对指定id的environment进行配置
      ​
      -->
          <environments default="development">
      <!--        测试环境变量-->
      <!--        environment中的id就是为了给environments中的default使用-->
              <environment id="development">
      <!--
                  配置事务管理器
                      type:是为了指定事务管理的方式
                          JDBC:事务的管理通过程序员来完成
                          MANAGED:事务的管理通过框架(spring)来完成
      -->
                  <transactionManager type="JDBC"></transactionManager>
      <!--
                  配置数据源
                  type:指定不同的配置方式
                      JNDI:java命令目录接口,在服务端进行数据库连接池的管理
                      POOLED:使用数据库连接池
                      UNPOOLED:不使用数据库连接池
      -->
                  <dataSource type="POOLED">
                      <property name="driver" value="${jdbc.driverClassName}"/>
                      <property name="url" value="${jdbc.url}"/>
                      <property name="username" value="${jdbc.username}"/>
                      <property name="password" value="${jdbc.password}"/>
                  </dataSource>
              </environment>
      <!--        家庭环境变量-->
      <!--        <environment id="home">-->
      <!--            <transactionManager type=""></transactionManager>-->
      <!--            <dataSource type=""></dataSource>-->
      <!--        </environment>-->
      <!--&lt;!&ndash;        上线环境变量&ndash;&gt;-->
      <!--        <environment id="online">-->
      <!--            <transactionManager type=""></transactionManager>-->
      <!--            <dataSource type=""></dataSource>-->
      <!--        </environment>-->
          </environments>
          <!--注册mapper.xml文件
          resource:从resource目录下寻找指定名称的mapper.xml文件
              url:使用绝对路径寻找mapper.xml文件
              class:动态代理方式下的注册
      -->
          <mappers>
              <mapper resource="StudentMapper.xml"></mapper>
          </mappers>
      </configuration>
  • 创建实体类Student,用来封装数据

  • 添加完成学生表的增删改查的功能的StudentMapper.xml文件

    • <?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标签是整个xml的结束和开始-->
      <!--当完成mapper的编写之后需要在SqlMapConfig中对mapper进行注册-->
      <!--namespace:指定命名空间(相当于包名),用来区分不同mapper.xml文件中相同的id属性-->
      <mapper namespace="zar">
      <!--
          完成查询所有学生的功能
          resultType:若没有参数,则指定返回数据的集的类型,如果是集合,则必须是泛型的类型
          parameterType:若有参数,则通过他来指定参数的类型
      -->
          <select id="getAll" resultType="com.wsh.bean.Student">
              select id,name,email,age from student
          </select>
      <!--
          完成根据id主键对学生的查询
          getById(Integer id);
      -->
          <select id="getById" parameterType="int" resultType="com.wsh.bean.Student">
              select id,name,email,age from student where id = #{id}
          </select>
      <!--
          按学生名称模糊查询
          List<Student> getByName(String name)
      -->
          <select id="getByName" parameterType="string" resultType="com.wsh.bean.Student">
              select id,name,email,age
              from student
              where name like '%${name}%'
          </select>
      <!--
          增加学生
          int insert(Student stu)
          实体类属性:
              private Integer id;
              private String name;
              private String email;
              private Integer age;
          占位符上可以使用实体类属性名称使用
      -->
          <insert id="insert" parameterType="com.wsh.bean.Student">
              insert into student (name,email,age) values (#{name},#{email},#{age})
          </insert>
      <!--
          根据主键来删除
          int delete (Integer id)
      -->
          <delete id="delete" parameterType="int">
              delete
              from student
              where id = #{id};
          </delete>
      <!--
          根据主键修改更新
          int update(Student stu)
          实体类属性:
              private Integer id;
              private String name;
              private String email;
              private Integer age;
          占位符上可以使用实体类属性名称使用
      -->
          <update id="update" parameterType="com.wsh.bean.Student">
              update student set name = #{name},email = #{email},age = #{age}
              where id = #{id}
          </update>
      </mapper>
  • 创建测试类进行测试

    • public class Test {
      ​
          @org.junit.Test
          public void testGetAll() throws IOException {
      ​
              //使用文件流读取核心配置文件SqlMapConfig.xml文件
              InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
              //创建SqlSessionFactory工厂
              SqlSessionFactory factoryBuilder = new SqlSessionFactoryBuilder().build(in);
              //取出sqlSession对象
              SqlSession sqlSession = factoryBuilder.openSession();
              //完成查询操作
              List<Student> students = sqlSession.selectList("zar.getAll");
              students.forEach(student -> System.out.println(student));
              //关闭sqlSession
              sqlSession.close();
      ​
          }
      ​
          @org.junit.Test
          public void testGetById() throws IOException {
      ​
              InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
              SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
              SqlSession sqlSession = factory.openSession();
              Student student = sqlSession.selectOne("zar.getById",1);
              System.out.println(student);
              sqlSession.close();
      ​
          }
      ​
          @org.junit.Test
          public void testGetByName() throws IOException {
      ​
              InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
              SqlSessionFactory factory = new  SqlSessionFactoryBuilder().build(in);
              SqlSession sqlSession = factory.openSession();
              List<Student> students = sqlSession.selectList("zar.getByName","张三");
              students.forEach(student -> System.out.println(student));
              sqlSession.close();
      ​
          }
      ​
          @org.junit.Test
          public void testInsert()throws IOException{
              InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
              SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
              SqlSession sqlSession = factory.openSession();
              Student student = new Student();
              student.setName("赵六");
              student.setId(6);
              student.setEmail("zhaoliu@qq.com");
              student.setAge(34);
              sqlSession.insert("zar.insert",student);
              //在数据库的增删改之后必须手动的提交事务
              sqlSession.commit();
              sqlSession.close();
          }
      ​
          @org.junit.Test
          public void testDelete() throws IOException {
              InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
              SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
              SqlSession sqlSession = factory.openSession();
              sqlSession.delete("zar.delete",2);
              sqlSession.commit();
              sqlSession.close();
          }
      ​
          @org.junit.Test
          public void testUpdate() throws IOException {
              InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
              SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
              SqlSession sqlSession = factory.openSession();
              sqlSession.update("zar.update",new Student(3,"万三","wansan@126.com",23));
              sqlSession.commit();
              sqlSession.close();
          }
      ​
      }

MyBatis对象分析

  • Resource类

    • 就是解析SqlMapConfig.xml文件,创建出相应的对象

    • InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

  • SqlSessionFactory接口

    • 使用快捷键Ctrl+H查看本接口的子接口及其实现类

    • DefaultSqlSessionFactory是本接口的实现类

    • SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

  • SqlSession接口

    • DefaultSqlSession是本接口的实现类

    • SqlSession sqlSession = factory.openSession();

简化Test测试类

  • public class Test {
    ​
        SqlSession sqlSession;
        
        @Before
        public void openSqlSession() throws IOException {
            //使用文件流读取核心配置文件SqlMapConfig.xml文件
            InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //创建SqlSessionFactory工厂
            SqlSessionFactory factoryBuilder = new SqlSessionFactoryBuilder().build(in);
            //取出sqlSession对象
            sqlSession = factoryBuilder.openSession();
        }
        
        @After
        public void closeSqlSession(){
            //关闭sqlSession
            sqlSession.close();
        }
    ​
        @org.junit.Test
        public void testGetAll() throws IOException {
            //完成查询操作
            List<Student> students = sqlSession.selectList("zar.getAll");
            students.forEach(student -> System.out.println(student));
        }
    ​
        @org.junit.Test
        public void testGetById(){
            Student student = sqlSession.selectOne("zar.getById",1);
            System.out.println(student);
        }
    ​
        @org.junit.Test
        public void testGetByName(){
            List<Student> students = sqlSession.selectList("zar.getByName","张三");
            students.forEach(student -> System.out.println(student));
        }
    ​
        @org.junit.Test
        public void testInsert(){
            Student student = new Student();
            student.setName("赵六");
            student.setId(6);
            student.setEmail("zhaoliu@qq.com");
            student.setAge(34);
            sqlSession.insert("zar.insert",student);
            //在数据库的增删改之后必须手动的提交事务
            sqlSession.commit();
        }
    ​
        @org.junit.Test
        public void testDelete(){
            sqlSession.delete("zar.delete",2);
            sqlSession.commit();
        }
    ​
        @org.junit.Test
        public void testUpdate(){
            sqlSession.update("zar.update",new Student(3,"万三","wansan@126.com",23));
            sqlSession.commit();
        }
    ​
    }

关于对Mapper.xml文件的优化

  • 首先在SqlMapConfig.xml中进行typeAlias的配置

  • <!--    完成实体类的别名注册-->
        <typeAliases>
    <!--    单个实体类的别名注册-->
    <!--        <typeAlias type="com.wsh.bean.Student" alias="student"></typeAlias>-->
    <!--    批量的对实体类进行注册
            别名是类名的驼峰命名(规范)
    -->
            <package name="com.wsh.bean"/>
        </typeAliases>
  • 配置完成之后,Mapper.xml文件中的parameterType="com.wsh.bean.Student"-------->parameterType="student"(类名进行驼峰命名后的名字),同理resultType也是同样的处理方式

  • parameterType="student"
    resultType="student"

设置日志的输出

  • <settings>
        	<!--固定格式-->
            <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

MyBatis框架动态代理

  • 在三层架构中,业务逻辑层要通过接口访问数据访问层的功能,则需要动态代理来实现

Mapper.xml文件的优化

  • <mappers>
    <!--        单个注册-->
    <!--        <mapper class="com.wsh.mapper.UsersMapper"></mapper>-->
    <!--        批量注册-->
            <package name="com.wsh.mapper"/>
        </mappers>

#{}和${}的区别

  • #{}是占位符

    • 传参一般都会使用#{},他的底层使用的是PreparedStatement对象,是安全的数据库访问,防止sql注入

    • #{}内容如何写,看parameterType参数的类型,两种情况

      • 如果parameterType的类型是简单的类型)(八种基本类型(封装)+String),则#{}可以写任意的内容

      • 若parameterType的类型是实体类的类型,则#{}里只能是类中成员变量的名称,而且区分大小写

  • ${}字符串拼接或字符串替换

    • 字符串拼接一般用于模糊查询中,建议少用,因为有sql注入的风险

      • ${}内容的书写也有两种情况:

        • 若parameterType的类型是简单类型,则在3.5.1及其以下版本只写value

        • 若parameterType的类型是实体类的类型,则${}里只能是类中成员变量的名称

    • 字符串替换

      • //模糊查询用户名和地址,接口中编写的内容
            List<User> getByNameOrAddress(@Param("columnName") String columnName,@Param("columnValue") String columnValue);
      • <!--    //模糊查询用户名和地址
                //当遇到两个及两个以上的参数则不需要写parameterType
                //在sql语句中可以使用注解的名称
                List<User> getByNameOrAddress(
        		@Param是为了在sql语句中使用该名称
                @Param("columnName") String columnName,
                @Param("columnValue") String columnValue);
            -->
            <select id="getByNameOrAddress" resultType="user">
                select id,username,brithday,sex,address
                from users
                where ${columnName} like concat('%',#{columnValue},'%')
            </select>

关于模糊查询的优化

  • 使用#{}来防止sql的注入

  • <select id="getByName" parameterType="string" resultType="user">
            select id,username,brithday,sex,address
            from users
            where username like concat('%',#{name},'%')
        </select>
  • 注意:

    • 在使用concat()方法时字符串的拼接需要使用","而不是"+"。

返回主键的业务需求

  •     <selectKey keyProperty="id" resultType="int" order="AFTER">
            select last_insert_id()
        </selectKey>
  • 在插入语句结束之后,使用该方法可以返回加入用户的自增的主键(id)值

  • <selectKey>参数的含义:

    • keyProperty:user对象使用那个属性来接取返回的主键值

    • resultType:返回的主键值的类型

    • order:该方法是在插入语句之前执行还是在插入语句之后执行

UUID的使用

  • 是一个全球唯一的字符串,由36个字母和中划线组成

  • //使用UUID创建一个随机的UUID
    UUID uuid = UUID.randomUUID();
    //2cc701c4-4253-4e69-b9f7-e165ef5c48d5
    //d2f82bec7f5c
    System.out.println(uuid.toString().replaceAll("-","").substring(20));
  • 可以使用对字符串的操作在控制UUID的输出

动态sql

什么是动态sql

  • 可以定义代码片段,可以进行逻辑的判断,可以进行循环处理(批量处理),使得条件判断更加简单

动态sql的标签

  • <sql>:用来定义代码片段,可以将所有的列名,或复杂的条件定义为代码片段,供使用时调用

  • <include>:用来引用<sql>定义的代码片段

  • 例子:

    • <!--    自定义代码片段-->
          <sql id="allColumns">
              id,username,brithday,sex,address
          </sql>
          <select id="getAll" resultType="user">
          <!--对于sql标签定义的代码片段进行引用-->
              select <include refid="allColumns"></include>
              from users
          </select>
  • <if>:进行条件的判断

  • <where>:进行多条件的拼接,在查询,删除,更新中使用

  • 例子:

    • <!--    //按指定的条件进行多条件的查询
          List<User> getByCondition(User user);
          条件就是判断实体类中的成员变量是否有值来决定是否添加条件
          -->
          <select id="getByCondition" parameterType="user" resultType="user">
              select <include refid="allColumns"></include>
              from users
              <where>
                  <if test="userName != null and userName != ''">
                      and username like concat('%',#{userName},'%')
                  </if>
                  <if test="birthday != null">
                      and birthday = #{birthday}
                  </if>
                  <if test="sex != null and sex != ''">
                      and sex = #{sex}
                  </if>
                  <if test="address != null and address != ''">
                      and address like concat('%',#{address},'%')
                  </if>
              </where>
          </select>
      @org.junit.Test
          public void testGetByCondition() throws ParseException {
      //    //按指定的条件进行多条件的查询
      //    List<User> getByCondition(User user);
              User user = new User();
              user.setSex("1");
              user.setBrithday(sf.parse("2001-01-01"));
              List<User> users = usersMapper.getByCondition(user);
              users.forEach(user1 -> System.out.println(user1));
          }
  • <set>:有选择的进行更新,至少更新一列

  • 例子:

    • <!--    //有选择的进行更新
          int updateBySet(User user);
          -->
          <update id="updateBySet" parameterType="user">
              update users
              <set>
                  <if test="userName != null and userName!=''">
                      username = #{userName},
                  </if>
                  <if test="brithday!=null">
                      brithday = #{brithday},
                  </if>
                  <if test="sex!=null and sex!=''">
                      sex = #{sex},
                  </if>
                  <if test="address!=null and address!=''">
                      address = #{address},
                  </if>
              </set>
      where id = #{id}
          </update>
      /**
           * //有选择的进行更新
           *     int updateBySet(User user);
           */
          @org.junit.Test
          public void testUpdateBySet() throws ParseException {
              User u = new User();
              u.setId(7);
              u.setUserName("王仕豪");
              u.setBrithday(sf.parse("2001-06-22"));
              u.setSex("1");
              u.setAddress("杭州");
              int num = usersMapper.updateBySet(u);
              System.out.println(num);
              sqlSession.commit();
          }
  • <foreach>:主要用来对集合和数组进行遍历

    • 拥有参数:

      • collection:选择类型(list、array、map)

      • item:命名(临时变量)

      • separator:多个值或语句之间的分隔符

      • open:前括号(循环外)

      • close:后括号(循环外)

指定参数位置

  • 如果入参是多个,可以通过指定参数位置进行传参,是实体类包含不了的条件,实体类只能封装住成员变量的条件,如果某个成员变量要有区间范围内的判断,或者有两个值进行处理,则实体类包不住

  • 例子:指定日期范围内的用户

    • <!--    //指定日期范围进行查询
          List<User> getByBrithday(Date begin,Date end);
          -->
          <select id="getByBrithday" resultType="user">
              select <include refid="allColumns"></include>
              from users
              where brithday between #{arg0} and #{arg1}
          </select>
      @org.junit.Test
          public void testGetByBrithday() throws ParseException {
              Date begin = sf.parse("1999-02-22");
              Date end = sf.parse("2002-01-01");
              List<User> users = usersMapper.getByBrithday(begin,end);
              users.forEach(user -> System.out.println(user));
          }

入参是Map(重点)

  • 当入参超过一个以上,推荐使用Map封装查询条件,更有语义,更加明确

  • 例子:

    • <!--    //使用Map对查询条件进行封装
          List<User> getByMap();
          #{brithdayBegin}就相当于map中的key
          -->
          <select id="getByMap" resultType="user">
              select <include refid="allColumns"></include>
              from users
              where brithday between #{brithdayBegin} and #{brithdayEnd}
          </select>
      @org.junit.Test
          public void testGetByMap() throws ParseException {
              Date brithdayBegin = sf.parse("1999-02-22");
              Date brithdayEnd = sf.parse("2002-01-01");
              Map map = new HashMap();
              map.put("brithdayBegin",brithdayBegin);
              map.put("brithdayEnd",brithdayEnd);
              List<User> users = usersMapper.getByMap(map);
              users.forEach(user -> System.out.println(user));
          }
  • 返回值是map

    • 如果返回的数据实体类无法包含,可以使用map返回多张表中的若干数据,返回后这些数据之间没有任何关系,就是Object类型

    • 返回的map的key就是列名或是别名

  • 当数据库列名和实体类名称不一致时,推荐使用resultMap完成手工映射(id是select标签中的resultMap自定义的名称,type是注册的实体类的名称)

    • property指的是实体类中的名称

    • column指的是数据库中的名称

  • 表的关联关系

关联关系是有方向的

  • 一对多关联

    • <!--//根据客户id获取该客户下的所有订单
          Customer getById(Integer id);
          //customer三个属性
          private Integer id;
          private String name;
          private Integer age;
      
          //该客户名下的所有订单
          List<Orders> ordersList;
          -->
          <resultMap id="customermap" type="customer">
      <!--        主键绑定-->
              <id property="id" column="cid"></id>
      <!--        非主键绑定-->
              <result property="name" column="name"></result>
              <result property="age" column="age"></result>
      <!--        多出来的额外设定
          private Integer id;
          private Integer orderNumber;
          private Double orderPrice;
      -->
              <collection property="ordersList" ofType="orders">
                  <id property="id" column="oid"></id>
                  <result property="orderNumber" column="orderNumber"></result>
                  <result property="orderPrice" column="orderPrice"></result>
              </collection>
          </resultMap>
          <select id="getById" parameterType="int" resultMap="customermap">
              select c.id cid,name,age,o.id oid,orderNumber,orderPrice,customer_id
              from customer c left join orders o on c.id = o.customer_id
              where c.id = #{id}
          </select>
      @org.junit.Test
          public void testGetById(){
              Customer customer = customerMapper.getById(3);
              System.out.println(customer);
          }
  • 多对一关联

    • <mapper namespace="com.wsh.mapper.OrdersMapper">
      <!--    //根据主键查订单,并返回客户信息
          Orders getById(Integer id);
          private Integer id;
          private Integer orderNumber;
          private Double orderPrice;
      
          //关联订单的客户信息
          private Customer customer;
          -->
          <resultMap id="ordersmap" type="orders">
              <id property="id" column="oid"></id>
              <result property="orderNumber" column="orderNumber"></result>
              <result property="orderPrice" column="orderPrice"></result>
      <!--        对于成员变量中的类进行绑定-->
              <association property="customer" javaType="customer">
                  <id property="id" column="cid"></id>
                  <result property="name" column="name"></result>
                  <result property="age" column="age"></result>
              </association>
          </resultMap>
          <select id="getById" parameterType="int" resultMap="ordersmap">
              select o.id oid,orderNumber,orderPrice,customer_id,c.id cid,name,age
              from orders o inner join customer c on o.customer_id = c.id
              where o.id = #{id}
          </select>
      </mapper>
    • @org.junit.Test
          public void testGetOrdersById(){
              Orders orders = ordersMapper.getById(11);
              System.out.println(orders);
          }
  • 多对多关联

  • 一对一关联

事务

  • 多个操作同时完成,或是同时失败成为事务(保证数据库不是出现垃圾数据)

事务的四个特点

  • 一致性

  • 持久性

  • 原子性

  • 隔离性

在MyBatis框架中设置事务

  • <transactionManager type="JDBC"></transactionManager>
    <!--程序员自己手动提交-->

    可以设置为自动提交

  • 默认是手动提交(false)

  • sqlSession = factoryBuilder.openSession();
  • sqlSession = factoryBuilder.openSession(true);===>为自动提交,在增删改后不需要commit();

  • MyBatis支持两种事务管理器类型:JDBC和MANAGED,MANAGED是容器管理事务的处理方式(交给容器自动提交)

缓存(面试)

  • MyBatis框架有两层缓存(一级缓存,二级缓存),会默认的开启一级缓存

  • 缓存就是为了提高效率

使用缓存后查询的流程

  • 查询时,先到缓存中查找,若没有则查询数据库,放入缓存中一份,再返回客户端,下次再进行查询的时候直接从缓存中返回数据,不再访问数据库,如果数据库中发生commit()操作,则会清空全部缓存

  • 当数据库发生改变时,会清空缓存,再次进行访问会直接到数据库中进行查询

缓存的作用域

  • 一级缓存使用的是SqlSession的作用域,同一个SqlSession共享一级缓存的数据

  • 二级缓存使用的是Mapper的作用域,不同的SqlSession只要访问同一个mapper.xml文件,就会共享二级缓存中的数据

二级缓存的开启步骤

ORM

  • ORM就是对象关系映射

  • MyBatis框架是一个十分优秀的持久层框架

  • java语言中以对象的方式操作数据,存到数据库中是以表的形式来存储,对象中的成员变量与表中的列之间的数据互换称为映射,整个这一套操作就是ORM

  • 持久化的操作:将对象保存到关系型数据库中,将关系型数据库中的数据读取出来以对象的形式封装。

源码追踪

  • 在SqlMapConfig中写的标签都会注册到configuration中

Mapper的注册方式

  • 四种,分别为

    • package(优先级最高的)

    • resource(第二)

    • url(第三)

    • class(第四)

SqlSession解析

  • alt+7:列出本接口或类中的全部成员变量

  • ctrl+h:列出本接口的实现类,或是本类中的所有子类,改变按钮可以得到父类

  • 在之前的步骤中存入的configuration中的数据,都会在SqlSession中取出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值