六、MyBatis
什么是Mybatis
Mybatis是一个半ORM框架,它简化了JDBC操作,让开发者只需关注SQL语句本身,而无需处理底层连接、语句创建等细节。Mybatis支持XML或注解配置,可灵活映射POJO与数据库记录,自动生成并执行SQL,最终将结果映射回Java对象。这大大减少了JDBC代码量,提高了开发效率。
Mybatis的优缺点(重点)
优点
- 基于SQL编程,灵活度高,不影响应用程序或数据库设计。SQL写在XML里面,与代码解耦,便于管理。提供XML标签,支持编写动态SQL语句,并可重用。
- 与JDBC相比,代码量大幅减少,无需处理连接等冗余代码。
- 兼容各种数据库,因为Mybatis底层使用JDBC来连接数据库,所以只要JDBC支持的数据库Mybatis都支持。
- 能够与Spring很好地集成。
- 提供映射标签,支持对象与数据库的字段关系映射。
缺点
- 当字段和关联表较多时,SQL编写工作量大,对开发人员编写SQL 语句的功底有一定要求。
- SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis框架适用场合
- 需要灵活DAO层解决方案:Mybatis专注于SQL,提供了足够的灵活性。
- 性能要求高或需求变化多:如互联网项目,Mybatis是不错的选择。
Mybatis组件
-
SqlSessionFactoryBuilder:的作用是生成SqlSessionFactory实例,一旦生成了,它的生命周期就结束了。
-
SqlSessionFactory:用于创建SqlSession实例,它是重量级的,包含了创建SqlSession所需的全部配置信息。在整个应用的生命周期内被重用,而不是每次需要时都创建一个新的实例。
-
SqlSession:提供了在数据库上执行命令所需的所有方法,类似于JDBC中的Connection。它是轻量级的,应用于每个方法调用或每个事务中,方法结束时,应该关闭SqlSession,以释放数据库资源。
-
Mapper:当你通过SqlSession调用getMapper(Class type)方法时,MyBatis会动态地生成一个实现了该Mapper接口的代理对象。这个代理对象的生命周期与SqlSession相同。
MyBatis与Hibernate有哪些不同(重点)
ORM(Object-Relational Mapping)是一种技术,它让开发者能在面向对象的语言中,通过对象与数据库表之间的映射,直接操作数据库数据,无需编写SQL或处理数据库底层细节。
开发模式与自动化程度
- Hibernate:全自动的对象关系映射(ORM)框架,几乎无需手写SQL,降低开发难度。
- MyBatis:半自动的ORM框架,手写SQL,灵活控制数据库操作。
SQL管理与优化
- Hibernate:自动生成SQL,复杂查询可能不够优化,性能问题。
- MyBatis:手写SQL,易于优化,支持动态SQL,SQL语句更加灵活复用。
学习难度
- Hibernate:需理解映射、事务、缓存等复杂概念,才能有效使用Hibernate。
- MyBatis:上手相对容易,尤其对于熟悉JDBC的开发者。
预编译的作用
预编译主要对SQL的执行进行优化。经过预编译的SQL多数情况下可以直接执行,数据库服务器无需再次编译,从而提升了性能。此外,预编译语句对象可以重复利用,对于相同的SQL,可以直接使用缓存的预编译对象,进一步提高了效率。
更为关键的是,预编译有助于防止SQL注入攻击。由于预编译后的参数不会再进行SQL编译,系统默认其为普通参数而非SQL语句,从而增强了系统的安全性。
MyBatis中${}和#{}的区别(重点)
#{}可以有效防止SQL注入,#{}是通过PreparedStatement的占位符?来实现的。PreparedStatement会先对SQL语句进行预编译,然后再将参数值设置到占位符上,这样就避免了SQL注入的风险。
${}是字符串替换、是拼接符。MyBatis在处理${}时,会把sql中的${}替换成变量的值。
#{}对应的变量值在替换时,对于字符串类型的变量,会自动在变量值周围加上单引号,以符合SQL语法。
${}替换后的变量值不会自动加上单引号。
MyBatis的插件运行原理,如何编写一个插件。
MyBatis插件机制主要依赖于JDK 的动态代理技术,为特定的接口( Executor、StatementHandler、ParameterHandler、ResultSetHandler)创建代理对象来拦截并增强这些接口方法。
编写插件步骤
- 编写一个类实现MyBatis的Interceptor 接口,并重写 intercept() 方法。在这个方法中,你可以使用反射来调用目标方法,并在调用前后添加自定义逻辑。
- 在插件类上使用 @Intercepts注解标识它是一个插件,并使用 @Signature注解指定要拦截的接口、方法以及参数类型。
- 在MyBatis的配置文件中(如 mybatis-config.xml),使用 标签来配置自定义插。或者在MyBatis的配置文件(MpConfig),使用@Bean注解配置自定义插件。
//自定义插件
@Intercepts({
@Signature(
type = Executor.class,
method = "update",
args = {MappedStatement.class, Object.class})
})
public class ExamplePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("Before update method");
Object result = invocation.proceed(); // 调用原始方法
System.out.println("After update method");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
//可以使用 properties 来传递配置参数
}
}
MyBatis一级缓存和二级缓存的区别
作用域:一级缓存是SqlSession级别的,仅在当前SqlSession中有效;而二级缓存是mapper级别的,整个应用共享,可以跨线程使用。
开启方式:一级缓存默认开启,无需额外配置;而二级缓存默认不开启,需要手动配置。
缓存地址:一级缓存主要存储在SqlSession内部;二级缓存则通常存储在应用的外部,如内存或硬盘。
存储对象:一级缓存存储的是具体的查询结果集;二级缓存存储的则是mapper映射文件对应的namespace下的数据对象。
xml中结果类型映射方式
- 通过在查询的 sql 语句中定义字段名的别名,让字段名的别名和实体类 的属性名一致。
<select id=”selectorder” parametertype=”int” resultetype=” me.gacl.domain.order”>
select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>
- 通过来映射字段名和实体类属性名的一一对应的关系。
<resultMap type=”me.gacl.domain.order” id=”orderMap”>
<!–用id属性来映射主键字段–>
<id property=”id” column=”order_id”>
<!–用result属性来映射非主键字段,property为实体类属性名,column 为数据表中的属性–>
<result property = “orderno” column =”order_no”/>
<result property=”price” column=”order_price” />
</reslutMap>
<select id="getOrder" parameterType="int" resultMap="orderMap">
select * from orders where order_id=#{id}
</select>