MyBatis
MyBatis、JDBC、Statement与PreparedStatement区别?#{}、${}区别? √模糊查询?一对一、一对多查询?批量操作?动态SQL?作用?原理?
半ORM(对象关系映射)框架,封装了JDBC,开发时只关注SQL语句,不用处理加载驱动、创建连接等繁杂的过程,灵活度高。但sql语句编写工作量大,要求高,数据库移植性差
Java DataBase Connectivity用于执行SQL的API接口
//1.注册驱动
Class.forName(“com.mysql.jdbc.Driver”);
//2.获得链接
connection conn = DirverManger.getConnection(jdbc:mysql://ip:port/test,,密码);
String sql = "select * from t where a = ?";
PreparedStatement psmt = conn.prepareStatement(sql);//3.获得预处理对象
//void setXxx(int index, Xxx xx);
psmt.setString(1,'张三');//4.设置实际参数
//int executeUpdate(); --执行insert update delete语句.
//ResultSet executeQuery(); --执行select语句.
//boolean execute(); --执行select返回true 否则返回false
ResultSet rs = psmt.executeQuery();//5.执行sql
while( rs.next() ){//6.处理结果集
Object getObject(int index/String name) 获得任意对象
String getString(int index/String name) 获得字符串
int getInt(int index/String name) 获得整形
double getDouble(int index/String name) 获得双精度浮点型
}
rs.close();//7.释放资源
stmt.close();
con.close();
用途:不~;支持参数化查询
性能:每次都解析和编译,性能差;预编译,高性能
批量操作:不支持,支持
安全性:不能~;防止SQL注入
#{}
是占位符,预编译处理;${}
是拼接符,没有~#{}
能防止SQL注入,提高系统安全性;${}
不能~#{}
的变量替换是在DBMS中;${}
的~外#{}
自动转换java类型和jdbc类型。自动加上'';${}
不~~不超过最大限制max_allowed_packet(1M)
LIKE CONCAT('%',#{question},'%')或者"%"#{question}"%"
association一对一关联对象,collection一对多查询关联集合对象的延迟加载。
用foreach标签:属性item,index,collection,open,separator,close。
用ExecutorType.BATCH
以XML标签的形式编写,完成逻辑判断和动态拼接SQL的功能。
动态SQL标签:<if />、<choose />、<when />、<otherwise />、<trim />、<where />、<set />、<foreach />、<bind />
原理:使用OGNL的表达式,从SQL参数对象中计算表达式的值
Mybatis延迟加载?原理?
配置文件:lazyLoadingEnabled=true
使用CGLIB创建目标对象的代理对象,调用目标方法时进入拦截器方法,如a.getB().getName(),拦截器invoke()方法发现a.getB()是null则发送事先保存好的查询关联B对象的sql查询B并调用a.setB(b)
Hibernate同理
MyBatis事务管理形式?
使用JDBC的事务管理机制。利用java.sql.Connection对象
使用MANAGED的事务管理机制。让容器如WebLogic、JBOSS
MyBatis一级缓存和二级缓存?
不推荐,因为分布式环境下缓存基于本地,会有脏数据,不如直接使用Redis、Memcached等分布式缓存
都是基于PerpetualCache没有容量限定的HashMap缓存
一级缓存(默认开):存储作用域为sqlSession或者statement,建议设为Statement。配置:<setting name="localCacheScope" value="SESSION|STATEMENT"/>
二级缓存:~Mapper(Namespace),可自定义存储源Ehcache。配置:<setting name="cacheEnabled" value="true"/>
在映射XML中配置cache或者cache-ref
C/U/D操作后清除作用域下select缓存
MyBatis执行顺序√SQLSession 流程,核心组件
读取mybatis-config.xml,生成 Configuration,存储全局配置。
加载 Mapper.xml 文件:生成 MappedStatement 和其他映射信息,存储到 Configuration。
SqlSessionFactoryBuilder 通过Configuration创建 SqlSessionFactory:
SqlSessionFactory创建 SqlSession 对象:用于执行SQL、处理事务
Executor根据MappedStatement动态生成 SQL,执行 SQL 和维护缓存:处理参数绑定、缓存管理,并调用数据库。
输入参数映射:通过 MappedStatement 将传入参数映射到 SQL。
输出结果映射:通过 MappedStatement 将结果集映射为 Java 对象。
调用Mapper接口方法执行SQL
SqlSession 根据调用的 StatementID(Mapper接口的全限定名+方法名)找到MappedStatement对象(包含 SQL 的配置信息)
Executor根据MappedStatement对象生成动态SQL、维护一二级缓存
SQL参数转化、动态SQL拼接,生成JDBC Statement 对象。
使用 ParameterHandler 将参数填充到SQL占位符中,
使用 StatementHandler 将参数绑定到JDBC Statement对象。
JDBC statement对象执行 SQL 语句。返回 ResultSet结果集。
ResultSetHandler 使用 MappedStatement 中的结果映射关系将ResultSet结果集映射为Java对象
关闭SqlSession,释放资源
Configuration:保存全局配置信息(如jdbc数据源、SQL映射文件等)
SqlSession:面向用户的接口,封装了JDBC操作
Executor:用于和数据库交互
MappedStatement:用于描述SQL配置信息,存储 SQL 映射信息,如 SQL 语句、参数映射、结果映射等
StatementHandler:封装了对JDBC中Statement 对象的操作
TypeHandler:类型处理器,用于Java类型与基础类型之间的转换。
ParameterHandler:用于为SQL的参数占位符设置值。
ResultSetHandler:将结果集ResultSet对象转换为Java实体对象
Mapper接口与XML对应关系?Mapper接口方法能重载吗?映射Enum枚举类?Executor执行器分类及区别?
接口的全限名、方法名、方法参数映射文件namespace值、MappedStatement的id值、SQL的参数
不能重载,因为是全限名+方法名的保存和寻找策略。
原理:select、insert、update、delete标签都解析为MappedStatement对象。Mapper接口的实现类通过使用JDK动态代理自动生成代理对象Proxy时会拦截接口方法,根据接口全限名+方法名拼接字符串作为key值,唯一定位一个对应的MappedStatement执行SQL
图
![sqlexecutionprocedure.png](https://b.bdstatic.com/comment/I4MgmLj55Sgosm2EdtLMDQ6fc9b6abdff1d6b729e17adb0f9ad16c.png)
EnumTypeHandler基于Enum.name枚举名称(String)。默认。
EnumOrdinalTypeHandler基于Enum.ordinal枚举数值(int)。
设置<setting name="defaultEnumTypeHandler" value="EnumOrdinalTypeHandler" />
通过自定义TypeHandler类实现#setParameter()和getResult()接口完成从javaType和jdbcType双向转换
SimpleExecutor:每次执行update或select都创建Statement对象,用完后立刻关闭
ReuseExecutor:执行update或select时以SQL作为key查找缓存的Statement对象,存在就使用,不存在就创建;用完后放入缓存Map<String, Statement>内
BatchExecutor:执行update操作调用addBatch方法将所有SQL都添加到批处理中,等待executeBatch方法统一执行
CachingExecutor :在三个执行器之上增加二级缓存功能<setting name="defaultExecutorType" value="SIMPLE、REUSE、BATCH">
分别使用上面三个执行器<setting name="cacheEnabled" value="">
value=true时创建 CachingExecutor执行器
Mybatis插件原理?自定义插件?分页插件原理?
基于JDK动态代理,拦截ParameterHandler、ResultSetHandler、StatementHandler、Executor对象的方法
实现Interceptor接口实现intercept(Invocation invocation)方法,给插件添加注解指定要拦截哪接口的方法,在配置文件中配置插件
@Intercepts({
@Signature(
type = StatementHandler.class, // 拦截对象类型
method = "prepare", // 拦截方法
args = {Connection.class, Integer.class} // 方法参数
)
})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 被拦截方法名
System.out.println("拦截方法: " + invocation.getMethod().getName());
long startTime = System.currentTimeMillis();
// 执行目标方法
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
System.out.println("方法执行时间: " + (endTime - startTime) + "ms");
return result;
}
@Override
public Object plugin(Object target) {
// 判断是否需要生成代理对象
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 读取配置文件的属性
}
}
Configuration configuration = new Configuration();
MyPlugin myPlugin = new MyPlugin();
Properties properties = new Properties();
properties.setProperty("param1", "value1");
myPlugin.setProperties(properties);
configuration.addInterceptor(myPlugin);
不推荐:使用RowBounds对象对ResultSet结果集执行内存分页,而非数据库分页
推荐:手动或用分页插件给SQL添加分页参数
原理:用插件接口拦截Executor的query方法添加分页参数
分页插件:Mybatis-PageHelper MyBatis-Plus