框架技术及MyBatis

  • 框架技术
  • 优势
  • 主流框架
  • MyBatis框架
  • Spring框架
  • Spring MVC框架
  • Hibernate框架
  • Struts框架
  • 持久化
  • ORM
  • MyBatis框架实现数据库操作
  • 概念
  • 使用步骤
  • 优点
  • 缺点
  • 基本要素
  • 核心接口和类
  • 核心配置文件
  • SQL映射文件
  • MyBatisUtil
  • SQL映射文件
  • 顶级文件
  • 条件查询
  • 单一条件查询
  • 多条件查询
  • 结果映射
  • 使用resultMap元素自定义结果映射
  • 嵌套结果映射
  • resultMap的自动映射行为
  • MyBatis缓存
  • 一级缓存
  • 二级缓存
  • cache元素中各种属性的作用
  • MyBatis框架动态SQL处理多参数查询
  • MyBatis框架动态SQL的常用标签
  • if标签
  • where标签
  • choose标签
  • foreach标签
  • set标签
  • trim标签


框架技术

框架(framework)是一种经过经验,具有特定的半成品软件

优势

  • 无需考虑开发中的公共问题,基础问题,框架已经做好了实现
  • 可以专注于项目的业务逻辑设计,提升开发效率,并且有助于提高核心业务的开发质量
  • 项目架构统一,便于沟通协作,便于修改维护
  • 框架汇集了软件分析,设计,实现等多方面的有序经验,可以帮助开发者快速构建优美,运行稳定且性能优良的高质量应用

主流框架

MyBatis框架
  • 基于ORM的优秀的持久化框架
  • 半自动化的ORM实现
  • SQL映射器机制
  • 使用XML或注解将Java中的接口和POJO与SQL语句结合在一起
  • DAO层
Spring框架
  • 开源的,轻量级的企业级应用框架
  • 声明式事务
  • 覆盖了Java EE技术的方方面面
  • 依赖注入容器和AOP实现存在
Spring MVC框架
  • 结构清晰的MVC Model2实现
  • 基于Servlet API构建的Web框架
  • 具有高度的可配置性,可以方便地进行定制化开发,所支持的视图技术非常全面
Hibernate框架
  • 持久化框架,是一个典型的ORM解决方案
  • 简化数据库操作
  • DAO层
Struts框架
  • 基于Web的MVC框架
  • 拦截器

持久化

将内存中有用的数据以某种技术保存起来,内存中的数据模型和存储模型之间的转换

ORM

ORM(Object Relational Mapping)是一种关系型数据库和面向对象编程语言之间进行数据转换的编程技术

MyBatis框架实现数据库操作

概念

  • 实体类和SQL语句之间建立映射关系
  • 支持普通SQL语句,存储过程映射,并提供了其他高级的映射用法

使用步骤

  • 下载所需文件
  • mysql-connector-java-8.0.19.jar
  • mybatis-3.5.1.jar
  • 创建项目并添加所需jar文件
  • 编写MyBatis框架的核心配置文件
    mybatis-config.xml文件
<?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>
    <!-- 配置MyBatis框架的运行环境-->
    <environments default="dev">
        <environment id="dev">
            <!--事务管理器-->
            <transactionManager type="JDBC"/>
            <!--数据源配置-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/onlineauctionsystem?useUnicode=true;characterEncoding=UTF-8;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="ok"/>
            </dataSource>
        </environment>
    </environments>

</configuration>
  • 创建实体类
  • 创建Mapper接口
  • 创建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="dao.user.UserMapper">
    <select id="getCount" resultType="int">
        SELECT COUNT(1) FROM t_sys_user
    </select>
</mapper>
  • 编写测试类
package test;
import dao.user.UserMapper;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class UserTest {
    @Test
   public void getCountTest(){
        try {
            //1.读取配置文件
            InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
            //2.创建 SqlSessionFactory 工厂
            SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
            //3.开启SqlSession
            SqlSession sqlSession = build.openSession();
            //4.获取mapper接口 并且调用方法 执行SQL语句
            Integer result = sqlSession.getMapper(UserMapper.class).getCount();
            //5.处理结果
            System.out.println(result);
            //6.关闭SqlSession
            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

优点

  • 基于原生SQL,使用灵活,不会对数据库的现有设计产生影响
  • SQL语句通过XML文件进行维护,与程序代码解耦,便于统一管理和优化
  • 支持编写动态SQL语句,与应用代码解耦
  • 支持Java对象属性与查询结果集字段的映射(ORM)

缺点

  • SQL语句的编写工作量较大,且对开发人员的SQL使用经验有一定的要求
  • 原生SQL语句依赖特定的数据库产品,导致数据库移植性差,不能方便的更换数据库

基本要素

核心接口和类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qIIvMj63-1618906916633)(resources/image-20210107095901081.png)]

核心配置文件
<?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>
    <!--加载外部配置文件-->
    <properties resource="database.properties">
        <!--如果外部配置文件没有此属性,则使用该属性,否则使用外部配置的属性-->
        <property name="password" value="ok"/>
    </properties>

    <!--系统设置-->
    <settings>
        <!--日志实现-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>

    <!--别名配置-->
    <typeAliases>
        <!--<typeAlias type="pojo.SysUser" alias="SysUser"/>-->
        <package name="pojo"/>
    </typeAliases>
    
    <!-- 配置MyBatis框架的运行环境-->
    <environments default="dev">
        <environment id="dev">
            <!--事务管理器-->
            <transactionManager type="JDBC"/>
            <!--数据源配置-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/cvs_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    
    <!-- 注册Mapper-->
    <mappers>
        <mapper resource="dao/user/UserMapper.xml"></mapper>
    </mappers>
</configuration>
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="dao.user.UserMapper">
    <!--查询用户总数-->
    <select id="getCount" resultType="int">
        SELECT COUNT(1) FROM t_sys_user
    </select>

    <!--查询用户列表-->
    <select id="getList" resultType="SysUser">
        SELECT * FROM t_sys_user
    </select>

</mapper>

MyBatisUtil

package 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 build;

    static {
        try {
            //1.读取配置文件
            InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
            //2.创建 SqlSessionFactory 工厂
            SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建SqlSession
     * @return
     */
    public static SqlSession createSqlSession(){
        if (null == build){
           throw new RuntimeException("请先创建 SqlSessionfactory工厂");
        }
        return build.openSession();
    }

    /**
     * 关闭SqlSesion
     * @param sqlSession
     */
    public static void closeSqlSession(SqlSession sqlSession){
        if (null !=sqlSession){
            sqlSession.close();
        }
    }

}

SQL映射文件

实现了SQL语句与代码解耦,可以最大限度地实现对SQL语句的灵活管理,方便SQL调优以保证性能

顶级文件
  • mapper
    SQL映射文件的根元素,只有一个属性namespace,用于区分不同的mapper,必须全局唯一
  • cache
    为给定命名空间配置缓存
  • cache-ref
    引用其他命名空间的缓存配置
  • resultMap
    用来描述查询结果集中的字段和Java实体类属性的对应关系
  • sql
    定义可重用的SQL语句块,可以在其他语句映射中引用,提高编写和维护SQL语句的效率
  • insert
    映射insert语句
  • update
    映射update语句
  • delete
    映射delete语句
  • select
    映射select语句

条件查询

单一条件查询
  1. 在接口中添加方法
List<SysUser> getUserByName(String name);
  1. 添加SQL映射
<!--根据用户名进行模糊查询-->
    <select id="getUserByName" resultType="SysUser" parameterType="String">
        SELECT * FROM t_sys_user WHERE realName LIKE CONCAT('%',#{name},'%')
    </select>
  1. 测试
List<SysUser> users = sqlSession.getMapper(UserMapper.class).getUserByName("李");
          for (SysUser su:users) {
          System.out.println(su.getRealName());
          }
多条件查询
  • 将查询条件封装成Java对象作为入参
  • 在接口中添加方法
List<SysUser> getUserByNameRoleId(SysUser sysUser);
  • 添加SQL映射
<select id="getUserByNameRoleId" resultType="SysUser" parameterType="SysUser">
        SELECT * FROM t_sys_user WHERE realName LIKE CONCAT('%',#{realName},'%') AND roleId=#{roleId}
    </select>
  • 测试
SysUser sysUser = new SysUser();
            sysUser.getRealName();
            sysUser.setRoleId(2);
           List<SysUser> users = sqlSession.getMapper(UserMapper.class).getUserByNameRoleId(sysUser);
  • 将查询条件封装称Map对象作为入参
  • 在接口中添加方法
List<SysUser> getUserByNameRoleId2(Map<String,Object> map);
  • 添加SQL映射
<select id="getUserByNameRoleId2" resultType="SysUser" parameterType="Map">
        SELECT * FROM t_sys_user WHERE realName LIKE CONCAT('%',#{realName},'%') AND roleId=#{roleId}
    </select>
  • 测试
Map<String,Object> map=new HashMap<>();
            map.put("realName","李");
            map.put("roleId",2);
           List<SysUser> users = sqlSession.getMapper(UserMapper.class).getUserByNameRoleId2(map);
  • 使用@Param注册实现多参数入参
  • 在接口中添加方法
List<SysUser> getUserByNameRoleId3(@Param("realName") String realName,@Param("roleId") Integer roleId);
  • 添加SQL映射
<select id="getUserByNameRoleId3" resultType="SysUser">
        SELECT * FROM t_sys_user WHERE realName LIKE CONCAT('%',#{realName},'%') AND roleId=#{roleId}
    </select>
  • 测试
List<SysUser> users = sqlSession.getMapper(UserMapper.class).getUserByNameRoleId3("李",2)

结果映射

使用resultMap元素自定义结果映射
<resultMap id="SysUserResultMap" type="SysUser">
        <id property="id" column="id"/>
        <result property="name" column="realName"/>
    </resultMap>
嵌套结果映射
  • association
    用来处理has-one类型的关系,一对一的关系
<resultMap id="SysUserResultMap" type="SysUser">
        <id property="id" column="id"/>
        <association property="role" javaType="SysRole">
            <result property="roleName"  column="roleName"/>
        </association>
</resutMap>
  • collection
    用来处理一对多的关系
<resultMap id="SupplierResultMap" type="Supplier">
        <id property="id" column="id"/>
        <collection property="storageRecord" ofType="StorageRecord"/>
    </resultMap>

<select id="getListById" parameterType="int" resultMap="SupplierResultMap">
    SELECT s.id,s.supName,r.totalAmount,r.goodsName FROM  t_supplier s,t_storage_record r WHERE r.supplierId=s.id AND s.id=#{id}
    </select>

resultMap的自动映射行为

  • NONE
  • 禁用自动映射,仅为手工映射的属性赋值
  • PARTIAL
  • 这是默认行为,对于灭有嵌套映射的resultMap使用自动映射,而对于有嵌套映射的resultMap不使用自动映射,仅为手工映射的属性赋值
  • FULL
  • 全部使用自动映射,即使有嵌套映射的resultMap也会使用自动映射


MyBatis缓存

一级缓存

MyBatis框架的一级缓存市基于PerpetualCache的HashMap本地缓存,默认是SqlSession级别的缓存,在SqlSession的一个生命周期内有效。当SqlSession关闭后,该SqlSession中所有的以及缓存会被清空。MyBatis框架的一级缓存默认是开启的。

二级缓存

二级缓存是SqlSessionfactory级别的,缓存中的数据被所有SqlSession共享。MyBatis框架的二级缓存是默认关闭的,使用时需要在MyBatis框架的核心配置中设置开启

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

在Sql映射文件中配置缓存

<mapper namespace="dao.user.UserMapper">
    <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
</mapper>
cache元素中各种属性的作用
  • eviction–选择缓存回收策略
  • LRU – 这是默认选项,最近最少回收,移除最长时间不被使用的缓存对象
  • FIFO – 先进先出,按照对象进入缓存的顺序来移除他们
  • SOFT – 软引用,移除基于垃圾回收器状态和软引用规则的对象
  • WEAK – 弱引用,更积极地移除基于垃圾回收器和弱引用规则地对象
  • flushInterval – 设定缓存刷新间隔,以毫秒为单位设定缓存多长时间自动刷新一次,默认不自动刷新
  • size – 设定缓存数据是否只读,默认false,表示缓存数据会用于读写操作,MyBatis框架会返回缓存对象的副本以避免脏读;true表示缓存数据只用于读操作,MyBatis框架会为所有从缓存中获取数据的操作返回缓存对象的相同实例,以获得更好的性能

MyBatis框架动态SQL处理多参数查询

MyBatis框架动态SQL的常用标签

标签

说明

if

条件判断

where

为SQL语句动态添加where关键字

choose

条件判断,这是一个组合标签,需要与when,otherwise标签搭配使用,可实现与java中的switch语句类似的功能

foreach

以便利的方式处理集合类型

set

为SQL语句动态添加set关键字,实现动态实现数据更新功能

trim

对SQL语句进行格式化处理,添加或移除前后缀

if标签

<if  test="条件判断,返回true或fslse">
	SQL语句
</if>
<if test="realName!='' and realName!=nul">AND realName LIKE CONCAT('%',#{realName},'%')</if>

where标签

<where>
    <if  test="条件判断,返回true或fslse">
      SQL语句
    </if>
		.....
</where>
<where>
            <if test="realName!='' and realName!=nul">AND realName LIKE CONCAT('%',#{realName},'%')</if>
            <if test="roleId!=0 and roleId!=nul">AND roleId=#{roleId}</if>
        </where>

choose标签

<choose>
	<when test="条件判断,返回true或false">
		SQL语句...
	</when>
	<when test="条件判断,返回true或false">
		SQL语句...
	</when>
	......
	<otherwise>
		SQL语句...
	</otherwise>
</choose>
<where>
  	<choose>
       <when test="realName!='' and realName!=nul">
       		realName LIKE CONCAT('%',#{realName},'%')
       </when>
       <when test="roleId!=0 and roleId!=nul">
       		roleId=#{roleId}
       </when>
    </choose>
 </where>

当程序执行到choose标签时,只会进入test属性判断为true的第一个when标签,执行之后便会跳出choose变迁,如果所有when标签的test属性判断都为false,则进入otherwise标签

foreach标签

<foreach  collection="参数名称" item=“元素别名” open=“(” separator="," close=")" index=“当前元素位置下标”>
	#{元素别名}
</foreach>
  • item:遍历数组时,为数组或List集合中的元素起的别名
  • open:起始位置的拼接字符,表示in语句以"("括号开始
  • close:结束位置的拼接字符,表示in语句以")"括号结束
  • separator:元素之间的链接符,表示in语句中的元素以","逗号链接
  • collection:参数名称,当参数为数组类型时,默认参数名为array。当参数类型为List集合时,默认参数名为list。当参数类型为Map时,参数名为Map中集合元素所在键值对的key
<foreach collection="ids" item="id" open="(" separator="," close=")">
     #{id}
 </foreach>
<!--(1,2,3,4,5);-->

set标签

<set>
	<if test="条件判断">
		SQl语句....
	</if>
</set>
UPDATE
  t_sys_user
<set>
    <if test=" account !=null"> account = #{account},</if>
    <if test=" realName !=null"> realName = #{realName},</if>
    <if test="password !=null"> `password` = #{password},</if>
    <if test=" sex !=null"> sex = #{sex},</if>
    <if test=" birthday !=null"> birthday = #{birthday},</if>
 </set>

trim标签

<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀" suffixOverrides="忽略后缀">
		......
</trim>
  • prefix:前缀,可以自动对trim标签所包含的语句是否又返回值进行判断,如果又返回值,则为SQK语句拼接相应的前缀
  • suffix:后缀,在trim标签包含的语句末尾拼接后缀
  • prefixOverrides:忽略的前缀,忽略trim标签内部首部指定的内容
  • suffixOverrides:忽略的后缀,忽略trim标签包含内部尾部指定的内容
<trim prefix="where" prefixOverrides="and|or">
                    <if test="realName!='' and realName!=nul">AND realName LIKE CONCAT('%',#{realName},'%')</if>
                   <if test="roleId!=0 and roleId!=nul">AND roleId=#{roleId}</if>
                </trim>

=null"> sex = #{sex},
birthday = #{birthday},

### trim标签

```dart
<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀" suffixOverrides="忽略后缀">
		......
</trim>
  • prefix:前缀,可以自动对trim标签所包含的语句是否又返回值进行判断,如果又返回值,则为SQK语句拼接相应的前缀
  • suffix:后缀,在trim标签包含的语句末尾拼接后缀
  • prefixOverrides:忽略的前缀,忽略trim标签内部首部指定的内容
  • suffixOverrides:忽略的后缀,忽略trim标签包含内部尾部指定的内容
<trim prefix="where" prefixOverrides="and|or">
                    <if test="realName!='' and realName!=nul">AND realName LIKE CONCAT('%',#{realName},'%')</if>
                   <if test="roleId!=0 and roleId!=nul">AND roleId=#{roleId}</if>
                </trim>