1:简介
1.1:什么是mybatis
- MyBatis 是一款优秀的持久层框架,
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.2:持久化
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 内存:断电即失
- 数据库(jdbc),io文件持久化。
1.3:持久层
Dao层、service层、controller层
- 完成持久化工作的代码块
- 层是界限十分明显的
1.4::为什么需要mybatis
- 保住程序员将数据存入数据库中
- 方便
- 传统的jdbc代码太复杂
2:第一个mybatis程序
思路:搭建环境-导入mybatis-编写代码-测试
2.1:搭建环境
- 搭建数据库
- 新建项目项目
- 新建普通的maven项目
- 删除src目录—当做父工程
- 导入maven依赖(mysql、junit、mybatis)
2.2:创建模块
-
编写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核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true&userUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- 每一个Mapper.xml都需要在mybatis核心配置文件中注册!--> <mappers> <mapper resource="com/chen/dao/UserDao.xml"/> </mappers> <settings> <setting name="lazyLoadingEnabled" value="true"/> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!-- 配置插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> </plugin> </plugins> </configuration>
-
编写mybatis工具类
package com.chen.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;
/**
* @Author CYXing
* @Date 2021/9/30 15:28
* @Version 1.0
*/
//sqlSessionFactory
public class mybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
//第一步:获取SqlSessionFactory对象
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SQLSession对象
public static SqlSession sqlSession(){
return sqlSessionFactory.openSession();
}
}
2.3:编写代码
-
实体类
@Data public class User { private int id; private int age; private String name; }
-
Dao接口
public interface UserDao { List<User> getUserList(); }
-
接口实现类
<?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.chen.dao.UserDao"> <select id="getUserList" resultType="com.chen.pojo.User"> select * from user </select> </mapper>
2.4:测试
注意点:
org.apache.ibatis.binding.BindingException: Type interface com.chen.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?
核心配置文件中注册mapper
<!-- 每一个Mapper.xml都需要在mybatis核心配置文件中注册!-->
<mappers>
<mapper resource="com/chen/dao/UserDao.xml"/>
</mappers>
测试:
可能遇到的问题:
- 配置文件没有注册
- 绑定接口错误
- 方法名不对
- 返回类型不对
- Maven导出资源问题
3:CRUD
3.1:namespace:
namespace中的包名要与接口名一致
3.2:select、insert、delete、update
选择,查询语句
- id:就是对应的namespace中的方法名
- resultType:sql语句执行的返回值,
- paramType:参数类型!
编写接口
编写对应的mapper语句
3.3:万能Map:
假设实体类或者数据库中的表,字段或者参数过多,我们应当使用Map!
4:配置解析
1:核心配置文件
-
mybatis-config.xml
-
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
2:环境配置(environments)
事务管理器:JDBC(默认)、MANAGED
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
连接数据库的数据源有:dbcp、c3p0、druid、jdbc等
连接池:POOLED|UNPOOLED|JNDI
3:属性(properties)
我们可以通过properties属性实现引用
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useSSL=true&userUnicode=true&characterEncoding=UTF-8
username=root
password=123456
在核心配置文件中引入
<properties resource="db.properties"/>
4:类型别名(typeAliases)
<!-- 给实体类起别名-->
方式一
<typeAliases>
<typeAlias type="com.chen.pojo.User" alias="user"/>
</typeAliases>
方式二
<typeAliases>
<package name="com.chen.pojo"/>
</typeAliases>
5:设置
6:其他设置
7:映射器(mappers)
MapperRegistry:注册绑定我们的Mapper文件
method01:
<mappers>
<mapper resource="com/chen/dao/UserMapper.xml"/>
</mappers>
method02:使用class文件绑定注册
<mappers>
<mapper class="com.chen.dao.UserMapper"/>
</mappers>
注意点:
- 接口和他的Mapper配置文件必须同名!
- 接口和他的Mapper配置文件必须在同一个包下!
method03:使用扫描包进行注入绑定
<mappers>
<package name="com.chen.dao"/>
</mappers>
注意点:
- 接口和他的Mapper配置文件必须同名!
- 接口和他的Mapper配置文件必须在同一个包下!
8:生命周期
生命周期和作用域至关重要,因为错误使用会导致严重的并发问题
SQLSessionFactoryBuilder:
- 一旦创建SQLSessionFactory,就不需要了就不需要它了
- 最佳作用域为局部变量
SQLSessionFactory:
- 数据库连接池
- 一旦创建在运行期间一直存在,没有任何理由丢弃它或者重新创建
- 最佳作用域为全局变量
- 最简单的就是使用单例模式或者静态单例模式
sqlSession:
- 连接到连接池的一个请求
- 最佳作用域是方法作用域
- 用完之后赶紧关闭
5:解决属性名跟字段名不一致的问题
问题:
当实体类与指定数据表对应时,实体类中的属性名与字段名不匹配时会出现赋值为空的情况!
解决方法:
- 给数据库查询字段起别名
- resultMap
5.1:resultMap
结果集映射
6:日志
日志工厂
如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手
曾经:sout、debug
现在:日志工厂
logImpl:指定mybatis所用日志的具体实现,未指定时将自动查找
在mybatis中具体使用哪个日志实现,在设置中实现
6.1:STDOUT_LOGGING标准日志输出
6.2:Log4j
什么是log4j?
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式
- 定义每一条日志信息的级别
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
-
先导入log4j包
dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
log4j.properties
-
配置log4j为日志的实现
-
直接运行
简单应用
- 在要是用的log4j的类中,导入包
2:日志对象,参数为当前类的class
3:日志级别
7:分页
思考:为什么要分页?
- 减少数据的处理量
7.1:使用limit分页
select * from user limit startIndex,pagesize
使用mybatis实现分页,核心SQL
-
接口
//分页查询 List<User> getUserByLimit(Map<String,Object> map);
-
mapper.xml
<select id="getUserByLimit" parameterType="map" resultType="user"> select * from user limit #{startIndex},#{pageSize} </select>
-
测试
7.2:RowBounds分页
不适用sql实现分页
-
接口
- 测试
7.3:分页插件
pageHelper:https://pagehelper.github.io/
8:使用注解开发
8.1:面向接口编程
8.2:使用注解开发
-
注解在接口上实现
@Select("select * from user") List<User> getUser();
-
在核心配置文件中绑定接口
<!-- 绑定接口--> <mappers> <mapper class="com.chen.dao.UserMapper"/> </mappers>
底层原理:动态代理
8.3:CRUD
我们可以在工具类出创建的时候实现自动提交事务!
关于@Param()注解
- 基本类型的参数或者String类型需要加上
- 引用类型不需要加
- 如果只有一个基本类型可以忽略
- sql中引用的就是param中的参数名
9:Lombok
步骤:
-
在idea中安装lombok插件
-
在项目中导入lombok依赖
-
@Data:无参构造、get、set、toString、equals、hascode @AllArgsConstructor @NoArgsConstructor
10: 多对一处理
多对一:
- 多个学生对应一个老师
- 对于学生而言:多对一 关联
- 对于老师而言:一对多 集合
测试环境搭建:
- 导入lombok
- 新建实体类Teacher、Student
- 建立Mapper接口
- 建立Mapper.XML文件
- 在核心配置文件中绑定注册我们的Mapper接口
- 测试查询能否成功!
多对一:
按照查询嵌套处理:
按照结果嵌套处理:
一对多:
实例:按照结果嵌套处理:
*变换前:*主要依靠多条sql语句获取到所需要的的信息
<select id="selectLevel1" resultMap="BaseResultMap">
select t1.`code` business_type_code, t1.`name` business_type_name
from pile_business_type t1
<where>
t1.is_deleted='N'
</where>
order by t1.`code`
</select>
<select id="selectChildrenByBusiness" resultMap="Children">
select t2.`code`, t2.`name`
from pile_report_type t2
<where>
t2.business_type_code=#{businessTypeCode,jdbcType=VARCHAR}
and t2.is_deleted = 'N'
and (t2.parent_code is null or t2.parent_code='')
</where>
order by t2.`code`
</select>
变换后:主要依赖resultMap中的collection一次性获取所需的信息
减少的sql语句的使用相对来说提高了性能!
《方式一》
<resultMap id="test" type="com.jandar.pile.yw.entity.PileBusinessType">
<result property="code" column="business_type_code"/>
<result property="name" column="business_type_name"/>
<!-- 复杂属性 我们需要单独处理
多对一: 对象:association ==》javaType=“” 指定属性类型
一对多: 集合:collection ==》 ofType=“” 获取集合中泛型信息
-->
<collection property="children" ofType="com.jandar.pile.yw.entity.PileReportType">
<result property="code" column="code"/>
<result property="name" column="name"/>
</collection>
</resultMap>
<select id="selectLevel1" resultMap="test">
select t1.`code` business_type_code, t1.`name` business_type_name,t2.`code` code, t2.`name` name
from pile_business_type t1, pile_report_type t2
<where>
t1.code=t2.business_type_code
and t1.is_deleted='N'
and t2.is_deleted='N'
and (t2.parent_code is null or t2.parent_code='')
</where>
order by t1.`code`, t2.`code`
</select>
《方式二》
小结:
- 关联:association–【多对一】
- 集合:collection–【一对多】
- javaType & ofType
- javaType用来指定实体类中属性的类型
- ofType用来指定映射到List或者集合中的pojo,泛型中的约束类型
注意点:
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对多和多对一中,属性名和字段问题
- 如果问题不好排查可以使用日志
高频面试:
- mysql引擎
- innoDB底层原理
- 索引
- 索引优化
12:动态sql
什么是动态sql:指根据不同的条件生成不同的sql语句
if
set
choose
when
sql片段
有时候,我们可能将一些公共的部分抽取出来,方便适用
使用sql标签抽取公共部分
<sql id="Base_Column_List">
id, unique_id, report_time, user_id, report_user_name, report_user_phone, report_address,
report_content, report_obj, report_pic_names, report_video_names, report_pic_urls, report_video_urls,
report_province, report_city, report_street, report_code, anonymous, status, read_status,
report_update_time, download_status,name
</sql>
在需要使用的地方用Include标签引用
<include refid="Base_Column_List"/>
foreach
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VnQJD2CI-1646898584827)(C:\Users\19828\AppData\Roaming\Typora\typora-user-images\image-20211011162238042.png)]
13:缓存
13.1:简介
查询:连接数据库,耗资源!
一次查询的结果,给他暂存在一个可以直接取到的地方 ==》内存:缓存
我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
- 什么是缓存{cache}?
- 内存中的临时数据
- 用户在缓存中查找数据,不用从磁盘查询数据,从而提高效率,解决高并发系统的性能问题
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销
- 什么样的数据能缓存?
- 经常查询并且不经常改变的数据【可以使用缓存】
13.2:mybatis缓存
-
mybatis包含非常强大的查询缓存特性,他可以非常方便的定制和配置缓存
-
默认两级缓存:一级缓存和二级缓存
-
默认情况下,只有一级缓存开启
-
二级缓存需要手动开启和配置,基于namespace级别的缓存
-
为了提高扩展性mybatis定义了接口Cache
-
13.3:一级缓存
- 一级缓存也叫本地缓存:sqlsession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
测试步骤:
- 开启日志
- 测试在一个session中查询两次相同的记录
- 查看输入值输出
缓存失效的情况:
-
查询不同的数据
-
增删改操作,可能会改变原来的数据
-
查询不同的Maper.xml
-
手动清理缓存
//手动清理缓存 sqlSession.clearCache();
小结:一级缓存默认开启,只在一次sqlsession中有效,也就是拿到连接到关闭这个区间
一级缓存相当于一个map。
13.4:二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace几倍的缓存,一个名称空间,对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果关闭当前会话,一级缓存就没了;如果有二级缓存,当会话关闭时,一级缓存的数据会被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取
- 不同的mapper查询结果会放在不同的缓存中
步骤
-
开启全局缓存
<!-- 显示的开启全局缓存--> <setting name="cacheEnable" value="true"/>
-
在要是用二级缓存的Mapper中开启
<!-- 在当期Mapper.xml中使用二级缓存--> <cache eviction="FIFO" flushInterval="60000" readOnly="true" size="512"/>
-
测试
-
问题:我们需要将实体类序列化!否则就会报错
q:Caused by: java.io.NotSerializableException: com.chen.pojo.User
a:public class User implements Serializable
小结:
- 只要开启了二级缓存,在同一个mapper下就有效
- 所有的数据都会先放在一级缓存中
- 当会话结束时才会提交到二级缓存中
-
13.5:缓存原理:
13.6:自定义缓存:ehcache
Ehcache是一种广泛使用的开源java分布式缓存。主要面向通用缓存
要在程序中使用ehcache,先导入依赖。
redis数据库