JavaWeb - Maven&MyBatis

JavaWeb - Maven&MyBatis

目录

1 Maven

1.1 Maven的核心功能

1.1.1 依赖管理

maven的web项目不需要将jar包直接导入,可以通过pom.xml文件添加jar包坐标去访问jar包仓库获取jar包

1.1.2 项目构建

项目从编译、测试、打包、安装,部署整个过程都交给maven进行管理,这个过程称为构建

1.1.3 仓库的分类

本地仓库,远程仓库(中央仓库,私服,第三方仓库)

1.1.4 Maven配置本地仓库

/conf/settings.xml 添加标签

本地仓库地址

1.1.5 Maven配置阿里镜像仓库

/conf/settings.xml 添加标签

<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>

1.2 Maven工程的结构

1.2.1 maven-java项目
java工程名(项目名)
	|-- src目录
		|-- main目录(主干代码)
			|-- java目录(java代码)
			|-- resources目录(配置文件)
		|-- test目录(测试代码)
			|-- java目录(java代码)
			|-- resources目录(配置文件)
	|-- pom.xml(maven工程核心配置文件)
	|-- target目录(存放编译后的class文件.....)
1.2.2 maven-javaWeb项目
web工程名(项目名)
	|-- src目录
		|-- main目录(主干代码)
			|-- java目录(java代码)
			|-- resources目录(配置文件)
			|-- webapp目录(页面资源)
				|-- WEB-INF
					|-- web.xml(web工程核心配置文件)
				|-- index.jsp
				|-- css、js、img..
		|-- test目录(测试代码)
			|-- java目录(java代码)
			|-- resources目录(配置文件)
	|-- pom.xml(maven工程核心配置文件)
	|-- target目录(存放编译后的class文件.....)

1.3 Maven常用命令&插件&生命周期

1.3.1 Maven有哪些常见命令

常见命令包括清理、编译、测试、打包、安装、 部署

  • clean 清理命令,删除target目录及内容

  • compile 编译命令,将src/main/java下的文件编译为class文件输出到target目录下

  • test 测试命令,执行src/test/java下单元测试类,并编译为class文件

  • package 打包命令,将java工程打包成jar包,将web工程打包成war包

    • pom.xml中设置导包方式
    • 默认jar可手动设置为war
  • install 安装命令,将maven工程打包(jar或war)并发布到本地仓库

  • deploy 部署命令,将jar或war包部署到私服

1.3.2 Maven的生命周期

maven对项目构建过程分为三套独立的生命周期

1. Clean Lifecycle(清理生命周期)
在进行真正的构建之前进行一些清理工作。
命令:clean

2. Default Lifecycle(默认生命周期)
构建的核心部分,编译,测试,打包,部署等等。
命令: compile test package install deploy
* 在同一个生命周期中的命令,执行后面的命令,前面的命令自动执行

3. Site Lifecycle(站点生命周期)
生成项目报告,站点,发布站点。
命令: site

1.4 Idea配置Maven

1.4.1 IEDA Maevn全局配置

1.4.2 Maven依赖范围
* compile
默认依赖范围,作用域在编译、测试、运行时都有效。

* test
作用域在测试时有效。编译和运行时不需要,比如:Junit。

* provided
作用域在编译、测试时有效。运行时不需要,比如: servlet api 被 tomcat 容器提供。

* runtime
作用域在测试、运行时有效。编译时不需要,比如:jdbc的驱动包。

2 MyBatis

官网:

MyBatis官网地址:http://www.mybatis.org/mybatis-3/

常见的框架:

持久层框架:专注于解决数据持久化的框架。常用的有mybatis、hibernate、spring jdbc等等。

表现层框架:专注于解决与用户交互的框架。常见的有struts2、spring mvc等等。

全栈框架: 能在各层都给出解决方案的框架。比较著名的就是spring。

2.1 JDBC分析

2.1.1 原始JDBC存在的问题
  • 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
  • sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变java 代码。
  • 查询操作时,需要手动将结果集中的数据手动封装到实体中。
2.1.2 针对原始JDBC可能的解决方案
  • 使用数据库连接池初始化连接资源
  • 将sql语句抽取到xml配置文件中
  • 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射

2.2 MyBatis简介

MyBatis是一个优秀的基于ORM的半自动轻量级持久层框架,对jdbc的操作数据库的过程进行封装

2.2.1 ORM思想
2.2.1.1 概念

ORM(Object Relational Mapping),即对象关系映射

  • O(对象模型): 实体对象,即我们在程序中根据数据库表结构建立的一个个实体javaBean
  • R(关系型数据库的数据结构): 关系数据库领域的Relational(建立的数据库表)
  • M(映射): 从R(数据库)到O(对象模型)的映射,可通过XML文件映射
2.2.1.2 实现方式

创建实体类,实体类和数据库表一一对应,实体类属性与表中字段对应

操作对应的实体类对象变化,将实体类变化映射到表的变化

2.3 MyBatis使用实现步骤(非代理开发)

1. 创建数据库及实例表
2. 创建maven工程,导入依赖(MySQL驱动、mybatis、junit)
3. 编写User实体类
4. 编写UserMapper.xml映射配置文件(ORM思想)
5. 编写SqlMapConfig.xml核心配置文件
	数据库环境配置
	映射关系配置的引入(引入映射配置文件的路径)
6. 编写测试代码
	// 1.加载核心配置文件
	// 2.获取sqlSessionFactory工厂对象
	// 3.获取sqlSession会话对象
	// 4.执行sql
	// 5.打印结果
	// 6.释放资源
2.3.1 创建数据库及实例表
2.3.2 创建maven工程,导入依赖(MySQL驱动、mybatis、junit)
指定编码和版本
		<properties>
			<project.build.sourceEncoding>UTF-		8</project.build.sourceEncoding>
			<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
			<java.version>1.11</java.version>
<maven.compiler.source>1.11</maven.compiler.source>
<maven.compiler.target>1.11</maven.compiler.target>
		</properties>

mybatis常用依赖:
	<!--mybatis坐标-->
	<dependency>
		<groupId>org.mybatis</groupId>
		<artifactId>mybatis</artifactId>
		<version>3.5.4</version>
		</dependency>
	<!--mysql驱动坐标-->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.6</version>
		<scope>runtime</scope>
	</dependency>
	<!--单元测试坐标-->
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
		<scope>test</scope>
	</dependency>
2.3.3 编写表对应实例类
  • 根据表中字段创建实例表对象及其属性,基本数据类型使用封装类型
  • 生成get和set方法
2.3.4 编写xxxMapper.xml映射配置文件(ORM思想)

文件头:

<?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">

namespace+id 唯一标识对应了一张表的一条sql语句

2.3.5 编写SqlMapConfig.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">

配置

2.3.5.1 所有标签:
configuration(配置)
	|-properties(属性)
	|-settings(设置)
    |-typeAliases(类型别名)
    |-typeHandlers(类型处理器)
    |-objectFactory(对象工厂)
    |-plugins(插件)
    |-environments(环境配置)
    	|-environment(环境变量)
    	|-transactionManager(事务管理器)
    	|-dataSource(数据源)
    |-databaseIdProvider(数据库厂商标识)
    |-mappers(映射器)
2.3.5.2 environments(环境配置)标签:
|-environments(环境配置)
    	|-environment(环境变量)
    	|-transactionManager(事务管理器)
    	|-dataSource(数据源)
    		|-property(数据源配置的基本参数)

1. 其中,事务管理器(transactionManager)类型有两种:
- JDBC:
这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
- MANAGED:
这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
例如:mybatis与spring整合后,事务交给spring容器管理。

2. 其中,数据源(dataSource)常用类型有三种:
- UNPOOLED:
这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:
这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
- JNDI :
这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用

3. property(数据源配置的基本参数)如果通过外部文件引入(properties) value值的写法为${}
2.3.5.3 properties(属性)
|-properties(属性)
	|-resource(路径)

加载外部properties文件,文件路径为resources下该文件的路径

2.3.5.4 typeAliases(类型别名)

简化映射文件Java类型的别名

|-typeAliases(类型别名)
	|-type(类型全限定名)
	|-alias(别名)

MyBatis设置的常用别名:

别名 数据类型
string String
long Long
int Integer
double Double
boolean Boolean
2.3.5.5 mappers(映射器)

加载映射

|-mappers(映射器)
	|-resource(相对路径的资源引用)
	|-url(使用完全限定资源符)
	<-- Mybatis代理开发中使用下面两种方式,使用要求xml文件和接口必须同包同名 -->
	|-class(使用映射器接口实现类的完全限定类名)
	|-package(将包内的映射器接口实现全部注册为映射器)
2.3.6 编写测试代码
// 1.加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");

// 2.获取sqlSessionFactory工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);

// 3.获取sqlSession会话对象
SqlSession sqlSession = factory.openSession();

// 4.执行sql
sqlSession.相应的方法

// 5.释放资源
sqlSession.close();

2.4 MyBatis API

2.4.1 SqlSessionFactoryBuilder
  • Resources工具类,这个类在 org.apache.ibatis.io 包中。Resources 能从类路径下、文 件系统或一个 web URL 中加载资源文件

  • build方法可以获取一个SqlSessionFactory对象

    • build方法进行了初始化的配置:使用dem4j解析了配置文件,将映射配置文件(mappers)中的配置封装成了MappedStatement对象
      • MappedStatement对象属性包括sql,resultType,parameterType
      • 一段配置对应一个对象,多个MappedStatement对象封装成map集合,key为namespace.id,value为对应的MappedStatement对象
    • build方法还创建了sqlSessionFactory工厂对象,该工厂对象会生成SqlSession会话对象
      • SqlSession会话对象本身不会直接操作数据库,委派给Executor去执行
      • Executor中封装了JDBC操作数据库的代码,但执行JDBC操作需要获取sql,参数返回结果类型等
      • SqlSession调用方法传入的参数即为MappedStatement所封装map的key,根据key获取到MappedStatement对象并拿到JDBC需要的参数
      • JDBC操作执行完毕后封装返回结果
2.4.2 SqlSessionFactory
  • openSession方法可以创建SqlSession实例
方法 解释
openSession() 更新操作需要手动提交事务
openSession(boolean autoCommit) true时会自动提交事务

2.4.3 SqlSession会话对象

语句执行方法:

  • T selectOne(String statement, Object parameter)
  • List selectList(String statement, Object parameter)
  • int insert(String statement, Object parameter)
    • 主键自增的情况下,插入数据时sql语句不用为主键预留位置,通过实例对象传递参数时也不用为主键赋值,插入数据后数据库自动添加主键
    • 执行插入操作后中文乱码显示?,需要在数据源配置时设置编码方式为utf-8 - url = .....?characterEncoding=utf-8
    • 如果没有设置自动提交事务,需要commit手动提交
  • int update(String statement, Object parameter)
    • 如果通过主键id修改,可以传入实例对象要修改的内容并把要修改记录的id传给该实例对象
    • 如果没有设置自动提交事务,需要commit手动提交
  • int delete(String statement, Object parameter)
    • 通过某个值删除记录,参数类型为该值的类型
    • 传入参数为#{xxxx},括号中值任意
    • 如果没有设置自动提交事务,需要commit手动提交

操作事务的方法:

  • void commit()
  • void rollback()

2.5 MyBatis非代理开发问题

  • 接口中每一个方法的实现类中都有大量重复的代码(读取配置文件/生成工厂对象/关闭对象等)
  • 实现类调用方法时,xml中的sql硬编码到代码中(传入的namespace.id)

解决办法:

MyBatis对接口提供了动态代理来对sqlsession进行相似的操作

2.6 MyBatis代理开发

2.6.1 MyBatis代理开发的规范
  • 接口和XMl文件名也需要对应,如接口xxxMapper 文件名得为 xxxMapper.xml
  • Mapper.xml映射文件中的namespace与mapper接口的全限定名相同
  • Mapper接口方法名和Mapper.xml映射文件中定义的每个statement的id相同
  • Mapper接口方法的输入参数类型和mapper.xml映射文件中定义的每个sql的parameterType的类 型相同
  • Mapper接口方法的返回值类型和mapper.xml映射文件中定义的每个sql的resultType的类型相同

2.7 MyBatis高级查询(基于映射配置文件)

2.7.1 ResultMap

当实体类属性与表中字段名称不一致时使用ResultMap来手动封装

<resultMap id="" type="">   id 为该resultmap的名称,type为实体类
<id column="" property=""></id> id标签代表表中主键字段
<result column="" property=""><result/> result标签代表非主键字段

coulumn 表示数据库中表的字段名
property 表示实体类中属性名
2.7.2 多条件查询(3种)
2.7.2.1 方式一

通过 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数

  • 如果时arg从0开始,如果是param从1开始
  • select语句中不用指定ParamaterType
2.7.2.2 方式二

使用注解,引入 @Param() 注解获取参数

  • 该注解在接口中定义方法时设置在参数前,其值为String类型的别名
  • 在select语句中使用#{别名}的方式获取对应的值,括号中的值与设置别名必须一致
  • select语句中不用指定ParamaterType
2.7.2.3 方式三

通过pojo对象传递参数

  • select语句中需要指定ParamaterType为实例对象
  • 接口方法中的参数也为实例对象
  • 通过set方法为对应的属性赋值
2.7.3 模糊查询(2种)

可以使用#{}和${}占位符两种方式

2.7.3.1 方式一 #{}
  • parameterType需要指定参数类型
  • {}不需要添加引号,如果为单参数括号里的值任意

2.7.3.2 方式二 ${}
  • parameterType需要制定参数类型
  • ${}外需要添加一对单引号,因为是字符串的拼接
  • 如果为单参数括号里的值只能为value
2.7.3.3 #{}和${}的比较

#{} :表示一个占位符号

  • 通过 #{} 可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,# {}可以有效防止sql注入
  • #{} 可以接收简单类型值或pojo属性值
  • 如果parameterType传输单个简单类型值, #{} 括号中名称随便写

${} :表示拼接sql串

  • 通过 ${} 可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换,会出现sql注入 问题
  • ${} 可以接收简单类型值或pojo属性值
  • 如果parameterType传输单个简单类型值, ${} 括号中只能是value(TextSqlNode.java 源码证明)
2.7.4 返回主键
2.7.4.1 useGeneratedKeys
  • 只适用于主键自增的数据库
  • 支持mysql和sqlserver,不支持oracle
<insert useGeneratedKeys="true" keyProperty="id">
useGeneratedKeys="true" 声明返回主键
keyProperty="id" 把返回主键的值,封装到实体的id属性中
2.7.4.2 selectKey
  • 支持所有类型数据库
<insert>
	<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID(); //该查询返回最先插入数据的id
	</selectKey>
<insert/>

keyColumn="id" 数据库主键字段的名称
keyProperty="id" 要封装到实体类的属性的名称
resultType="int" 指定主键类型
order="AFTER" 设置在sql语句执行前还是执行后执行该语句
设置为after,表示先执行插入操作,再拿到该插入操作的id值
2.7.5 动态SQL
2.7.5.1

动态查询,条件参数不确定

  • 传输传递使用pojo对象
  • test里判断属性是否为空
  • sql 语句前需要添加AND
  • 标签相当于 where 1=1 如果没有条件不会拼接where关键字
SELECT * FROM XXX
	<where>
		<if test="xx != null">
			AND xx = #{xx}
		</if>
		<if test="xxx != null">
			AND xxx = #{xxx}
		</if>
	</where>
2.7.5.2

动态更新,有值就更新,没值不处理

  • sql 语句不需要在前手动添加set,set标签会自动添加
  • sql语句需要在后面添加上逗号,set标签会自动把最后一条语句的逗号去掉
UPDATE XXX
	<set>
		<if test="xxx != null">
			xxx = #{xxx},
		</if>
		<if test="xx != null">
			xx = #{xx},
		</if>
		<if test="xxx !=null">
			xxx = #{xxx},
		</if>
	</set>
WHERE xx = #{xx}
2.7.5.3

数据的循环遍历

该标签用于遍历集合:

• collection:代表要遍历的集合元素

  • parametertype 可不写会 int, int[]
  • 查询条件为普通类型 List集合该属性值为collection 或者 list
  • 查询条件为普通类型 Array数组,collection属性值为array

• open:代表语句的开始部分

• close:代表结束部分

• item:代表遍历集合的每个元素,生成的变量名

• sperator:代表分隔符

SELECT * FROM XXX
	<where>
		<foreach collection="collection/array" open="xx in(" close=")" item="xx"
separator=",">
			#{xx}
		</foreach>
	</where>
2.7.6 SQL片段

映射文件中可将重复的 sql 提取出来,使用时用 include引用即可,最终达到 sql 重用的目的

<sql id="xx">
	SQL
</sql>

<select>
<include refid="xx"></include>
<select/>

2.8 分页插件(基于核心配置文件)

基于第三方插件分页助手PageHelper来简单的实现的实现

  • pom.xml导入PageHelper
<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
  • 核心配置文件中配置pagehelper
<!-- 分页助手的插件 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>
  • pagehelper API
PageHelper.startPage(pageNum,pageSize)
//参数1表示当前页,参数2表示每页显示的条数
//该方法需要在调用mapper操作数据方法前一行执行

PageInfo<XX> pageInfo = new PageInfo<XX>(list);
//该语句在查询完数据库之后执行
//list为查询语句得到的结果

根据该对象可以获取:
"总条数:"+pageInfo.getTotal()
"总页数:"+pageInfo.getPages()
"当前页:"+pageInfo.getPageNum()
"每页显示长度:"+pageInfo.getPageSize()
"是否第一页:"+pageInfo.isIsFirstPage()
"是否最后一页:"+pageInfo.isIsLastPage()

2.9 MyBatis多表查询(关联查询)

关系型数据库表关系分为:

  • 一对一
  • 一对多
  • 多对多
2.9.1 一对一查询(多对一)

使用LEFT JOIN查询:

  • SQL语句编写并测试:select * from A left join B on A.xx = B.xx

  • 两张表两个实例对象,对象中的属性完整与表中一致

  • 实例(A)中添加属性字段,该属性为另一个实例对象(B),设置get和set方法

  • 编写对应的接口及方法

  • 为对应的接口编写xml配置文件

    • 将主键和除了另一个实例对象(B)的属性封装到
    • 将另一个实例对象(B)封装到
    • 再将实例对象(B)的主键和其他字段封装
    • 如果SQL查询到有相同名称且值不同的字段需要取别名加以区分
      • select A.*, B.xx bxx, ....
2.9.2 一对多查询

可使用left/right join

将表中多表中重复的字段取别名加以区分

  • SQL语句编写并测试:select A.*, B.xx bxx, B.xxx from A left join B on A.xx = B.xx
  • 实例(A)中添加属性字段,该属性为类型为另一个实例对象(B)的集合List,设置get和set方法
  • 编写对应的接口及方法为对应的接口编写xml配置文件
    • 将主键和除了List集合的属性封装到
    • 将集合List封装到
    • 再将实例对象(B)的主键和其他字段封装
    • 如果SQL查询到有相同名称且值不同的字段需要取别名加以区分
      • select A.*, B.xx bxx, ....
2.9.3 多对多查询

需要用到中间表

和一对多查询步骤相似,主要是SQL语句的编写

  • SQL语句:select * from A left join AB on A.xx = AB.xx left join B on B.xx x= AB.xxx

2.10 MyBatis多表查询(嵌套查询)

2.10.1 一对一查询(多对一)
-- 先查询A
SELECT * FROM A;

-- 再根据A的外键,去B查询
SELECT * FROM B WHERE B.xx = #{A.xx};
  • SQL语句:select * from A

  • 两张表两个实例对象,对象中的属性完整与表中一致

  • 实例(A)中添加B属性字段,该属性为另一个实例对象(B),设置get和set方法

  • 先在A编写对应的接口及方法(SELECT * FROM A;)

  • 为A对应的接口编写xml配置文件

    • 将主键和除了另一个实例对象(B)的属性封装到
    • 将另一个实例对象(B)封装到
    • 再将实例对象(B)的主键和其他字段封装
    • 在B对应的接口下编写子查询的方法
    • 为B接口对应的xml文件进行编写(子查询方法配置)
2.10.2 一对多查询
-- 先查A
SELECT * FROM A;

-- 再根据A.xx和B的外键xx子查询
SELECT * FROM B where A.xx = #{B.xx};
  • SQL语句:select * from A

  • 两张表两个实例对象,对象中的属性完整与表中一致

  • 实例(A)中添加集合属性字段,该属性为另一个实例对象(B)的集合List,设置get和set方法

  • 先在A编写对应的接口及方法(SELECT * FROM A;)

  • 为A对应的接口编写xml配置文件

    • 将主键和除了另一个实例对象(B)集合List的属性封装到
    • 将另一个实例对象(B)封装到
    • 再将实例对象(B)的主键和其他字段封装
    • 在B对应的接口下编写子查询的方法
    • 为B接口对应的xml文件进行编写(子查询方法配置)
2.10.3 多对多查询
-- 先查询A
SELECT * FROM A;
-- 再根据A.xx从B表以及中间表AB中进行子查询
SELECT * FROM B  INNER JOIN AB ON B.xxx = AB.xxx
WHERE AB.xx = #{A.xx};

  • SQL语句:select * from A

  • 两张表两个实例对象,对象中的属性完整与表中一致

  • 实例(A)中添加集合属性字段,该属性为另一个实例对象(B)的集合List,设置get和set方法

  • 先在A编写对应的接口及方法(SELECT * FROM A;)

  • 为A对应的接口编写xml配置文件

    • 将主键和除了另一个实例对象(B)集合List的属性封装到
    • 将另一个实例对象(B)封装到
    • 再将实例对象(B)的主键和其他字段封装
    • 在B对应的接口下编写子查询的方法
    • 为B接口对应的xml文件进行编写(子查询方法配置)

2.11 延迟加载

概念:

在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载

优点:

先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表 速度要快

缺点:

在大批量数据查询时,因为查询工作也要消耗时 间,所以可能造成用户等待时间变长,造成用户体验下降

适用场景:

延迟加载是基于嵌套查询来实现的

  • 一对多,多对多:通常情况下采用延迟加载
  • 一对一(多对一):通常情况下采用立即加载
2.11.1 局部延迟加载

association和collection标签中都有一个fetchType属性,通过修改它的值,可以修改局部的加载策略

2.11.2 触发延迟加载

当调用当前对象以下方法时也会触发关联对象的查询并触发延迟加载:

  • equals
  • clone
  • hashCode
  • toString

可以通过在核心配置文件中设置并覆盖掉上面四个方法

<settings>
<!--所有方法都会延迟加载-->
<setting name="lazyLoadTriggerMethods" value="toString()"/>
</settings>
2.11.3 全局延迟加载

在核心配置文件中修改全局延迟加载策略

局部延迟加载策略优先级高于全局延迟加载

<settings>
<!--开启全局延迟加载功能-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>

2.12 MyBatis缓存

缓存的好处:

通过缓存,减少网络连接和数据库查询带来的消耗进而提高查询的效率并减少高并发访问所带来的系统性能问题

Mybatis缓存分为一级缓存和二级缓存

2.12.1 一级缓存
  • SqlSession级别的缓存,默认开启
  • 条件:参数和SQL完全一样的情况下,用同一个SqlSession对象调用Mapper方法
  • 存入一级缓存以后,在没有声明刷新和缓存没有超时的情况下该查询都会使用缓存
  • 当执行SqlSession的增删改操作或调用SqlSession的clearCache(), commit(), close()方法都会清空缓存
  • 可以在查询时设置(select flushCache="true")在每次查询时都清除缓存
2.12.2 二级缓存
  • namespace级别的缓存(跨sqlsession),默认关闭
  • 当使用二级缓存时要求返回的POJO对象必须可序列化(即实现Serializable接口)
  • 核心配置文件设置二级缓存
<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
为true代表开启二级缓存;为false代表不开启二级缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>
  • 映射配置文件设置二级缓存
<mapper>
<!--当前映射文件开启二级缓存-->
<cache></cache>
<!--
<select>标签中设置useCache=”true”代表当前这个statement要使用二级缓存。
如果不使用二级缓存可以设置为false
注意:
针对每次查询都需要最新的数据sql,要设置成useCache="false",禁用二级缓存。
-->
<select useCache = "true">
<mapper/>
  • 映射文件中 select 语句会被缓存,insert, update, delete 语句会刷新缓存
  • 当执行SqlSession的commit() 和 close() 方法时一级缓存内容会刷新到二级缓存
  • 二级缓存会产生严重的脏读问题,不建议使用,用redis第三方缓存来代替
    • 当使用多表查询时由于是namespace级别的缓存,当单独更新第二张表的数据时,关联查询得到数据的缓存不会被删除(处在不同的namespace级别)
2.12.3 缓存小结
  • mybatis缓存自动维护
  • mybatis开启了二级缓存后,那么查询顺序:二级缓存 -> 一级缓存 -> 数据库

2.13 注解开发

2.13.1 常用注解
  • @Insert:实现新增,代替了insert
  • @Delete:实现删除,代替了delete
  • @Update:实现更新,代替了update
  • @Select:实现查询,代替了select
  • @Result:实现结果集封装,代替了result
  • @Results:可以与@Result 一起使用,封装多个结果集,代替了resultmap
  • @One:实现一对一结果集封装,代替了association
  • @Many:实现一对多结果集封装,代替了collection
2.13.2 注解开发的步骤
2.13.2.1 基本CRUD操作
  • pom.xml文件导入相关包
  • 创建核心配置文件,配置environments,properties并创建porperties文件配置数据库连接配置
  • 根据数据库表创建对应的实体类
  • 创建对应的xxxMpper接口并创建方法
  • 在核心配置文件中添加mappers标签并使用mapper class

简单的增删改查操作:

  • @Select("value")

    • value为查询的sql语句
  • @Insert("value")

    • value中为完整的sql语句,参数传递还是使用#{属性名}来进行传递
    • 主键不需要设置
  • @Update("value")

    • value中为完整的sql语句,参数传递还是使用#{属性名}来进行传递
    • 其中如果需要id为判断条件,将其封装到实例对象中传递
  • @Delete("value")

    • value中为完整的sql语句,参数传递如果是单个引用数据类型#{}中的值任意
2.13.2 注解发开 - 多条件查询

方式一:SQL语句中直接使用#{param1} 或 #{arg0}

@Select("value")

  • value 为完整的SQL语句

方式二:在方法的参数前面添加@Param("value")注释

@Select("value")

  • value为完整的SQL语句
  • 其中#{}中的值为方法体中参数前@Param注释中设置的值

方式三:通过Pojo对象中的属性值进行传递

@Select("value")

  • value为完整的SQL语句
  • 其中#{}中的值为Pojo对象中对应属性名的值
  • 参数传递为Pojo对象
2.13.3 注解开发 - 模糊查询

{}和${}两种方式

方式一:#{}

@Select("value")

  • value SQL语句 like #{xxx} 单个参数传递时括号中的值任意
  • 使用该方式时like后面不用手动添加 ' '

方式二:${}

@Select("value")

  • value SQL语句 like '${value}' 单个参数传递时括号中的值为value
  • 使用该方式立刻后面需要手动添加 ' '
2.13.4 注解开发 - 返回主键

方式一:useGeneratedKeys

该方式适用于主键自增的的数据库,mysql和sqlserver支持,orecal不支持

@Select (insert into xxx (....) values (#{xx}....)) 正常编写插入sql语句
@Options(useGeneratedKeys = true, keyProperty = "主键")
void insertXXX (Pojo pojo)

方式二:selectKey

适用范围广,支持所有类型数据库

@Select (insert into xxx (....) values (#{xx}....)) 正常编写插入sql语句
@SelectKey(
	keyColumn = "表中字段名",
	keyProperty = "封装到Pojo中的属性名",
	resultType = xxx.class, //该属性的Class类型
	before = false, // false表示在这条插入SQL语句之后执行,true表示在之前执行
	statement = "select LAST_INSERT_ID()" // 查询前一条插入语句的主键值
)
void insertXXX (Pojo pojo)
2.13.5 注解开发 - 动态SQL

在实现动态SQL之前,需要自定义一个SQL类用来存储自定义编写的动态SQL方法

针对xxMapper接口中需要用到动态SQL的方法在SQL类中生成对应的方法

//xxxMapper
@XxxxProvider(type = SQL类.class, method = "SQL类中对应方法名")
List<Pojo> xxxXXX (Pojo pojo)

SQL类方法:
public String 方法名(参数) {
	return new SQL(){
		{
			编写SQL...
		}
	}.toString();
}
2.13.5.1

@SelectProvider(type = , method = "")

SELECT("* from xx");
if (Pojo.getxx()!=null){
    WHERE("xx = #{xx}");
}
if (Pojo.getxxx()!= null){
    WHERE("xxx = #{xxx}");
}
2.13.5.2

该动态SQL与普通 update SQL语句的区别在于普通的更新操作要更新的字段是写死固定的,而该动态SQL可以根据传入实例对象的值来动态的创建SQL

@UpdateProvider(type = , method ="")

UPDATE("xxx"); //表名
if (pojo.getxx()!=null){
	SET("xx = #{xx}");
}
if (pojo.getxxx()!=null){
	SET("xxx = #{xxx}");
}
if (pojo.getxx()!=null){
	SET("xx = #{xx}");
}
WHERE("xx = #{xx}");
2.13.5.3

该动态SQL主要用于做数据的循环遍历,比如 in (a, b, c) 由于在SQL() 内没有找到与foreach对应的标签,可将该标签改写为 = a or = b or = c

//xxxMapper 中对应的方法
List<Pojo> xxxXXX(@Param(arg0) 参数) //该地方参数可以用集合 需要加上@Param注解因为该地方读入的数据为该名称
List<Integer>或者数组Integer[]

SELECT("* from user");
for (int i = 0; i < 参数.length; i++) {
	WHERE("xx = "+ids[i]); //循环拼接数值
	if (i != 参数.length-1){ //该地方拼接是因为使用OR()以后WHERE后面不再自动拼接AND而且该OR不会判断是否是最后一个条件
		OR(); 
		}
	}
}
2.13.6 注解开发 - 多表查询

在不使用嵌套查询的前提下进行多表查询,需要SQL语句使用join来实现,但一对多或多对多的方法时暂时未能找到实现方法,这里给出一对一的实现方式

2.13.6.1 一对一(多对一)

在封装表示一张表的实体类字段时,用实体类.属性名的方式来接收数据

@Results标签可以设置id值,在后面的方法中可使用@ResultMap来调用

@Select("select * from A left join B on A.xx = B.xxx")
    @Results(id = "orderWithUser", value = {
            @Result(id = true, property = "xx", column = "xx"),
            @Result(property = "xxx", column = "xxx"),     
            @Result(property = "B.xx", column = "xx"),
            @Result(property = "B.xxx", column = "xxx")
    })
2.13.7 注解开发 - 多表查询 - 嵌套查询
2.13.7.1 一对一

使用@One 注解来代替配置文件开发中的标签

//AMapper
//@One可以通过fetchType来设置延迟加载

@Select("select * from A")
    @Results({
            @Result(id = true, property = "xx", column = "xx"),
            @Result(property = "xxx", column = "xxx"),     
            @Result(property = "Pojo", javaType = Pojo.class, column = "xx", one = @One(select = "另一个而查询对应接口的全路径.方法名"))
    })

在另一个接口中编写查询,将@One所在@Result里的column值传递过来作为参数

 @Select("select * from B where xx = #{xxx}")
 Pojo findBByxx(XXX xx);
2.13.7.2 一对多(或多对多)

使用@Many注解来代替配置文件开发中的标签

一对多 和 多对多的区别在于另一个查询SQL语句的不同

@Select("select * from A")
    @Results({
            @Result(id = true, property = "xx", column = "xx"),
            @Result(property = "xxx", column = "xxx"),
            @Result(property = "lists", column = "xx", javaType = List.class, many = @Many(select = "另一个查询对应接口的全路径.方法名")),
    })

在另一个接口中编写对应的方法和注释

2.13.8 注解开发 - 设置二级缓存

核心配置文件配置:

<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
为true代表开启二级缓存;为false代表不开启二级缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>

在mapper接口上添加注解开启

@CacheNamespace
public interface UserMapper {...}
2.13.9 注解开发- 延迟策略

在@one或@Many中配置fetchType的值

fetchType = FetchType.LAZY 表示懒加载
fetchType = FetchType.EAGER 表示立即加载 
fetchType = FetchType.DEFAULT 表示使用全局配置
posted @ 2021-02-04 17:15  Pengc931482  阅读(172)  评论(0)    收藏  举报