1.什么是ORM框架
ORM(Object-Relational Mapping,面向对象关系映射)是一种技术,用于将面向对象编程中的对象(如类)与关系性数据库中的表相对应,从而通过操作对象来间接操作数据库对象,而无需直接编写SQL语句。
ORM 框架的核心概念
- 对象与表映射: 类对应数据库表,类的属性对应表中的字段,类的实例对应表中的一行记录。
- 关系映射: 对象之间的关系(如一对一、一对多、多对多)对应数据库表之间的关系(如外键或关联表)。
- 透明操作: 开发者只需编写面向对象的代码,ORM 框架会自动将其转换为相应的 SQL 操作。
ORM 框架的作用
- 简化开发:减少直接与 SQL 和数据库交互的代码,提高开发效率。
- 降低耦合:数据库操作和数据库结构解耦,易于代码维护和迁移
- 跨数据库支持:ORM通常支持多种数据库类型,通过配置即可切换
ORM的核心功能
(1)基本的CRUD
- 支持增删改查对象化操作,例如:
创建对象,自动插入数据库。
修改对象属性,自动更新到数据库。
删除对象,自动执行对应的 SQL DELETE 操作。
- 支持增删改查对象化操作,例如:
(2)查询功能
- 提供面向对象的查询方式,如 HQL(Hibernate Query Language)或 Criteria API
- 支持动态查询构造,也可以直接执行原生 SQL
(3)关系映射
- 支持处理复杂的对象关系:
一对一(One-to-One)
一对多(One-to-Many)
多对多(Many-to-Many)
- 支持处理复杂的对象关系:
(4)事务管理
- 支持数据库事务操作,确保数据操作的原子性和一致性
(5)延迟加载
- 只有在需要时才从数据库加载关联数据,优化性能。
2.什么是Mybatis
MyBatis 是一个优秀的 持久层框架,它通过 SQL 映射文件或注解 来将 Java 对象与数据库中的数据记录进行绑定,从而简化了对数据库的操作。
与传统的 ORM 框架(如 Hibernate)不同,MyBatis 不会完全封装 SQL,它强调对 SQL 的直接使用,同时提供灵活的动态 SQL 构造和对象映射能力,兼顾了效率与易用性。
Mybatis核心特点
直接操作SQL
- 开发者需要手写 SQL。
- 提供对复杂 SQL 的完全控制
灵活的配置
- 支持 XML 和注解配置 SQL。
- 动态生成 SQL,适应多变的业务需求。
对象关系映射
- 将数据库结果集映射为 Java 对象。
- 提供简单的结果映射或高级的复杂关系映射。
支持主流数据库
- 如 MySQL、PostgreSQL、Oracle、SQL Server 等。
轻量级框架
- 不依赖其他大型框架,易于集成
Mybatis的核心组件
SQL 映射文件 (XML)
- 定义SQL语句和对象映射规则
Mapper接口:
- 绑定 SQL 映射文件的方法
- 通过接口直接调用 SQL
MyBatis配置文件(mybatis-config.xml)
- 配置全局信息,如数据库连接、插件、类型别名等。
会话管理器 (SqlSessionFactory 和 SqlSession):
- 用于管理数据库会话,执行 SQL 操作。
MyBatis核心功能
提供CRUD
- 提供简单的增删改查操作,通过 SQL 完成数据操作。
动态SQL
- 使用 XML 中的标签(如
<if>、<choose>
等)或注解动态生成 SQL。
- 使用 XML 中的标签(如
结果映射
- 支持单表查询结果的映射到 Java 对象,也支持复杂对象关系的映射。
插件扩展
- 提供灵活的拦截器机制,可扩展功能(如分页、审计等)。
MyBatis工作流程
- 初始化:加载全局配置文件和映射文件,创建SqlSessionFactory。
- 获取会话:通过SqlSessionFactory获取SqlSession
- 执行操作:使用SqlSession调用SQL,进行增删改查
- 返回结果:返回查询结果或者受影响的行数
- 关闭会话,释放资源
Mybatis的优点
- 小巧,学习成本低,会写SQL就可以上手
- 大部份工作只需要专注于SQL语句
- 方便维护管理,SQL代码可以分离出来
- 支持动态SQL语句(条件判断,范围判断)
- 支持对象与ORM字段的关系映射
Mybatis的核心组件
SqlSessionFactory
作用
- 是 MyBatis 的核心接口之一,用于创建 SqlSession 对象。
- 它是线程安全的,通常被设计为单例模式,应用程序启动时只需要初始化一次
工作原理
- 根据 MyBatis 的配置文件(mybatis-config.xml)构建。
- 负责加载配置信息,解析映射文件(Mapper)并初始化数据库连接池
SqlSession
作用
- 是 MyBatis 与数据库交互的直接接口,用于执行 SQL 操作(如查询、插入、更新、删除)
- 线程不安全,通常在每次数据库操作时创建并使用,完成后关闭。
常用方法
- selectOne
- selectList
- insert/update/delete
Mapper映射器
- 作用:映射文件(XML)或者接口,定义了 SQL 语句与 Java 方法的对应关系。
xml映射文件:
- 让在Resource目录里,通常命名为xxxMapper.xml
- 包含具体的 SQL 语句以及与 Java 对象的映射关系
Mapper 接口
- 作用:用来定义Java方法,与XML文件或者注解中的SQL语句绑定
MyBatis 配置文件
- 作用:定义全局配置,包括数据库连接信息,用户名,密码,事务管理,映射文件路径
Executor执行器
作用:
- MyBatis 的底层执行组件,负责执行 SQL 语句并返回结果
- 根据查询请求,调用数据库连接,处理参数映射和结果映射
类型
- singleExecutor: 每次执行都会打开和关闭连接。
- reuseExecutor: 复用相同的 Statement 对象,提高性能。
- batchExecutor: 执行批量操作,常用于批量插入或更新。
StatementHandler
作用:
- 负责将 SQL 转换成数据库可执行的 Statement 对象。
- 提供 SQL 的预编译、参数设置、执行结果处理等功能。
工作流程
- 接收Mapper中定义的SQL
- 根据参数动态生成SQL语句
- 使用JDBC执行SQL,并获取结果
ParameterHandler
作用:
- 用于处理 SQL 语句的输入参数,将参数传递到预编译的 SQL 中
示例:
- 将 #{id} 替换成具体的值,比如 1
ResultSetHandler
作用:
- 负责将 SQL 查询结果集(ResultSet)映射到 Java 对象或集合中
映射方式:
- 自动映射:通过列名与 Java 对象属性名匹配
- 手动映射:通过 resultMap 明确映射关系。
<resultMap id="userResultMap" type="User">
<result property="id" column="id" />
<result property="name" column="name" />
<result property="email" column="email" />
</resultMap>
TypeHandler(类型处理器)
作用:
- 用于处理 Java 数据类型与数据库字段类型之间的转换
默认处理器:
- MyBatis 内置了常用的类型处理器,如 StringTypeHandler、IntegerTypeHandler
自定义处理器
- Java String(JSON序列化)-> MySQL JSON类型
Cache(缓存)
作用:
- 提升查询性能,减少对数据库的直接访问。
类型
- 一级缓存(本地缓存):SqlSession 级别,默认开启,同一个 SqlSession 内重复查询时缓存结果
- 二级缓存(全局缓存):二级缓存(全局缓存):Mapper 级别,多个 SqlSession 可共享。
#{}
与${}
的区别
#{}
和 ${}
都用于动态传参,但它们有不同的处理机制和适用场景
- 基本区别
特性 | #{}(占位符处理) | ${}(字符串替换处理) |
---|---|---|
解析方式 | 使用 预编译参数占位符 | 直接将值拼接到 SQL 中 |
安全性 | 自动进行 SQL 注入防护 | 无防护,可能引发 SQL 注入问题 |
SQL 表现 | 生成 ? 占位符,动态绑定参数 | 值直接拼接到 SQL 语句 |
使用条件 | 传递参数值,如条件、字段值等 | 动态拼接字段名、表名或其他 SQL 结构 |
- 使用示例
#{}
示例:
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id};
</select>
实际执行 SQL:SELECT * FROM users WHERE id = ?
(MyBatis 使用 JDBC 将 1 绑定到 ? 中,防止 SQL 注入)
优点:安全、高效,适用于动态值(如查询条件、插入/更新字段值等)。
${}
示例:
<select id="getUsersByTable" parameterType="string" resultType="User">
SELECT * FROM ${tableName} WHERE id = #{id};
</select>
实际执行 SQL:SELECT * FROM users WHERE id = ?
优点:适用于动态结构(如表名、列名),但容易引发 SQL 注入,必须确保输入值可信。
- 安全性
#{}
的安全性
- MyBatis 自动处理参数的转义,防止恶意注入
${}
的安全性
- 如果用户输入的值包含恶意 SQL 片段(如 users; DROP TABLE users;),可能引发安全问题
<select id="getUserByName" parameterType="string" resultType="User">
SELECT * FROM users WHERE name = '${name}';
</select>
实际执行 SQL:SELECT * FROM users WHERE name = 'admin' OR '1'='1';
结果:查询条件被篡改,返回了所有用户数据。
- 性能影响
#{}
优化性能:
- SQL 使用占位符,数据库引擎会缓存 SQL 的执行计划(Prepare Statement 缓存),提升性能。
${}
缓存差:
- 每次执行都会重新生成完整 SQL,数据库引擎无法缓存执行计划,性能稍逊。
Mybatis里的动态标签
MyBatis 的动态标签是为了简化动态 SQL 的编写,支持根据条件动态生成 SQL 语句。它提供了一些标签来处理常见的动态场景,比如条件判断、循环、拼接等。
<if>
标签:用于根据条件判断是否生成某段 SQL。
<if test="条件表达式">
SQL语句片段
</if>
- test 属性是条件表达式,支持 Java 的逻辑表达式
<choose>
、<when>
和<otherwise>
标签
类似于 Java 的 switch,用于多条件选择。
<choose>
<when test="条件1">
SQL语句片段1
</when>
<when test="条件2">
SQL语句片段2
</when>
<otherwise>
默认SQL语句片段
</otherwise>
</choose>
<choose>
标签中只能有一个<when>
生效,如果没有满足条件的<when>
,则执行<otherwise>
<trim>
标签
用于对动态SQL进行前后缀处理(如添加 AND 或去除多余的逗号)
<trim prefix="前缀" suffix="后缀" suffixOverrides="移除后缀" prefixOverrides="移除前缀">
SQL语句片段
</trim>
例子:
<select id="getUsers" resultType="User">
SELECT * FROM users
<trim prefix="WHERE" prefixOverrides="AND | OR">
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</trim>
</select>
<where>
标签
智能地添加 WHERE 关键字,并自动去除多余的 AND 或 OR。
<select id="getUsers" resultType="User">
SELECT * FROM users
<where>
<if test="name != null and name != ''">
name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
<set>
标签
用于动态生成 UPDATE 语句,自动处理多余的逗号。
说明:
<set>
SQL语句片段
</set>
示例:
<update id="updateUser">
UPDATE users
<set>
<if test="name != null">
name = #{name},
</if>
<if test="age != null">
age = #{age},
</if>
<if test="email != null">
email = #{email}
</if>
</set>
WHERE id = #{id}
</update>
说明:
- 自动去除最后一个逗号。
- 如果所有子条件为空,则
<set>
标签不生成内容。
<foreach>
标签
用于循环生成 SQL 片段,常用于 IN 查询或批量操作。
语法:
<foreach collection="集合" item="变量名" index="索引" open="开始符" close="结束符" separator="分隔符">
SQL语句片段
</foreach>
动态标签总结
标签 | 功能描述 | 常见场景 |
---|---|---|
if | 根据条件动态生成SQL | 动态条件拼接 |
choose/when/otherwise | 类似于 switch,从多个条件中选择一个 | 条件分支逻辑 |
trim | 自定义前后缀并去除多余关键字 | 灵活生成复杂SQL结构 |
where | 自动添加 WHERE 并清理多余关键字 | 动态查询 |
set | 自动处理 UPDATE 中的逗号 | 动态更新 |
foreach | 遍历集合生成 SQL | 批量操作或动态 IN 查询 |
xml映射文件上的标签
全局配置标签
1.<mapper>
表示一个映射文件的根目标
- 描述:表示一个映射文件的根标签
- 属性:namespace 定义映射的命名空间,用于区分SQL语句
- 示例:
<mapper namespace="com.example.UserMapper">
<!-- 映射内容 -->
</mapper>
SQL映射标签
2.select
- 描述:用于定义查询语句。
属性:
- id:标识方法名,唯一。
- resultType:返回的结果类型(如 Java 对象)
- parameterType:传入参数的类型。
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id};
</select>
3.insert
- 描述:用于定义插入语句
属性:
- id:方法名
- parameterType:传入参数的类型
- 示例:
<insert id="insertUser" parameterType="User">
INSERT INTO users (name, age, email) VALUES (#{name}, #{age}, #{email});
</insert>
4.update
- 描述:用于定义更新语句
属性:
- id:方法名
- parameterType:传入方法类型
- 示例:
<update id="updateUser" parameterType="User">
UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id};
</update>
5.delete
- 描述:用于定义删除语句
属性:
- id:方法名
- parameterType:传入方法类型
- 示例:
<delete id="deleteUserById" parameterType="int">
DELETE FROM users WHERE id = #{id};
</delete>
动态SQL标签
6.<if>
根据条件动态生成 SQL 语句。
<if test = "name != null and name != ''">
AND name = #{name}
</if>
<choose>
用于多条件选择(类似于 switch-case)。
<choose>
<when test = "status == 1">
status = 1
</when>
<when test = "status == 2">
status = 2
</when>
<otherwise>
status IN (1, 2)
</otherwise>
</choose>
<trim>
动态添加或去除前缀、后缀。
<trim prefix = "WHERE" prefixOverrides = "AND|OR">
<if test = "name!= null and name != ''">
AND name = #{name}
</if>
<if test = "age!= null and age !=''">
AND age = #{age}
</if>
</trim>
<where>
智能地添加 WHERE 并去掉多余的 AND 或 OR。
<where>
<if test = "name != null">
name = #{name}
</if>
<if test = "age != null">
AND age = #{age}
</if>
<where>
10.<set>
自动处理更新语句中的多余逗号。
<set>
<if test = "name != null">
name = #{name}
</if>
<if test = "age != null">
age = #{age}
</if>
</set>
11.<foreach>
用于遍历集合生成动态SQL
<foreach collection="ids" item="id" open="(" close=")" seperator=",">
#{id}
</foreach>
可重用标签
12.<sql>
定义可重用的 SQL 片段。
<sql id = "baseColumns">
id, name, age, email
</sql>
引用方式:
<select id="getAllUsers" resultTyper="User">
SELECT <include refid = "baseColumns"/> FROM users;
</selecr>
<include>
引入可重用的 SQL 片段。
### 引入可重用的 SQL 片段
<include refid = "baseColumns"/>
高级功能标签
<resultMap>
resultMap 是 MyBatis 提供的强大的结果映射功能,主要用于将查询结果集映射到Java对象
- id: 唯一标识,供查询时引用。
- type: 映射结果的 Java 对象类型。
示例 1:简单映射
<resultMap id="userResultMap" type="com.example.MyObject">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
</resultMap>
<select id = "getUserById" resultMap = "userResultMap">
SELECT id, user_name, user_age FROM users WHERE id = #{id}
</select>
示例 2:嵌套对象映射
<resultMap id="orderResultMap" type = "com.example.Order">
<id property = "id" column = "id"/>
<result property = "totalPrice" column = "total_price"/>
<association property = "user" javaType = "com.example.User">
<id property = "id" column = "user_id"/>
<result property = "name" column = "user_name"/>
</association>
</resultMap>
<select id = "getOrderById" resultMap = "orderResultMap">
SELECT o.id, o.total_price, u.id AS user_id, u.name AS user_name
FROM order o
JOIN users u ON o.user_id = u.id
WHERE o.id = #{id}
</select>
示例 3:集合映射
<resultMap id = "catagoryResultMap" type = "com.example.Catagory">
<id property = "id" column = "id"/>
<result property = "name" column = "name"/>
<collection property = "products" ofType = "com.example.Product">
<id property = "id" column = "product_id"/>
<result property = "name" column = "product_name"/>
</collection>
</resultMap>
<paramemterMap>
标签
<parameterMap>
用于定义 SQL 语句的输入参数映射。
注意:在现代 MyBatis 中已不推荐使用,直接使用 JavaBean 或 Map 作为参数即可。
<parameterMap id = "paramMap" type ="com.example.User">
<parameter property="id" column="id"/>
<parameter property="name" column="user_name"/>
</parameterMap>
<selectKey>标签
<selectKey>
用于在插入语句之前或之后生成主键值,适用于主键生成策略需要查询数据库或使用序列的情况。
基本语法
<selectKey keyProperty="id" resultType="int" order="BEFORE">
SELECT SEQ_USER.NEXTVAL FROM DUAL
</selectKey>
<insert id="insertUser" parameterType="User">
INSERT INTO users (id, name, age) VALUES (#{id}, #{name}, #{age});
</insert>
属性
- keyProperty:生成主键值对应的 Java 对象属性。
- resultType:主键的类型。
- order:主键生成顺序,BEFORE 表示在执行 SQL 前生成,AFTER 表示在执行 SQL 后生成。
示例: 自动生成主键
适用于 MySQL 数据库,利用自增主键。
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (name, age) VALUES (#{name}, #{age});
</insert>
使用MyBatis进行分页
- 数据级别的分页
在 SQL 中直接使用 LIMIT 和 OFFSET,并通过参数传递页码和页大小。
<select id = "getUser" parameterType = "int" resultType = "User">
SELECT * FROM users
LIMIT #{pageSize} OFFSET #{offset}
</select>
说明:
- pageSize 每页的数量
- pageNum 页码
int offset =(pageNum - 1)* pageSize;
- 使用MyBatis插件实现分页(Page-Helper)
引入第三方分页插件,比如 PageHelper,可以简化分页操作。
PageHelper 提供静态方法,通过拦截器自动修改 SQL,添加分页参数。
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
public List<User> getPagedUsers(int pageNumber, int pageSize) {
// 开启分页
PageHelper.startPage(pageNumber, pageSize);
// 执行查询
List<User> users = userMapper.getAllUsers();
// 封装为 PageInfo 对象,包含分页信息
PageInfo<User> pageInfo = new PageInfo<>(users);
return pageInfo.getList();
}
- 自定义分页查询(推荐结合数据库查询优化)
对于复杂查询场景,可以手动优化分页逻辑,比如:
- 只查询当前页的数据,而不是全量扫描。
- 配合索引和 WHERE 条件优化性能。
Mybatis的动态代理实现(Mapper没有实现类 却可以调用)
什么是 MyBatis 动态代理?
动态代理是 Java 提供的一种机制,允许在运行时动态生成代理对象并拦截方法调用。
在 MyBatis 中,通过动态代理机制,Mapper 接口不需要手动实现,而由 MyBatis 自动生成代理实现类。
- Mapper接口:定义数据库操作
- 动态代理实现:在运行时生成接口的实现类,调用方法时由 MyBatis 的内部逻辑拦截并执行 SQL。
MyBatis 动态代理的核心原理
- Mapper 接口:用户只需要定义接口,并添加注解或编写 XML 映射文件。
- 动态代理生成实现类:MyBatis 使用 JDK 动态代理(Proxy.newProxyInstance)生成接口的代理类。
- 方法调用拦截:方法调用被动态代理拦截,并将方法名与 SQL 映射绑定,最终执行 SQL。
核心流程:
sqlSession.getMapper(Class<T> type)
方法返回一个代理对象- 调用接口方法时,代理对象会拦截方法调用,通过 MappedStatement 解析 SQL 语句并执行数据库操作
- 返回结果自动映射为 Java 对象。
动态代理的底层原理
- 生成代理对象
调用 sqlSession.getMapper(UserMapper.class) 时,MyBatis 使用 JDK 动态代理创建了一个代理对象。
Proxy.newProxyInstance(
targetInterface.getClassLoader(),
new Class<?>[]{targetInterface},
new MappperProxy<>(sqlSession, targetInterface, methodCache)
);
- 方法拦截
- 调用getUserById方法时,代理对象会调用MapperProxy.invoke()方法
- MapperProxy 查找对应的 MappedStatement,解析 SQL,并执行。
- SQL执行
- MappedStatement 包含了 SQL 配置和映射规则。
- MyBatis 使用 Executor 执行 SQL,并将结果映射到返回的对象。
实体属性和表中字段不一致解决方案
- 别名:比如order_num
select order_num OrderNum
- 通过映射
<result property = "order_Num" column = "order_num"/>
- 如果是驼峰注解用自动驼峰转换
mapUnderscoreToCamelCase = true
Mybatis嵌套查询用什么标签
- 多对一情况
<association> 标签
SQL映射文件(XML)
<resultMap id="UserResultMap" type="User">
<id property="id" column="user_id"/>
<result property="name" column="name"/>
<!-- 多对一嵌套查询 -->
<association property="address" javaType="Address" column="address_id" select="getAddressById"/>
</resultMap>
<select id="getUserById" resultMap="UserResultMap">
SELECT * FROM users WHERE user_id = #{id}
</select>
<select id="getAddressById" resultType="Address">
SELECT * FROM addresses WHERE id = #{id}
</select>
- 一对多情况
<collection> 标签
SQL 映射文件(XML)
<resultMap id="OrderResultMap" type="Order">
<id property="id" column="order_id"/>
<result property="orderName" column="order_name"/>
<!-- 一对多嵌套查询 -->
<collection property="items" ofType="OrderItem" column="order_id" select="getOrderItemsByOrderId"/>
</resultMap>
<select id="getOrderById" resultMap="OrderResultMap">
SELECT * FROM orders WHERE order_id = #{id}
</select>
<select id="getOrderItemsByOrderId" resultType="OrderItem">
SELECT * FROM order_items WHERE order_id = #{id}
</select>
- 嵌套查询的注意事项
性能问题:
- 嵌套查询可能引发 N+1 查询问题(每次主表查询都触发关联表的单独查询)。
- 如果数据量大,建议优化为连接查询或批量查询。
延迟加载:
- MyBatis 支持延迟加载,可以减少不必要的嵌套查询执行
- 通过配置文件启用:
<setting name="lazyLoadingEnabled" value="true"/>
使用Mybatis进行模糊查询
- 使用 #{} 占位符
- 特点:
#{}
会自动对参数进行 SQL 注入防护,推荐使用。 - 示例:
假设有一张用户表 users,字段包括 id 和 name,需要根据 name 进行模糊查询。
<select id = "findUserByName" result = "User">
SELECT * FROM users
WHERE name LINKE CONCAT('%', #{name}, '%')
</select>
#{name}
:自动替换为参数值并进行 SQL 注入防护。CONCAT('%', #{name}, '%')
:将输入的值拼接成 %value% 的形式,实现模糊匹配。
- 使用
${}
动态 SQL 拼接
- 特点:
${}
会直接将参数拼接到SQL中,不进行防护。需确保参数安全性,否则可能引发SQL注入。 - 示例:
<select id="findUsersByNameUnsafe" resultType="User">
SELECT * FROM users
WHERE name LIKE '%${name}%'
</select>
- 使用动态标签
- 特点:
利用 MyBatis 的<if>
和<trim>
标签,动态生成模糊查询条件。
<select id = "findUsers" resultType = "User">
SELECT * FROM users
WHERE 1=1
<if test = "name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test = "enmail != null and name != ''">
AND email LIKE CONCAT('%', #{email},'%')
</if>
</select>
- 配合 MyBatis Plus 或插件(高级方式)
如果使用了 MyBatis Plus,可以直接利用其内置的查询条件构造器,简化模糊查询的实现。
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", "John"); // 相当于 name LIKE '%John%'
List<User> users = userMapper.selectList(wrapper);
- 使用
<bind>
实现模糊查询
<select id="findUsersByName" resultType="User">
<bind name="searchName" value="'%' + name + '%'" />
SELECT * FROM users
WHERE name LIKE #{searchName}
</select>
推荐将 bind 与 #{} 一起使用,以避免 SQL 注入风险,并确保参数的安全性
Mybatis类型转换器
全局配置
<typeHandlers>
<typeHandler handler="com.example.typehandler.CustomTypeHandler"/>
</typeHandlers>
局部配置
<resultMap id="userResultMap" type="User">
<result property="isActive" column="active" typeHandler="com.example.typehandler.BooleanTypeHandler"/>
</resultMap>
SqlSessionFactoryBuilder生命周期
SqlSessionFactoryBuilder作用
SqlSessionFactoryBuilder 主要负责从配置文件(通常是 mybatis-config.xml)中读取 MyBatis 的配置信息,并根据这些配置信息构建 SqlSessionFactory。这个工厂类是 MyBatis 框架启动和初始化的关键组件之一。
SqlSessionFactory 的作用
SqlSessionFactory 是 SqlSession 的工厂类,它负责创建和配置 SqlSession。一个 SqlSessionFactory 对应一个数据库连接池,通常应用程序在启动时创建一次 SqlSessionFactory 实例,然后使用它来创建多个 SqlSession 实例。
SqlSession 提供的主要功能:
- 执行SQL:通过 SqlSession 执行查询、插入、更新、删除等 SQL 操作。
- 获取映射器接口示例:通过 getMapper() 方法获取 Mapper 接口实例,使用这些接口来执行具体的数据库操作。
- 管理事务:SqlSession 可以通过手动提交或回滚来管理数据库事务。
MyBatis插件运行原理
具体原理
- 接口拦截:MyBatis 插件通过拦截器(Interceptor)接口来拦截某些方法的调用。MyBatis 提供了多种接口类型供开发者拦截,包括 Executor、StatementHandler、ParameterHandler、ResultSetHandler 等。插件会在这些接口的实现方法上进行增强。
- 插件注册:在 MyBatis 配置文件中,通过
<plugins>
标签来注册自定义插件。MyBatis 会在初始化时扫描并加载这些插件,并将插件应用到相应的执行流程中。 - 动态代理:MyBatis 使用 Java 动态代理技术(通过 java.lang.reflect.Proxy)为 Executor、StatementHandler 等接口生成代理对象,并在这些对象的方法执行前后进行插入自定义逻辑。比如,你可以在执行 SQL 前后记录日志,或者修改 SQL 语句。
- 拦截方法:当 MyBatis 执行 SQL 操作时,会通过插件拦截器触发自定义的方法。在插件的 intercept 方法中,可以根据需要修改 SQL、修改参数、记录日志等。
MyBatis缓存
MyBatis 提供了缓存机制来提高数据库访问的效率,减少不必要的数据库查询操作。MyBatis 缓存的工作原理是将查询结果存储在内存中,避免重复查询相同的数据。当数据在缓存中存在时,MyBatis 会直接从缓存中获取数据,而不需要再次访问数据库。
缓存的工作原理
- 一级缓存(Local Cache):默认启用的缓存机制,缓存是作用于一个 SqlSession 的生命周期内,属于每个会话的本地缓存。
- 二级缓存(Global Cache):是跨多个 SqlSession 共享的缓存,它是通过 Mapper 映射文件来共享的。二级缓存的作用范围大于一级缓存,可以共享多个会话之间的数据。
一级缓存
一级缓存是默认启用的缓存,它存在于 SqlSession 的生命周期中。也就是说,在同一个 SqlSession 内,查询的结果会被缓存。一级缓存不会跨 SqlSession 共享数据,因此,关闭 SqlSession 后,一级缓存的数据会被清空。
- 一级缓存是 MyBatis 默认启用的,并且是基于 SqlSession 的。
- 缓存粒度:缓存的粒度是 SQL 查询,因此同一个 SQL 查询的结果会被缓存。
- 生命周期:一级缓存的生命周期与 SqlSession 相同。当 SqlSession 被关闭时,缓存也会被清除。
- 使用方式:不需要任何额外配置,只要使用 SqlSession 执行查询,查询结果会被缓存。
二级缓存
二级缓存是跨 SqlSession 的缓存,它是在多个 SqlSession 之间共享的缓存。二级缓存的作用域是 Mapper 映射文件,也就是说,不同 SqlSession 使用相同 Mapper 的查询时,数据会被存储在二级缓存中。
- 需要手动启用:二级缓存默认是关闭的,需要通过配置启用。
- 跨 SqlSession 共享:不同的 SqlSession 可以共享相同 Mapper 中的缓存数据。
- 缓存粒度:缓存的粒度是 Mapper,同一个 Mapper 下的查询可以共享缓存数据。
- 配置:二级缓存需要在 mybatis-config.xml 和 Mapper 映射文件中进行配置。
- 启用二级缓存
<configuration>
<!-- 启用全局二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
- 在 Mapper 文件中启用缓存
<mapper namespace="com.example.mapper.UserMapper">
<!-- 启用缓存 -->
<cache/>
<!-- 其他 SQL 映射 -->
</mapper>
- 二级缓存的回收策略
LRU(最近最少使用):缓存策略是一种常用的回收策略。LRU 缓存会移除最长时间未使用的缓存数据,以保证缓存的大小在一定范围内。
IFO(先进先出):按照对象进入缓存的顺序来移除
SOFT(软引用):移除基于垃圾收集器的状态和软引用规则的对象
WEAK(弱引用):更积极地移除基于垃圾收集器和弱引用规则对象
3.什么是Hibernate
Hibernate 是一个功能强大的 Java ORM(Object-Relational Mapping)框架,它提供了一个简单的方式来将 Java 对象与数据库中的表进行映射,从而简化了数据库操作。它能够自动生成 SQL 并执行,减少了开发人员编写 SQL 的负担,同时提供了一些优化和扩展机制。
Hibernate的核心特点
自动化的数据操作:
- Hibernate自动生成SQL语句来进行数据库操作,开发者只需要操作Java对象,不要编写原生的SQL
对象关系映射(ORM)
- 将Java类与数据库表进行映射,属性与字段映射,提供了对象和关系型数据库之间的桥梁
持久化管理
- Hibernate提供了持久化机制,将Java对象的状态同步到数据库表中
透明持久化
- 无需显式得处理对象和数据库的转换,Hibernate自动处理对象的持久化操作
查询语言
- Hibernate提供了Hibernate Query Language(HQL),一种类似于 SQL 的面向对象查询语言
- 支持 Criteria API 和 SQL 查询,可以灵活地进行数据库查询
缓存机制:
- Hibernate 提供了 一级缓存 和 二级缓存,可以显著提升查询性能
数据库独立性:
- Hibernate 可以支持多种数据库(如 MySQL、PostgreSQL、Oracle 等),使应用程序不依赖于具体的数据库类型
事务管理:
- Hibernate 支持声明式事务管理,和 JTA(Java Transaction API)集成,可以确保事务的一致性和原子性。
Hibernate核心组成部分
SessionFactory:
- 是 Hibernate 的核心接口,用于创建和管理 Session 对象。SessionFactory 会读取配置文件,连接数据库,并提供 Session 的实例。
Session:
- Session 是与数据库交互的接口,负责执行数据库操作(如持久化、查询、删除、更新等)。
- 一个 Session 对象代表一个数据库会话,它通常对应一个事务
Transaction
- Hibernate 管理事务,确保操作的原子性。每个操作都应在事务中完成。
Mapping 文件
- Hibernate 通过 XML 映射文件 或 注解 来定义 Java 对象与数据库表之间的映射关系。
Query
- Hibernate 提供了多种查询方式:HQL(Hibernate Query Language)、Criteria API 和原生 SQL。
Cache
- Hibernate 提供了 一级缓存(与 Session 相关)和 二级缓存(跨 Session 的缓存),用来提高性能
核心功能
持久化对象
- Hibernate 可以自动将 Java 对象保存到数据库,也可以从数据库中加载数据到 Java 对象中。
查询功能
Hibernate 提供了多种查询方式
- HQL:Hibernate Query Language,面向对象的查询语言。
- Criteria API:通过 Criteria 构造查询。
- 原生 SQL:可以直接使用 SQL 查询数据库。
关系映射
- 支持 一对一、一对多、多对多 等关系的映射。
- 使用 外键 或 连接表 来表示关系。
级联操作
- Hibernate 支持级联操作,可以让数据库的增删改操作自动传播到相关的对象。
懒加载和预加载
- 支持 懒加载(Lazy Loading)和 预加载(Eager Loading),帮助优化性能。
事务支持
- Hibernate 支持与 Spring、Java EE 等框架集成,提供事务管理功能,确保数据一致性。
Hibernate的优点
- 无需管理数据库的连接,直接配置在xml中即可
- 一个会话,不要操作多个对象,而是操作Session对象
Hibernate的缺点
- 全表映射带来的不便,如果更新某个字段,需要发送所有的字段
- 无法根据不同的条件组装不同的Sql
- 对多表查询和复杂SQL的支持较差,需要自己写SQL,还需要自己数据组装成POJO
- 不能有效支持存储过程
- 虽然有HQL,但是性能很差,大型互联网需要优化SQL
4.Mybatis和Hibernate之间的比较
特性 | Hibernate | Mybatis |
---|---|---|
SQL控制 | 自动生成SQL | 需要手动编写SQL |
学习曲线 | 较高,需要学习HQL、Criteria API 等 | 较低,直接使用SQL语句 |
灵活性 | 较低,复杂查询需要调优生成的 SQL | 高,完全控制 SQL |
缓存支持 | 支持一二级缓存 | 不提供内建缓存机制 |
适用场景 | 对象关系映射较多的应用、简化 SQL 操作的场景 | 需要精细控制 SQL 的场景 |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。