拉勾 mybatis
初始化
1.创建git仓库
1.新建一个目录 然后点击右键 git base here 创建git (会弹出一个窗口)
2.初始化 再窗口输入 git init
3.指定仓库 git clone 仓库地址
4.上传文件 点击右键 git提交->master (选择提交并推送)
1. jdbc连接数据库的缺陷
1.1 代码
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/lgstudy?characterEncoding=utf-8", "root", "root");
//---有参数
// //?占位符
// String sql = "select * from user where username = ?";
// //获取预处理statement
// preparedStatement = connection.prepareStatement(sql);
// // 设置参数
// preparedStatement.setString(1, "占山");
//---无参数
String sql = "select * from user";
preparedStatement = connection.prepareStatement(sql);
// 执行sql查询结果集
resultSet = preparedStatement.executeQuery();
//遍历结果集
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
User user = new User();
//封装user
user.setId(id);
user.setUsername(username);
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally
{
// 释放资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
1.2 缺陷
1.连接数据库的配置信息硬编码 ===》存到配置文件中
2.数据库的频繁断开连接==》连接池
3.sql语句,设置参数 硬编码 ==》存到配置文件中 设置参数 反射
4.手动遍历封装结果集 ==》 内省
2. 自定义持久层框架
2.1 思路
2.1.1使用端
1.引入自定义持久层的包
2.提供两部分配置信息:
1.数据库的连接配置信息 sql配置信息
2.mapper.xml 存放sql配置信息
2.1.2自定义持久层框架(封装jdbc的操作)
1.加载配置文件;根据配置文件的路径,加载配置成字节输入流,存储在内存中
2.创建两个javaBean(容器)
Configuration核心配置类:存放sql的配置信息
MappedStatement映射配置类 存放mapper.xml信息
3.解析配置文件 dom4j
创建类sqlSessionFactoryBuilder
1.使用dom4j解析配置文件,将解析出来的文件存放到javabean(容器)中
2.创建sqlSessionFactory对象,生产session (工厂模式)
4.创建sqlSessionFactory接口实现类
openSession
5.创建sqlSession接口及DefaultSession
定义数据库的crud操作 selectlist()
update()
6.执行Exector接口及实现类SimpleExector
query()//执行jdbc代码
2.1.3自定义持久层框架(缺陷)
1.dao层,存在代码重复,整个操作的过程模版重复(加载配置文件)
2.statementId存在硬编码
========》jdk动态代理
Proxy.newProxyInstance InvocationHandler
2.2 代码实现
2.2.1 使用端
1.创建项目
1.创建lagou-mybatis项目(maven骨架项目)
2.创建sqlMapConfig.xml(sql源配置文件)
<configuration>
<!--数据库配置信息-->
<dataSource>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///lgstudy"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
<!--存放mapper.xml的全路径-->
<mapper resource="UserMapper.xml"></mapper>
</configuration>
#####2.创建UserMapper.xml(user类的sql)
<!--namespace 命名空间 当前xml的-->
<!--
有三种全路径:namespace绑定实体类的全路径,绑定dao接口的全路径,绑定mapper的sql.xml文件。
第一种:namespace绑定实体类的全路径:
第二种:namespace绑定dao层接口的全路径:
第三种:namespace绑定的是mapper接口对应的sql.xml。
https://www.cnblogs.com/zjdxr-up/p/8681382.html
-->
<mapper namespace="com.test.dao.IUserDao">
<!--sql的唯一标识:namespace.id来组成 : statementId-->
<select id="findAll" resultType="com.test.pojo.User" >
select * from user
</select>
<select id="findOneById" resultType="com.test.pojo.User" paramterType="com.test.pojo.User">
select * from user where id = #{id}
</select>
</mapper>
2.2.2 框架端
1.创建项目
1.创建lagou-mybatis项目(maven骨架项目)
2.pojo类
Configuration 用于存储 数据库源
public class Configuration {
private DataSource dataSource;
/*
* key: statementid value:封装好的mappedStatement对象
* */
Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
}
MappedStatement 用于存储 sql文件的引用
//存储的SQL xml文件的映射对象
public class MappedStatement {
//id标识
private String id;
//返回值类型
private String resultType;
//参数值类型
private String paramterType;
//sql语句
private String sql;
}
3.io类
Resources
public class Resources {
// 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中
public static InputStream getResourceAsSteam(String path){
InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
}
}
4.扫描类 封装 UserMapper
public class XMLMapperBuilder {
private Configuration configuration;
private String namespace;
public XMLMapperBuilder(Configuration configuration) {
this.configuration =configuration;
}
public void parse(InputStream inputStream) throws DocumentException {
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
//获取定义的命名空间
namespace = rootElement.attributeValue("namespace");
getNodes(rootElement);
}
public void getNodes(Element rootElement){
//获取标签名称
String name = rootElement.getName();
List<Element> list = rootElement.selectNodes("//"+name);
for (Element element : list) {
String id = element.attributeValue("id");
String resultType = element.attributeValue("resultType");
String paramterType = element.attributeValue("paramterType");
String sqlText = element.getTextTrim();
if(id!=null){
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setResultType(resultType);
mappedStatement.setParamterType(paramterType);
mappedStatement.setSql(sqlText);
mappedStatement.setTypeValue(name);
//封装key
String key = namespace+"."+id;
configuration.getMappedStatementMap().put(key,mappedStatement);
}
}
//递归遍历当前节点所有的子节点
List<Element> listElement=rootElement.elements();//所有一级子节点的list
for(Element e:listElement){//遍历所有一级子节点
this.getNodes(e);//递归
}
}
}
5.方法反射类
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
}
@Override
public <E> List<E> selectList(String statementId, Object... param) throws Exception {
//调用执行器
//将要去完成对simpleExecutor里的query方法的调用
simpleExecutor simpleExecutor = new simpleExecutor();
//根据statementId获取Mapped
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
List<Object> list = simpleExecutor.query(configuration, mappedStatement, param);
return (List<E>) list;
}
@Override
public <T> T selectOne(String statementId, Object... params) throws Exception {
List<Object> objects = selectList(statementId, params);
if (objects.size() == 1) {
return (T) objects.get(0);
} else {
throw new RuntimeException("查询结果为空或者返回结果过多");
}
}
@Override
public int update(String statementId, Object... params) throws Exception {
simpleExecutor simpleExecutor = new simpleExecutor();
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
int update = simpleExecutor.update(configuration, mappedStatement, params);
return update;
}
@Override
public <T> T getMapper(Class<?> mapperClass) {
// 使用JDK动态代理来为Dao接口生成代理对象,并返回
// loader: 用哪个类加载器去加载代理对象
// interfaces:动态代理类需要实现的接口
// h:动态代理方法在执行时,会调用h里面的invoke方法去执行
Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
//proxy 当前代理对象的应用
//method 当前被调用方法的引用
//args 传递的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 底层都还是去执行JDBC代码
//根据不同情况,来调用selctList或者selectOne
// 准备参数 1:statmentid :sql语句的唯一标识:namespace.id= 接口全限定名.方法名
// 方法名:findAll
String methodName = method.getName();
String className = method.getDeclaringClass().getName();
Object result = new Object();
String statementId = className + "." + methodName;
//com.test.dao.IUserDao.updateById
//com.test.dao.IUserDao.update
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
//获取方法的标签值
String typeValue = mappedStatement.getTypeValue();
switch (typeValue) {
case "insert": {
result = update(statementId, args);
break;
}
case "update": {
result = update(statementId, args);
break;
}
case "delete": {
result = update(statementId, args);
break;
}
case "select": {
Type genericReturnType = method.getGenericReturnType();
// 判断是否进行了 泛型类型参数化
if (genericReturnType instanceof ParameterizedType) {
List<Object> objects = selectList(statementId, args);
result= objects;
break;
}else{
result= selectOne(statementId, args);
break;
}
}
}
return result;
}
});
return (T) proxyInstance;
}
}
6.JDbc执行类
public class simpleExecutor implements Executor {
@Override //user
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
// 1. 注册驱动,获取连接
Connection connection = configuration.getDataSource().getConnection();
// 2. 获取sql语句 : select * from user where id = #{id} and username = #{username}
//转换sql语句: select * from user where id = ? and username = ? ,转换的过程中,还需要对#{}里面的值进行解析存储
String sql = mappedStatement.getSql();
BoundSql boundSql = getBoundSql(sql);
// 3.获取预处理对象:preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
// 4. 设置参数
//获取到了 参数的全路径
String paramterType = mappedStatement.getParamterType();
Class<?> paramtertypeClass = getClassType(paramterType);
List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
for (int i = 0; i < parameterMappingList.size(); i++) {
ParameterMapping parameterMapping = parameterMappingList.get(i);
String content = parameterMapping.getContent();
//反射
Field declaredField = paramtertypeClass.getDeclaredField(content);
//暴力访问
declaredField.setAccessible(true);
Object o = declaredField.get(params[0]);
preparedStatement.setObject(i+1,o);
}
// 5. 执行sql
ResultSet resultSet = preparedStatement.executeQuery();
//获取返回值类
String resultType = mappedStatement.getResultType();
Class<?> resultTypeClass = getClassType(resultType);
ArrayList<Object> objects = new ArrayList<>();
// 6. 封装返回结果集
while (resultSet.next()){
//反射new一个类
Object o =resultTypeClass.newInstance();
//元数据
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
// 字段名
String columnName = metaData.getColumnName(i);
// 字段的值
Object value = resultSet.getObject(columnName);
//使用反射或者内省,根据数据库表和实体的对应关系,完成封装
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(o,value);
}
objects.add(o);
}
return (List<E>) objects;
}
@Override
public int update(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
//update user set username=#{} where id=#{}
// 1. 注册驱动,获取连接
Connection connection = configuration.getDataSource().getConnection();
// 2. 获取sql语句 : select * from user where id = #{id} and username = #{username}
//转换sql语句: select * from user where id = ? and username = ? ,转换的过程中,还需要对#{}里面的值进行解析存储
String sql = mappedStatement.getSql();
BoundSql boundSql = getBoundSql(sql);
// 3.获取预处理对象:preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
// 4. 设置参数
//获取到了 参数的全路径
String paramterType = mappedStatement.getParamterType();
Class<?> paramtertypeClass = getClassType(paramterType);
List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
for (int i = 0; i < parameterMappingList.size(); i++) {
ParameterMapping parameterMapping = parameterMappingList.get(i);
String content = parameterMapping.getContent();
Object o =null;
if(mappedStatement.getTypeValue().equals("delete")){
o= params[i];
}else{
//反射
Field declaredField = paramtertypeClass.getDeclaredField(content);
//暴力访问
declaredField.setAccessible(true);
o = declaredField.get(params[0]);
}
preparedStatement.setObject(i+1,o);
}
// 5. 执行sql
int i1 = preparedStatement.executeUpdate();
return i1;
}
private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
if(paramterType!=null){
Class<?> aClass = Class.forName(paramterType);
return aClass;
}
return null;
}
/**
* 完成对#{}的解析工作:1.将#{}使用?进行代替,2.解析出#{}里面的值进行存储
* @param sql
* @return
*/
private BoundSql getBoundSql(String sql) {
//标记处理类:配置标记解析器来完成对占位符的解析处理工作
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
//解析出来的sql
String parseSql = genericTokenParser.parse(sql);
//#{}里面解析出来的参数名称
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
BoundSql boundSql = new BoundSql(parseSql,parameterMappings);
return boundSql;
}
}
7.测试类
@Test
public void testProxyDao() throws IOException {
InputStream resourceAsStream =
Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findById(1);
System.out.println(user);
sqlSession.close();
}
2.2.3打jar问题
1.在pom文件中添加编译属性
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
2.idea中点击 projrct Structure =>点击module 设置项目的编译版本
3.srtting=> builder=>compiler=>java compiler设置编译版本
3.mybatis快速启动
3.1导依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!--引入依赖-->
<dependencies>
<!--mybatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</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>
</dependency>
</dependencies>
4 自定义插件
xml
<plugins>
<plugin interceptor="com.lagou.plugin.MyPlugin">
<property name="name" value="Bob"/>
</plugin>
</plugins>
java
package com.lagou.plugin;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.util.Properties;
@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("增强了");
return invocation.proceed();
}
//把这个拦截器生成一个代理放到拦截器链中
@Override
public Object plugin(Object target) {
System.out.println("将要包装的目标对象"+target);
return Plugin.wrap(target,this);
}
//插件初始化时候调用,只调用一次 获取配置文件的属性
@Override
public void setProperties(Properties properties) {
System.out.println("获取配置文件的属性"+properties);
}
}
mybatis 拦截的类
一、executor,executor类可以说是执行sql的全过程,如组装参数,sql改造,结果处理,比较广泛,但实际用的不多
二、StatementHandler,这个是执行sql的过程,可以获取到待执行的sql,可用来改造sql,如分页,分表,最常拦截的类
三、paremeterHandler,这个用来拦截sql的参数,可以自定义参数组装规则
四、resultHandler,这个用来处理结果
5 缓存
一级缓存
二级缓存
二级缓存是序列化存储 需要实现POJO需要实现Serializable接口
<settings>
<setting name="cacheEnabled" value="true" />
</settings>
//在sql的mapper的添加 <cache/>表填
问题
1.缓存使用顺序
先到二级缓存当中查找
如果二级缓存中没有,就去找一级缓存
如果一级缓存中也没有就去到数据库当中查询
2.二级Chache使用原则
1.只能在一个命名空间下使用二级缓存
由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。
2.在单表上使用二级缓存
如果一个表与其它表有关联关系,那么久非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰,所以就有可能出现多个namespace中的数据不一致现象。
3.查询多于修改时使用二级缓存
在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。
java补充
1.java类型 T,E,K,V,?
泛型中的通配符
?表示不确定的 java 类型
?无界通配符
例子 List<? extends Animal> listAnimals
T (type) 表示具体的一个java类型
List<T>是确定的某一个类型
K V (key value) 分别代表java键值中的Key Value
E (element) 代表Element
上界通配符 < ? extends E>
下界通配符 < ? super E>
T和?运用的地方有点不同,
? 是定义在引用变量上
T 是类上或方法上
2. List<T.?.Object>区别
ArrayList<T> arrayList = new ArrayList<T>(); 指定集合元素类型只可以是T类型
ArrayList<?>arrayList = new ArrayList<?>(); 集合元素可以是任意类型,一般是方法中为了说明方法
ArrayList<? extends E> arrayList = new ArrayList<? extends E>();泛型的限定:? extends E:接受E类型或者E的子类型、? super E :接受E类型或者E的父类型
3.java的内省与反射
//https://www.cnblogs.com/winclpt/articles/7405271.html
1.内省是先得到属性描述器PropertyDecriptor后再进行各种操作,内省(Introspector)是Java语言对JavaBean类属性、事件的处理方法
2.反射则是先得到类的字节码Class后再进行各种操作的。对任意一个类,能够获取得到这个类的所有属性和方法;
代码:
反射:
Field f = user.getClass().getDeclaredField("name");
f.setAccessible(true);
f.set(user, "mld");//设置属性值
内省
//操作单个属性
PropertyDescriptor pd = new PropertyDescriptor("name", User.class);
Method w = pd.getWriteMethod();//获取属性的setter方法
w.invoke(user, "winclpt");
4.java的代理
1.静态代理
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成
2.动态代理
实现InvocationHandler接口
动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。
原文链接:https://blog.csdn.net/flyfeifei66/article/details/81481222 **
// 使用JDK动态代理来为Dao接口生成代理对象,并返回
// loader: 用哪个类加载器去加载代理对象
//
// interfaces:动态代理类需要实现的接口
//
// h:动态代理方法在执行时,会调用h里面的invoke方法去执行
Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
//proxy 当前代理对象的应用
//method 当前被调用方法的引用
//args 传递的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 底层都还是去执行JDBC代码 //根据不同情况,来调用selctList或者selectOne
// 准备参数 1:statmentid :sql语句的唯一标识:namespace.id= 接口全限定名.方法名
// 方法名:findAll
String methodName = method.getName();
String className = method.getDeclaringClass().getName();
String statementId = className+"."+methodName;
// 准备参数2:params:args
// 获取被调用方法的返回值类型
Type genericReturnType = method.getGenericReturnType();
// 判断是否进行了 泛型类型参数化
if(genericReturnType instanceof ParameterizedType){
List<Object> objects = selectList(statementId, args);
return objects;
}
return selectOne(statementId,args);
}
});
return (T) proxyInstance;
}
5.newInstance与new的区别
new关键字能调用任何构造方法。
newInstance()只能调用无参构造方法。
newInstance()构造对象的地方通过new关键字也可以创建对象.
在使用newInstance()方法的时候,必须保证这个类已经加载并且已经连接
适用:
使用newInstance()在通用性方面比较高,className我们可以用配置文件进行相关的配置。
String className = 从配置文件中读取className;
A a = (A) Class.forName(className).newInstance();
再配合依赖注入的方法,就提高了软件的可伸缩性、可扩展性。框架的开发中用的比较多!
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
String str = (String) Class.forName("java.lang.String").newInstance();
String str1 = new String();
if(str.getClass() == str1.getClass()){
System.out.println("YES");
}
}
output:YES
//原文链接:https://blog.csdn.net/qq_33704186/java/article/details/86596614
单词
statement 声明,陈述
bound 一定会,边界
Declaring 公然地,生命
Accessible 可进入的
descrip 描述
relation 关系
example例子
Generic通用的
Security 安全的
Multiple 多样的
extract提取
wrap包
signature签名
Additional附加的 额外的/
作业
简答题
一.
1.Mybatis动态sql是做什么的?
1.动态sql就是 根据条件标签动态的拼接sql,包括判空,循环,拼接等
2.哪些动态sql?
动态sql大致有
1.: if是为了判断传入的值是否符合某种规则,比如是否不为空;
2.:where标签可以用来做动态拼接查询条件,当和if标签配合的时候,不用显示的声明类似where 1=1这种无用的条件
3.:foreach标签可以把传入的集合对象进行遍历,然后把每一项的内容作为参数传到sql语句中,
4.:include可以把大量重复的代码整理起来,当使用的时候直接include即可,减少重复代码的编写;
5.:是一个格式化标签
6.choose、when、otherwise 标签类似于 Java 中的 switch、case、default。只有一个条件生效,
<select id="findOneById" resultType="com.lagou.pojo.User">
select <include refid="userInfo"/> from user
<where>
<if test="id != null and id != 0">
AND id = #{id}
</if>
<foreach collection="list" item="id" open="(" close=")" separator="," >
#{id}
</foreach>
<trim prefix="where" suffix="order by id" prefixOverrides="and | or" suffixOverrides=",">
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="id != null">
AND id = #{id}
</if>
</trim>
</where>
</select>
3. 简述一下动态sql的执行原理?
1.在xmlMapperBuilder中 解析配置文件时
2.解析 <mapper />
节点
3.解析 节点
// 1.在xmlMapperBuilder中 解析配置文件时
public void parse() {
// 解析 `<mapper />` 节点
configurationElement(parser.evalNode("/mapper"));
}
//2. 解析 `<mapper />` 节点
private void configurationElement(XNode context) {
try {
// 获得 namespace 属性
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 设置 namespace 属性
builderAssistant.setCurrentNamespace(namespace);
// 解析 <resultMap /> 节点们
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析 <sql /> 节点们
sqlElement(context.evalNodes("/mapper/sql"));
// 解析 <select /> <insert /> <update /> <delete /> 节点们
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
// 3.解析 <select /> <insert /> <update /> <delete /> 节点们
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
// 上面两块代码,可以简写成 buildStatementFromContext(list, configuration.getDatabaseId());
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
//遍历 <select /> <insert /> <update /> <delete /> 节点们
for (XNode context : list) {
// 创建 XMLStatementBuilder 对象,执行解析
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
// 解析失败,添加到 configuration 中
configuration.addIncompleteStatement(statementParser);
}
}
}
4.XMLStatementBuilder对象
public class XMLStatementBuilder extends BaseBuilder {
/**
* 执行解析
*/
public void parseStatementNode() {
// 获得 id 属性,编号。
String id = context.getStringAttribute("id");
// 获得 databaseId , 判断 databaseId 是否匹配
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// 获得各种属性
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
// 获得 lang 对应的 LanguageDriver 对象
LanguageDriver langDriver = getLanguageDriver(lang);
// 获得 resultType 对应的类
Class<?> resultTypeClass = resolveClass(resultType);
// 获得 resultSet 对应的枚举值
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
// 获得 statementType 对应的枚举值
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
}
5.LanguageDriver的实现类XMLLanguageDriver的方法解析方法createSqlSource
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
// 创建 XMLScriptBuilder 对象,执行解析
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
6.解析
XMLScriptBuilder标签解析
/**
* XML 动态语句( SQL )构建器,负责将 SQL 解析成 SqlSource 对象
*
* @author Clinton Begin
*/
public class XMLScriptBuilder extends BaseBuilder {
/**
* 当前 SQL 的 XNode 对象
*/
private final XNode context;
/**
* 是否为动态 SQL
*/
private boolean isDynamic;
/**
* SQL 方法类型
*/
private final Class<?> parameterType;
/**
* NodeNodeHandler 的映射
*/
private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();
public XMLScriptBuilder(Configuration configuration, XNode context) {
this(configuration, context, null);
}
public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
super(configuration);
this.context = context;
this.parameterType = parameterType;
// 初始化 nodeHandlerMap 属性
initNodeHandlerMap();
}
/**
* 初始化 {@link #nodeHandlerMap} 属性
*/
private void initNodeHandlerMap() {
nodeHandlerMap.put("trim", new TrimHandler());
nodeHandlerMap.put("where", new WhereHandler());
nodeHandlerMap.put("set", new SetHandler());
nodeHandlerMap.put("foreach", new ForEachHandler());
nodeHandlerMap.put("if", new IfHandler());
nodeHandlerMap.put("choose", new ChooseHandler());
nodeHandlerMap.put("when", new IfHandler());
nodeHandlerMap.put("otherwise", new OtherwiseHandler());
nodeHandlerMap.put("bind", new BindHandler());
}
/**
* 负责将 SQL 解析成 SqlSource 对象
*
* @return SqlSource 对象
*/
public SqlSource parseScriptNode() {
// 解析 SQL
MixedSqlNode rootSqlNode = parseDynamicTags(context);
// 创建 SqlSource 对象
SqlSource sqlSource;
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
/**
* 解析 SQL 成 MixedSqlNode 对象
*
* @param node XNode 节点
* @return MixedSqlNode
*/
protected MixedSqlNode parseDynamicTags(XNode node) {
// 创建 SqlNode 数组
List<SqlNode> contents = new ArrayList<>();
// 遍历 SQL 节点的所有子节点
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
// 当前子节点
XNode child = node.newXNode(children.item(i));
// 如果类型是 Node.CDATA_SECTION_NODE 或者 Node.TEXT_NODE 时
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
// 获得内容
String data = child.getStringBody("");
// 创建 TextSqlNode 对象
TextSqlNode textSqlNode = new TextSqlNode(data);
// 如果是动态的 TextSqlNode 对象
if (textSqlNode.isDynamic()) {
// 添加到 contents 中
contents.add(textSqlNode);
// 标记为动态 SQL
isDynamic = true;
// 如果是非动态的 TextSqlNode 对象
} else {
// 创建 StaticTextSqlNode 添加到 contents 中
contents.add(new StaticTextSqlNode(data));
}
// 如果类型是 Node.ELEMENT_NODE
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
// 根据子节点的标签,获得对应的 NodeHandler 对象
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) { // 获得不到,说明是未知的标签,抛出 BuilderException 异常
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
// 执行 NodeHandler 处理
handler.handleNode(child, contents);
// 标记为动态 SQL
isDynamic = true;
}
}
// 创建 MixedSqlNode 对象
return new MixedSqlNode(contents);
}
/**
* Node 处理器接口
*/
private interface NodeHandler {
/**
* 处理 Node
*
* @param nodeToHandle 要处理的 XNode 节点
* @param targetContents 目标的 SqlNode 数组。实际上,被处理的 XNode 节点会创建成对应的 SqlNode 对象,添加到 targetContents 中
*/
void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
}
/**
* `<bind />` 标签的处理器
*
* @see VarDeclSqlNode
*/
private class BindHandler implements NodeHandler {
public BindHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
// 解析 name、value 属性
final String name = nodeToHandle.getStringAttribute("name");
final String expression = nodeToHandle.getStringAttribute("value");
// 创建 VarDeclSqlNode 对象
final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
// 添加到 targetContents 中
targetContents.add(node);
}
}
/**
* `<trim />` 标签的处理器
*
* @see TrimSqlNode
*/
private class TrimHandler implements NodeHandler {
public TrimHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
// 解析内部的 SQL 节点,成 MixedSqlNode 对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
// 获得 prefix、prefixOverrides、"suffix"、suffixOverrides 属性
String prefix = nodeToHandle.getStringAttribute("prefix");
String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
String suffix = nodeToHandle.getStringAttribute("suffix");
String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
// 创建 TrimSqlNode 对象
TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
// 添加到 targetContents 中
targetContents.add(trim);
}
}
/**
* `<where />` 标签的处理器
*
* @see WhereSqlNode
*/
private class WhereHandler implements NodeHandler {
public WhereHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
// 解析内部的 SQL 节点,成 MixedSqlNode 对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
// 创建 WhereSqlNode 对象
WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
// 添加到 targetContents 中
targetContents.add(where);
}
}
/**
* `<set />` 标签的处理器
*
* @see SetSqlNode
*/
private class SetHandler implements NodeHandler {
public SetHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
// 解析内部的 SQL 节点,成 MixedSqlNode 对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
// 创建 SetSqlNode 对象
SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
// 添加到 targetContents 中
targetContents.add(set);
}
}
/**
* `<foreach />` 标签的处理器
*
* @see ForEachSqlNode
*/
private class ForEachHandler implements NodeHandler {
public ForEachHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
// 解析内部的 SQL 节点,成 MixedSqlNode 对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
// 获得 collection、item、index、open、close、separator 属性
String collection = nodeToHandle.getStringAttribute("collection");
String item = nodeToHandle.getStringAttribute("item");
String index = nodeToHandle.getStringAttribute("index");
String open = nodeToHandle.getStringAttribute("open");
String close = nodeToHandle.getStringAttribute("close");
String separator = nodeToHandle.getStringAttribute("separator");
// 创建 ForEachSqlNode 对象
ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
// 添加到 targetContents 中
targetContents.add(forEachSqlNode);
}
}
/**
* `<if />` 标签的处理器
*
* @see IfSqlNode
*/
private class IfHandler implements NodeHandler {
public IfHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
// 解析内部的 SQL 节点,成 MixedSqlNode 对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
// 获得 test 属性
String test = nodeToHandle.getStringAttribute("test");
// 创建 IfSqlNode 对象
IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
// 添加到 targetContents 中
targetContents.add(ifSqlNode);
}
}
/**
* `<otherwise />` 标签的处理器
*/
private class OtherwiseHandler implements NodeHandler {
public OtherwiseHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
// 解析内部的 SQL 节点,成 MixedSqlNode 对象
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
// 添加到 targetContents 中
targetContents.add(mixedSqlNode);
}
}
/**
* `<choose />` 标签的处理器
*
* @see ChooseSqlNode
*/
private class ChooseHandler implements NodeHandler {
public ChooseHandler() {
// Prevent Synthetic Access
}
@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> whenSqlNodes = new ArrayList<>();
List<SqlNode> otherwiseSqlNodes = new ArrayList<>();
// 解析 `<when />` 和 `<otherwise />` 的节点们
handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
// 获得 `<otherwise />` 的节点
SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
// 创建 ChooseSqlNode 对象
ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
// 添加到 targetContents 中
targetContents.add(chooseSqlNode);
}
private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {
List<XNode> children = chooseSqlNode.getChildren();
for (XNode child : children) {
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler instanceof IfHandler) { // 处理 `<when />` 标签的情况
handler.handleNode(child, ifSqlNodes);
} else if (handler instanceof OtherwiseHandler) { // 处理 `<otherwise />` 标签的情况
handler.handleNode(child, defaultSqlNodes);
}
}
}
// 至多允许有一个 SqlNode 节点
private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
SqlNode defaultSqlNode = null;
if (defaultSqlNodes.size() == 1) {
defaultSqlNode = defaultSqlNodes.get(0);
} else if (defaultSqlNodes.size() > 1) {
throw new BuilderException("Too many default (otherwise) elements in choose statement.");
}
return defaultSqlNode;
}
}
}
二、
Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
延迟加载主要通过动态代理实现,通过代理拦截指定方法没执行数据加载。
javassisProxyFactory会创建一个User代理对象,所有调用User对象方法,都会经过EnhancedResultObjectProxyImpl.invoke()方法的拦截。、
于是当调用User.getOrder()方法时,才真正去执行查询Order的动作并把结果赋值
<settings>
<!-- 开启全局配置的懒加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 关闭积极加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<mapper namespace="demo.cyj.dao.TeacherDao">
<select id="findTeacherByTname" resultType="Teacher" resultMap="getTeacher">
select t.t_id t_id,t.t_name t_name from teacher t where t.t_name =#{name}
</select>
//延迟加载
<resultMap type="Teacher" id="getTeacher">
<collection ofType="Student" property="stuList" fetchType="lazy" column="t_name" select="findTeacherByTnameLazy" />
</resultMap>
<select id="findTeacherByTnameLazy" resultType="Student" >
select s.id id,s.stu_name stu_name,s.stu_age age,s.t_id t_id from student s left join teacher t on t.t_id = s.t_id where t.t_name=#{name}
</select>
</mapper>
2.延迟加载的类
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
延迟加载的类
private static class DeferredLoad {
private final MetaObject resultObject;
private final String property;
private final Class<?> targetType;
private final CacheKey key;
private final PerpetualCache localCache;
private final ObjectFactory objectFactory;
private final ResultExtractor resultExtractor;
// issue #781
public DeferredLoad(MetaObject resultObject,
String property,
CacheKey key,
PerpetualCache localCache,
Configuration configuration,
Class<?> targetType) {
this.resultObject = resultObject;
this.property = property;
this.key = key;
this.localCache = localCache;
this.objectFactory = configuration.getObjectFactory();
this.resultExtractor = new ResultExtractor(configuration, objectFactory);
this.targetType = targetType;
}
public boolean canLoad() {
return localCache.getObject(key) != null && localCache.getObject(key) != EXECUTION_PLACEHOLDER;
}
public void load() {
@SuppressWarnings( "unchecked" )
// we suppose we get back a List
List<Object> list = (List<Object>) localCache.getObject(key);
Object value = resultExtractor.extractObjectFromList(list, targetType);
resultObject.setValue(property, value);
}
}
三
1.Mybatis都有哪些Executor执行器
BaseExecutor 简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个 Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象)
BatchExecutor 批处理执行器,用于将多个SQL一次性输出到数据库
simpleExexutor 每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LX6M9gvA-1597914279840)(C:\Users\gaoyuan\Desktop\simpleExexutor.png)]
ReuseExecutor 执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
cachingExecutor 更新缓存
2.它们之间的区别是什么?
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
默认是SimplExcutor,需要配置在创建SqlSession对象的时候指定执行器的类型即可。
四
1.一级缓存
Mybatis的一级缓存是指SqlSession级别的,作用域是SqlSession,Mybatis默认开启一级缓存,在同一个SqlSession中,相同的Sql查询的时候,第一次查询的时候,就会从缓存中取,如果发现没有数据,那么就从数据库查询出来,并且缓存到HashMap中,如果下次还是相同的查询,就直接从缓存中查询,就不在去查询数据库,对应的就不在去执行SQL语句。当查询到的数据,进行增删改的操作的时候,缓存将会失效
2. 二级缓存
MyBatis的二级缓存是基于Mapper级别的,也就是说多个SqlSession去使用某个Mapper的查询语句时,得到的缓存数据是可共用的。第一次调用mapper下的sql 的时候去查询信息,查询到的信息会存放到该mapper对应的二级缓存区域,第二次调用namespace下的mapper映射文件中,相同的SQL去查询,回去对应的二级缓存内取结果。二级缓存开启后,查询就会走二级缓存,没查到直接查库。MyBatis默认不开启二级缓存
五
简述Mybatis的插件运行原理,以及如何编写一个插件?
插件运行原理
实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可
实现Interceptor接口 在定义StatementHandler处理器的时候拦截prepare方法也就是准备的方法
//1.configuration.newStatementHandler()获取对象
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
Configuration configuration = ms.getConfiguration();
//定义StatementHandler处理器
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//初始化
stmt = prepareStatement(handler, ms.getStatementLog());
//执行
return handler.update(stmt);
}
//2.获取执行sql的StatementHandler组件
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//代理对象
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
//3.
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
}
如何编写插件
实现Interceptor接口,Interceptors注解表明要拦截的类,方法,参数
package com.lagou.plugin;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.util.Properties;
@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("增强了");
return invocation.proceed();
}
//把这个拦截器生成一个代理放到拦截器链中
@Override
public Object plugin(Object target) {
System.out.println("将要包装的目标对象"+target);
return Plugin.wrap(target,this);
}
//插件初始化时候调用,只调用一次 获取配置文件的属性
@Override
public void setProperties(Properties properties) {
System.out.println("获取配置文件的属性"+properties);
}
}
tementHandler);
return statementHandler;
}
//3.
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
}
如何编写插件
实现Interceptor接口,Interceptors注解表明要拦截的类,方法,参数
package com.lagou.plugin;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.util.Properties;
@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("增强了");
return invocation.proceed();
}
//把这个拦截器生成一个代理放到拦截器链中
@Override
public Object plugin(Object target) {
System.out.println("将要包装的目标对象"+target);
return Plugin.wrap(target,this);
}
//插件初始化时候调用,只调用一次 获取配置文件的属性
@Override
public void setProperties(Properties properties) {
System.out.println("获取配置文件的属性"+properties);
}
}