package com;
/**
*
*/
public class User {
Integer id;
String userName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
'}';
}
}
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.8version>
dependency>
dependencies>
package com;
import java.sql.*;
/**
*
*/
public class Mybatis {
public static void main(String[] args) throws Exception {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "123456");
// 定义sql语句?表示占位符
String sql = "select * from user where username = ?";
// 获取预处理statement
preparedStatement = connection.prepareStatement(sql);
// 设置参数,第一个参数为sql语句中参数的序号(从1开始),第⼆个参数为设置的参数值
preparedStatement.setString(1, "tom");
// 向数据库发出sql执⾏查询,查询出结果集
resultSet = preparedStatement.executeQuery();
// 遍历查询结果集
User user = new User();
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
// 封装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();
}
}
}
}
}
<configuration>
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
<property name="jdbcUrl" value="jdbc:mysql:///mybatis">property>
<property name="user" value="root">property>
<property name="password" value="123456">property>
<mapper resource="mapper.xml">mapper>
configuration>
<mapper namespace="User">
<select id="selectOne" paramterType="com.User" resultType="com.User">
select * from user where id = #{id} and username =#{username}
<!--id最好就是id,因为我们并没有操作什么大小写,或者get和set的操作,这里主要是类似于aClass.getDeclaredField的方法,他是必须-要对应参数存在的,否则报错,并且我们也没有在之前进行设置忽略的相关操作(比如可以操作try的不报错的循环判断,循环退出后,再考虑是否存在,从而考虑报错)->
select>
<select id="selectList" resultType="com.User">
select * from user
select>
mapper>
package com;
/**
*
*/
public class User { //记得与数据库中的表对应
Integer id;
String username;
String password;
String birthday;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday='" + birthday + '\'' +
'}';
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mybatisartifactId>
<groupId>org.examplegroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>mybaartifactId>
<properties>
<maven.compiler.source>11maven.compiler.source>
<maven.compiler.target>11maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.17version>
dependency>
<dependency>
<groupId>c3p0groupId>
<artifactId>c3p0artifactId>
<version>0.9.1.2version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.12version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.10version>
dependency>
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>1.6.1version>
dependency>
<dependency>
<groupId>jaxengroupId>
<artifactId>jaxenartifactId>
<version>1.1.6version>
dependency>
dependencies>
project>
package com.Res;
import java.io.InputStream;
/**
*
*/
public class Resources {
//这里是用来读取配置文件的,所以这里定义一个读取配置文件的方法
public static InputStream getResourceAsSteam(String path) {
//通过类加载器来使用对应的方法,得到输入流(加载器有顶端出现class,而顶端是c的代码,所以不用怀疑不能)
//默认是classpath路径,也就是项目路径,或者说资源文件夹里面的以及java(文件夹)里面的
//但要注意的是,如果对应的项目在当前idea,就算你安装了,那么首先也是使用他的,所以在实际操作中,记得去除掉对应的框架项目,使得不在当前idea中,当然,你可以另外创建一个项目在其他窗口也行
InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
}
}
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.10version>
dependency>
<dependency>
<groupId>org.examplegroupId>
<artifactId>testartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
package com;
import com.Res.Resources;
import org.junit.Test;
import java.io.InputStream;
/**
*
*/
public class test {
@Test
public void res() {
//得到配置文件的流,从而可以读取信息
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
}
}
//上面我们拿取了xml的配置信息,主要是解决硬编码问题了,那么现在开始,将配置信息进行实际操作
//在前面我们知道有四个信息:MappedStatement(Mapper的简称,一般是Mapper.xml的信息):主要是操作sql语句、statement类型(一般是操作标识,即定位的,当然大多数是代表本身)、输入参数java类型、输出参数java类型等等
//所以这里也需要四个参数
package com.pojo;
/**
*
*/
public class MappedStatement {
//id(statement类型,比如select,delete等等标签的对应id信息,所以这里最好是String类型)
private String id; //这里是statement,而不是statementId哦
//sql语句
private String sql;
//输入参数
private Class<?> paramterType;
//输出参数
private Class<?> resultType;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public Class<?> getParamterType() {
return paramterType;
}
public void setParamterType(Class<?> paramterType) {
this.paramterType = paramterType;
}
public Class<?> getResultType() {
return resultType;
}
public void setResultType(Class<?> resultType) {
this.resultType = resultType;
}
}
//Configuration,用来存放数据库基本信息(一般是sqlMapConfig.xml的信息)和Map<唯一标识,Mapper> ,唯一标识:namespace + "." + id
package com.pojo;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
public class Configuration {
//数据源
private DataSource dataSource;
//map集合: key:statementId value:MappedStatement(包含对应的标签哦,因为我们只是扫描一个xml,通过他来得到信息,所以且一般需要多个,既然是存,那么为什么不存在这里呢)
private Map<String, MappedStatement> mappedStatementMap = new HashMap<String, MappedStatement>();
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Map<String, MappedStatement> getMappedStatementMap() {
return mappedStatementMap;
}
public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
this.mappedStatementMap = mappedStatementMap;
}
}
package com.config;
import com.Res.Resources;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.pojo.Configuration;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
/**
*
*/
public class XMLConfigerBuilder {
private Configuration configuration;
//合成复用,每个操作都给一个类来进行操作,而不是创建一个,主要用于一个类被多人操作的情况
public XMLConfigerBuilder(Configuration configuration) {
this.configuration = new Configuration();
}
//解析配置文件,封装Configuration类
public Configuration parseConfiguration(InputStream inputStream) throws Exception {
Document document = new SAXReader().read(inputStream); //解析XML获取文档对象document
//,getRootElement() 获得根元素,这里的api可以选择到42章博客去查看
Element rootElement = document.getRootElement();
//selectNodes(query):得到的是xml根节点下的所有满足 xpath 的节点,参数是Xpath 查询串
List<Element> propertyElements = rootElement.selectNodes("//property");
Properties properties = new Properties();
for (Element propertyElement : propertyElements) {
//attributeValue(…) 获得指定属性名的属性值
String name = propertyElement.attributeValue("name");
String value = propertyElement.attributeValue("value");
//其中Properties类是Hashtable类的子类,该对象主要用于处理属性文件,key和value都是String类型的,即也是集合类的一种
properties.setProperty(name, value);
}
//连接池,使用默认配置,具体在41章博客有说明
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
comboPooledDataSource.setUser(properties.getProperty("user"));
comboPooledDataSource.setPassword(properties.getProperty("password"));
//填充 configuration
configuration.setDataSource(comboPooledDataSource);
//至此,对应的数据源信息都操作完毕,上面之所以要分开,主要是判断getProperty参数里面是否一致的问题,我们先进行添加就不用判断了
//mapper 部分,这里以多个指向的xml为主,不要认为是操作包的哦
List<Element> mapperElements = rootElement.selectNodes("//mapper");
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
for (Element mapperElement : mapperElements) {
String mapperPath = mapperElement.attributeValue("resource");
//继续读取,所以也需要一个对于的对象来操作,即xmlMapperBuilder,也自然需要传递输入流
InputStream resourceAsSteam = Resources.getResourceAsSteam(mapperPath);
xmlMapperBuilder.parse(resourceAsSteam);
}
return configuration;
}
}
package com.config;
import com.pojo.Configuration;
import com.pojo.MappedStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class XMLMapperBuilder {
private Configuration configuration;
public XMLMapperBuilder(Configuration configuration) {
this.configuration = configuration;
}
public void parse(InputStream inputStream) throws Exception {
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
String namespace = rootElement.attributeValue("namespace");
List<Element> select = rootElement.selectNodes("select");
for (Element element : select) { //id的值
String id = element.attributeValue("id");
String paramterType = element.attributeValue("paramterType");
String resultType = element.attributeValue("resultType");
//输入参数class,既然对应的输入和输出是一个具体的类型,且是全路径,自然需要直接得到对应的class类型
Class<?> paramterTypeClass = getClassType(paramterType);
//返回结果class
Class<?> resultTypeClass = getClassType(resultType);
//statementId
String key = namespace + "." + id;
//sql语句,得到内容,并且去除两边的空格
String textTrim = element.getTextTrim();
//封装 mappedStatement
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setParamterType(paramterTypeClass);
mappedStatement.setResultType(resultTypeClass);
mappedStatement.setSql(textTrim);
//填充 configuration
configuration.getMappedStatementMap().put(key, mappedStatement);
}
}
//得到class类型
private Class<?> getClassType(String paramterType) throws Exception {
//这里也要考虑对应是否为null,如果是,那么应该返回null
if(paramterType==null){
return null;
}
Class<?> aClass = Class.forName(paramterType);
return aClass;
}
}
package com.sqlsession;
import com.config.XMLConfigerBuilder;
import com.pojo.Configuration;
import org.dom4j.DocumentException;
import java.beans.PropertyVetoException;
import java.io.InputStream;
/**
*
*/
public class SqlSessionFactoryBuilder {
private Configuration configuration;
public SqlSessionFactoryBuilder() {
this.configuration = new Configuration();
}
public SqlSessionFactory build(InputStream inputStream) throws Exception {
//1.解析配置文件,封装Configuration,我们可以选择将解析过程写在这里,但是为了好维护,我们创建XMLConfigerBuilder类来进行解析,使得以后可以复用的
XMLConfigerBuilder xmlConfigerBuilder = new XMLConfigerBuilder(configuration);
Configuration configuration = xmlConfigerBuilder.parseConfiguration(inputStream);
//2.创建 sqlSessionFactory,工厂模式,使得主操作不用创建,只需要修改这一处即可,所以正是因为这样,一般来说我们需要一个接口,来完成可以以后的修改,那么自然不能是当前类了
SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
return sqlSessionFactory;
}
}
package com.sqlsession;
/**
*
*/
public interface SqlSessionFactory {
public SqlSession openSession();
}
package com.sqlsession;
import java.sql.SQLException;
import java.util.List;
/**
*
*/
public interface SqlSession {
public <E> List<E> selectList(String statementId, Object... param) throws Exception;
public <T> T selectOne(String statementId, Object... params) throws Exception;
public void close() throws SQLException;
}
package com.sqlsession;
import com.pojo.Configuration;
/**
*
*/
//因为是实现他,所以才说是工厂接口(接口可以让更多人进行操作,只需要改动类即可,所以一般我们都会将需求称为接口,因为接口必然对应实现类,也是现在的规范),其中我们将解析配置文件的结果configuration传递进来进行操作
//之所以是工厂,是因为如果有其他的实现类(其他的操作,或者就是该类,但是我们添加一些代表补充,虽然补充一般我们会认为是装饰器模式(前提是指向相同方法,且是方法)),那么使用他就行,从而改变所有使用对应的SqlSessionFactoryBuilder类的地方,这也是工厂模式的好处
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
//被返回后他也有方法可以操作,简单来说前面根据SqlSessionFactoryBuilder操作的配置文件后,返回的一个类就是这个,而他是工厂操作,那么自然每个工厂返回的类有他自己的类操作
//很明显,他在满足工厂模式的情况下,进一步返回操作封装的configuration,其实,如果可以,他可以返回自身,而自身继续操作,但是这样没有满足工厂的,也就没有什么扩展可言
//这里的方法,又实现工厂模式,大多数我们并不会在主操作中操作new,所以我们通常这样操作
@Override
public SqlSession openSession(){
return new DefaultSqlSession(configuration);
}
}
package com.sqlsession;
import com.pojo.Configuration;
import com.pojo.MappedStatement;
import java.sql.SQLException;
import java.util.List;
/**
*
*/
public interface Executor {
<E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] param) throws Exception;
void close() throws SQLException;
}
package com.sqlsession;
import com.pojo.Configuration;
import com.pojo.MappedStatement;
import java.sql.SQLException;
import java.util.List;
/**
*
*/
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
}
//处理器对象,专门来操作具体sql执行的,也就是jdbc代码,这里并不会用来返回给任何人,所以直接使用他的方法即可
private Executor simpleExcutor = new SimpleExecutor();
//对应工厂返回的对象类中,都有各自的操作,当然,这里一般只有一个工厂(大多都是这样,只是使用模式而已,总不能没有扩展吧),对应的类都也操作工厂模式
@Override
//由于我们不知道对应的类型是什么,所以这里利用泛型方法来表示对应的类型(变成对应的类型,包括返回值的泛型和参数的泛型)
public <E> List<E> selectList(String statementId, Object... param) throws Exception {
//这里我们传递对应的标识,既然我们知道对应的配置信息,那么要执行这个,必然是需要找到对应的sql语句的,并需要利用配置文件的数据库配置信息
//所以这里的statementId就是对应的namespace.id,而Object... param可以认为是条件(因为条件的多个的),且可以是类,所以设为Object的可变长参数
//当然,对应的由于是操作类,所以这里以一个类为主,即这个参数主要考虑paramterType的名称的值
//在map中拿取指定的标签的信息,即通过我们传递的statementId可以得到对应的信息,因为
/*
//statementId
String key = namespace + "." + id;
*/
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
List<E> query = simpleExcutor.query(configuration, mappedStatement, param);
return query;
}
@Override
//selectOne 中调用 selectList
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 if (objects.size() == 0) {
throw new RuntimeException("没有结果");
} else {
throw new RuntimeException("返回结果过多");
}
}
@Override
public void close() throws SQLException {
simpleExcutor.close();
}
}
package com.sqlsession;
import com.pojo.Configuration;
import com.pojo.MappedStatement;
import com.utils.GenericTokenParser;
import com.utils.ParameterMapping;
import com.utils.ParameterMappingTokenHandler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class SimpleExecutor implements Executor {
private Connection connection = null;
//操作jdbc代码,我们之前的配置最终都需要这样的操作,只是需要获取或者保存一系列的信息而已以及一些设计模式的使用
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] param) throws Exception {
//获取连接
connection = configuration.getDataSource().getConnection();
// select * from user where id = #{id} and username = #{username}
String sql = mappedStatement.getSql();
//对sql进行处理,由于对应的sql中存在#{}所以需要进行变化,且之所以没有操作?,是因为若是实体类,我们需要根据里面的信息来进行替换的,这也是为什么需要使用#{}的一个原因
//得到最终数据的结果,即?位置及其数据,替换了?哦
BoundSql boundsql = getBoundSql(sql);
// 得到select * from where id = ? and username = ?
String finalSql = boundsql.getSqlText();
//获取传入参数类型,mappedStatement可是保存我们定位到的标签信息哦
Class<?> paramterType = mappedStatement.getParamterType();
//获取预编译preparedStatement对象,他是操作?的,这也是为什么我们使用替换?的一个原因
PreparedStatement preparedStatement = connection.prepareStatement(finalSql);
//得到?的位置及其数据替换(如id等等)
List<ParameterMapping> parameterMappingList = boundsql.getParameterMappingList();
//由于这个大小的存在,所以如果没有参数,他自然不会执行,也就不会考虑paramterType是否为null的情况了
//因为没有参数,他自然为0,因为每有一个?他都会添加
for (int i = 0; i < parameterMappingList.size(); i++) {
//拿取每一个的ParameterMapping数据,他就包括了?的位置,实际上由于这个循环的作用,那么这个位置也不用操作了,我们只需要将他的对应的替换进行操作即可
ParameterMapping parameterMapping = parameterMappingList.get(i);
//得到那个替换数据
String name = parameterMapping.getContent();
//反射,注意:这里是操作类的,所以这里并没有其他的判断,这里要注意
//Field getDeclaredField(String name),用于获取此Class对象所表示类中参数指定的单个成员变量信息
//由于这里操作入参,因为?是替换的,所以获取该成员信息
Field declaredField = paramterType.getDeclaredField(name); //这之前,我们并不操作set,只是操作成员变量而已
//void setAccessible(booleanflag),当实参传递true时,则反射对象在使用时应该取消 Java 语言访问检查,即保证修改成功
declaredField.setAccessible(true);
//参数的值,因为是类,所以这里直接为0,因为只有一个
//Object get(Object obj),获取参数对象obj中此Field对象所表示成员变量的数值
Object o = declaredField.get(param[0]);
//实际上这里之所以利用反射,是保证入参类型与传递的参数类型一致,所以如果不一致,那么通常可能会报错
//也就是说Object o = declaredField.get(param[0]);会发生报错,所以利用反射也是一种防止手段,那么有个问题,可以自己判断吗
//答:不能,因为上面的Object类型的,你怎么知道他的类是啥呢,所以这里利用反射来确定一致性
//给占位符赋值
preparedStatement.setObject(i + 1, o);
}
//上面就完成了设置参数,现在我们需要执行
//因为都是select,所以操作executeQuery方法,当然,这里只是测试,如果是多个的话,我们一般是需要判断的
ResultSet resultSet = preparedStatement.executeQuery();
//获得返回的类型
Class<?> resultType = mappedStatement.getResultType();
ArrayList<E> results = new ArrayList<E>(); //因为可以得到多行数据,或者说列数据(对于结果来说),所以需要这个集合
//当然,由于我们并没有操作其他的标签属性来决定是否操作集合,所以这里统一操作集合
while (resultSet.next()) { //进行遍历
//PrepareStatement 预处理对象调用 getMetaData () , 获取的是ResultSetMetaData , 结果集元数据对象
//他(ResultSetMetaData metaData)包含如下方法:
/*
//getColumnCount() : 当前结果集共有多少列,注意,这里代表的是结果,也就是说代表一行一行的数据
//getColumnName(int i) : 获取指定列号的列名, 参数是整数 从1开始
//getColumnTypeName(int i): 获取指定列号列的类型, 参数是整数 从1开始
*/
ResultSetMetaData metaData = resultSet.getMetaData();
E e = (E) resultType.newInstance(); //创建定义返回的类,一般情况下,编译期会将这个e可能放在外面来操作,具体如何主要看编译器怎么操作了(可能如果后面没有,放在最外面),这里了解即可
//getColumnCount() : 当前结果集共有多少列
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) { //因为上面是从1开始的,所以这里也是哦
//属性名
//getColumnName(int i) : 获取指定列号的列名, 参数是整数 从1开始
String columnName = metaData.getColumnName(i);
//属性值
Object value = resultSet.getObject(columnName);
//很明显,这里我们可以根据上面的属性名来设置前面E类的值(反射,或者说内省),但是这里我们使用另外一种我们没有使用过的技术
//看如下:
//创建属性描述器,为属性⽣成读写方法,相当于操作对应的属性
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultType);
//获取写方法
Method writeMethod = propertyDescriptor.getWriteMethod();
//向类中写入值
writeMethod.invoke(e, value);
/*
一般情况下,我们会使用这种方式
Field declaredField = resultType.getDeclaredField(columnName);
declaredField.set(e,value);
很明显,PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultType);
就相当于
Field declaredField = resultType.getDeclaredField(columnName);
declaredField.setAccessible(true);
declaredField.set(e,value);
只是里面自定义了declaredField .set(e,value);的封装方法,使得进行操作,我们可以看到,使用原来的应该要好点,那么为什么使用这个呢,或者说好处是什么
首先他没有什么null的问题,因为他不是resultType来调用的,这就是主要的好处
*/
}
results.add(e);
}
return results;
}
@Override
public void close() throws SQLException {
//SqlSession的close最终还是连接的close关闭操作
connection.close();
}
//完成#{}的替换,然后将真的数据进行保存并返回
private BoundSql getBoundSql(String sql) {
//标记处理类:主要是配合通用标记解析器GenericTokenParser类完成对配置文件等的解析工作,其中TokenHandler主要完成处理
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
//GenericTokenParser :通用的标记解析器,完成了代码⽚段中的占位符的解析,然后再根据给定的标记处理器(TokenHandler)来进行表达式的处理
//三个参数:分别为openToken (开始标记)、closeToken (结束标记)、handler (标记处理器)
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
//将sql传递进来进行解析
String parse = genericTokenParser.parse(sql);
//这里就是得到?对应的替换信息,其中下标代表?的先后顺序,而类保存的值,代表替换的数据
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
//将解析数据进行保存
BoundSql boundSql = new BoundSql(parse, parameterMappings);
return boundSql;
}
}
package com.sqlsession;
import com.utils.ParameterMapping;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class BoundSql {
//解析过后的sql语句
private String sqlText;
//解析出来的参数
private List<ParameterMapping> parameterMappingList = new ArrayList<ParameterMapping>();
public BoundSql(String sqlText, List<ParameterMapping> parameterMappingList) {
this.sqlText = sqlText;
this.parameterMappingList = parameterMappingList;
}
public String getSqlText() {
return sqlText;
}
public void setSqlText(String sqlText) {
this.sqlText = sqlText;
}
public List<ParameterMapping> getParameterMappingList() {
return parameterMappingList;
}
public void setParameterMappingList(List<ParameterMapping> parameterMappingList) {
this.parameterMappingList = parameterMappingList;
}
}
package com.utils;
/**
* @author Clinton Begin
*/
public interface TokenHandler {
String handleToken(String content);
}
package com.utils;
public class ParameterMapping {
//保存了对应替换的信息,以字符串的形式进行
private String content;
public ParameterMapping(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
package com.utils;
import java.util.ArrayList;
import java.util.List;
public class ParameterMappingTokenHandler implements TokenHandler {
//之所以要这样,是保证添加的顺序,以及信息,所以这样就可以决定谁先操作?了
private List<ParameterMapping> parameterMappings = new ArrayList();
// context是参数名称 #{id} #{username}
@Override
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
return "?";
}
private ParameterMapping buildParameterMapping(String content) {
ParameterMapping parameterMapping = new ParameterMapping(content);
return parameterMapping;
}
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
public void setParameterMappings(List<ParameterMapping> parameterMappings) {
this.parameterMappings = parameterMappings;
}
}
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.utils;
/**
* @author Clinton Begin
*/
public class GenericTokenParser {
private final String openToken; //开始标记
private final String closeToken; //结束标记
private final TokenHandler handler; //标记处理器
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
/**
* 解析${}和#{}
*
* @param text
* @return 该方法主要实现了配置文件、脚本等片段中占位符的解析、处理工作,并返回最终需要的数据
* 其中,解析工作由该方法完成,处理工作是由处理器handler的handleToken()方法来实现
*/
//这里一般传递的是sql
public String parse(String text) {
// 验证参数问题,如果是null,就返回空字符串。
if (text == null || text.isEmpty()) {
return "";
}
// 下面继续验证是否包含开始标签,如果不包含,默认不是占位符,直接原样返回即可,否则继续执行,也就是不用解析,sql就是sql
//int indexOf(String str, int fromIndex),表示从字符串的fromIndex位置开始检索str第一次出现的位置
int start = text.indexOf(openToken, 0);
if (start == -1) {
return text;
}
// 把text转成字符数组src,并且定义默认偏移量offset=0、存储最终需要返回字符串的变量builder
// text变量中占位符对应的变量名为expression,判断start是否大于-1(即text中是否存在openToken),如果存在就执行下面代码
//我们将sql的字符串信息变成字符数组,在xml中得到的都是字符串哦
char[] src = text.toCharArray();
//定义起始偏移量
int offset = 0;
//使用这个可变的String
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) { //代表存在
// 判断如果开始标记前如果有转义字符,就不作为openToken进行处理,否则继续处理
if (start > 0 && src[start - 1] == '\\') {
//这个append方法代表将字符数组的offset起始位置,添加start - offset - 1数量的值进行添加,从offset开始
//然后继续加上openToken,说明拿取了包括#{之前的sql信息
builder.append(src, offset, start - offset - 1).append(openToken);
//起始位置发生改变,正好在#{后面一个位置
offset = start + openToken.length();
} else {
//重置expression变量,避免空指针或者老数据干扰
if (expression == null) {
expression = new StringBuilder();
} else {
//setLength()方法用于在将字符序列替换为新字符序列时设置字符序列的长度,因此该序列的长度将由给定参数指定
//比如:你有123这个数据,若我设置为2,那么只会显示12,3被清除,所以如果设置为0,代表清空数据,并且如果设置为4,那么多余的用空字符来代表,注意是字符哦,对于基本数据类型来说,超过的则总后面显示
expression.setLength(0);
}
//继续添加,但是并没有添加#{,主要是为了后面进行替换?
builder.append(src, offset, start - offset);
//改变起始位置
offset = start + openToken.length();
//再次获得结果,只不过这里以结束标记作为起始,且是以整个test的sql来查找的,一般都是利用test的
int end = text.indexOf(closeToken, offset);
while (end > -1) {//存在结束标记时
//继续判断,重复上面的动作,但是这里是判断结束标记的,而不是开始标记
if (end > offset && src[end - 1] == '\\') {//如果结束标记前面有转义字符时
// this close token is escaped. remove the backslash and continue.
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {//不存在转义字符,即需要作为参数进行处理,他也不添加},也是为了后面的?做替换
//并且expression保留了#{}里面的信息,但是由于我们只会保留一个,所以直接返回
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
break;
}
}
//如果没有结果,说明没有},所以直接执行如下,后面的就不用判断了,因为没有},自然不用操作替换,即必然会使得后面进行退出
if (end == -1) {
// close token was not found.
//得到后面的sql内容
builder.append(src, start, src.length - start);
offset = src.length;
} else {
//首先根据参数的key(即expression)进行参数处理,返回?作为占位符
//其中,handler是封装的,我们执行他的方法,且他的方法也是操作创建类的,这里也基本都利用的工厂模式
//总之,只是保存一下而已,而expression就是对应的要替换的信息,因为直接返回了,所以替换了?
builder.append(handler.handleToken(expression.toString()));
}
}
//查看后面是否还有#{,从而判断是否继续执行这个循环
start = text.indexOf(openToken, offset);
}
//判断开始标签直接不存在的情况
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
//注意:上面的操作中,你可能会认为是有很多的细节问题的,实际上并没有
//这个问题在后面进行说明,现在我们先使用这个,当然,那些问题又何尝不是一些小问题呢
//比如你可以尝试一下#{s\\} && user = #{user},会发现结果很特殊,所以说,如果是规范的那么不会出现这样的特殊问题,这主要是该替换的缺陷而已
//比如在if (end > offset && src[end - 1] == '\\')里面就有逻辑的问题(不规范的情况下)
//你可以尝试一下:select * from ee where id = #{s\\} && user = #{user}
//最终会变成select * from ee where id = ?,而不是select * from ee where id = #{s} && user = ?
//实际上他是对的,因为#{s\\} && user = #{user}中被包括的是s\\} && user = #{user,他是一个整体,要不然,为什么上面的if (end > offset && src[end - 1] == '\\')里面代表是否存在的问题呢,所以说该问题并不是问题,只是一个不好的操作而已
}
public String parse(String text) {
if (text != null && !text.isEmpty()) {
int start = text.indexOf(this.openToken);
if (start == -1) {
return text;
} else {
char[] src = text.toCharArray();
int offset = 0;
StringBuilder builder = new StringBuilder();
for(StringBuilder expression = null; start > -1; start = text.indexOf(this.openToken, offset)) {
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(this.openToken);
offset = start + this.openToken.length();
} else {
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + this.openToken.length();
int end;
for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
if (end <= offset || src[end - 1] != '\\') {
expression.append(src, offset, end - offset);
break;
}
expression.append(src, offset, end - offset - 1).append(this.closeToken);
offset = end + this.closeToken.length();
}
if (end == -1) {
builder.append(src, start, src.length - start);
offset = src.length;
} else {
builder.append(this.handler.handleToken(expression.toString()));
offset = end + this.closeToken.length();
}
}
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
} else {
return "";
}
}
//对于\\来说,一般无论是#{,还是},只要有一个人存在,那么直接找后面的,虽然有细节问题(但还是合理的)
package com;
import com.Res.Resources;
import com.sqlsession.SqlSession;
import com.sqlsession.SqlSessionFactory;
import com.sqlsession.SqlSessionFactoryBuilder;
import java.io.InputStream;
/**
*
*/
public class a {
public static void main(String[] args) throws Exception {
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
//读取配置信息,并通过工厂模式返回一个可以操作的类
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
//每个可以操作的类也有方法,且也是工厂模式来返回操作的类
SqlSession sqlSession = build.openSession();
User user = new User();
user.setId(2);
user.setUsername("tom");
User o = sqlSession.selectOne("User.selectOne", user);
System.out.println(o);
List<Object> list = sqlSession.selectList("User.selectList");
System.out.println(list);
}
}
//这里我们要记得一下,一般来说,有些mysql版本可能在mysql中password会被替换成authentication_string,所以注意即可,一般高版本是这样的,这里了解即可,即记得看版本哦,一般8.0好像就被替换了
package com.utils;
/**
*
*/
public class m {
public static void main(String[] args) {
String a = "select * from ee where id = #{s\\} && user = #{user}";
String parse = new GenericTokenParser("#{", "}", new ParameterMappingTokenHandler()).parse(a);
System.out.println(parse); //select * from ee where id = ?
//String a = "select * from ee where id = #{s && user = #{user}";也是
}
}
//注意:他是对的
//Field的setAccessible(true)方法
//用来满足私有的权限,并且满足不在同一个包下(其中若对应的类是当前包下的子包,也算)
//简单来说,如果对应的类的某个属性设置了私有,或者该类没有在你当前的包下,或者在当前包下的其他包下,那么就会不能访问,即报错,所以需要这个
//还有一点,如果maven没有操作包,那么可能直接在java资源文件夹下的类,应该是不能导入的,或者说,不能操作,这一般是maven的问题,在springboot中,一般也有这样的问题,这可能是一个规定,具体可以百度
package com.dao;
import com.User;
import java.util.List;
/**
*
*/
public interface IUserDao {
//查询所有用户
public List<User> findAll();
//根据条件进行用户查询
public User findBy(User user);
}
package com.dao;
import com.Res.Resources;
import com.User;
import com.sqlsession.SqlSession;
import com.sqlsession.SqlSessionFactory;
import com.sqlsession.SqlSessionFactoryBuilder;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class IUserDaoImpl implements IUserDao {
@Override
public List<User> findAll() throws Exception {
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
//读取配置信息,并通过工厂模式返回一个可以操作的类
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
//每个可以操作的类也有方法,且也是工厂模式来返回操作的类
SqlSession sqlSession = build.openSession();
List<User> list = sqlSession.selectList("User.selectList");
System.out.println(list);
return list;
}
@Override
public User findBy(User user) throws Exception {
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
//读取配置信息,并通过工厂模式返回一个可以操作的类
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
//每个可以操作的类也有方法,且也是工厂模式来返回操作的类
SqlSession sqlSession = build.openSession();
User o = sqlSession.selectOne("User.selectOne", user);
System.out.println(o);
return o;
}
}
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
//读取配置信息,并通过工厂模式返回一个可以操作的类
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
//每个可以操作的类也有方法,且也是工厂模式来返回操作的类
SqlSession sqlSession = build.openSession();
//为Dao接口生成代理实现类
public <T> T getMapper(Class<?> mapperClass);
@Override
public <T> T getMapper(Class<?> mapperClass) {
//使用JDk动态代理,来为Dao接口生成代理对象,并返回
/*
Proxy.newProxyInstance()方法的参数介绍:
ClassLoader loader,
类加载器,反射基本也是可以是类加载器来操作的(他可以操作Class,一般也有其他功能,所以这里是类加载器),这里可以借助被代理对象获取到类加载器
由于大多数的类加载器是一样的,所以这个参数基本可以随便使用某个类的加载器
Class>[] interfaces,
被代理类所需要实现的全部接口,所以是数组,注意是接口的Class类型哦,一般这里代表返回值类型(因为代理类与他相关,实现他)
InvocationHandler h
当代理对象调用接口中的任意方法时,那么都会执行InvocationHandler的invoke方法
*/
Object o = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 得到接口方法的名称
String methodName = method.getName();
// 得到接口方法当前接口(method.getDeclaringClass())的名称(getName())
String className = method.getDeclaringClass().getName();
//statementid
String key = className + "." + methodName;
//得到对应statementid的信息
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(key);
//得到接口方法的返回值类型
Type genericReturnType = method.getGenericReturnType();
//判断是否实现泛型类型参数化,也就是说,如果返回值类型有泛型,那么返回true
if (genericReturnType instanceof ParameterizedType) {
return selectList(key, args);
}
return selectOne(key, args);
}
});
return (T) o;
}
//很明显,上面是将接口的路径(全限定名,是在项目的路径)和名称作为配置文件的对应的statementid,其中我们并不操作
package com;
import com.Res.Resources;
import com.dao.UserMapper;
import com.sqlsession.SqlSession;
import com.sqlsession.SqlSessionFactory;
import com.sqlsession.SqlSessionFactoryBuilder;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class a {
public static void main(String[] args) throws Exception {
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
//读取配置信息,并通过工厂模式返回一个可以操作的类
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
//每个可以操作的类也有方法,且也是工厂模式来返回操作的类
SqlSession sqlSession = build.openSession();
User user = new User();
user.setId(2);
user.setUsername("tom");
// User o = sqlSession.selectOne("User.selectOne", user);
// System.out.println(o);
// List
// System.out.println(list);
//因为是泛型方法,所以不会考虑是否在编译期判断(他是自动的,所以一般不会考虑,单纯的泛型会认为是Object哦)
//即这里就算是User被接收也不会报错,但是运行报不报错就不知道了
//这里是JDk动态代理,接收了对应接口的代理类,并且,当对应的接口方法被执行时,会执行第一个参数中的方法,从而完成这个类的操作
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//那么这个好处是什么,首先,因为jdk代理可以在对应的拦截中进行补充操作,而这个补充就可以操作解决硬编码问题,这个补充就是在硬编码问题上操作对应的真的方法(selectList,selectOne)
//也可以操作重复问题,但是一般我们不会操作重复问题,因为对应的配置文件是会改变的,由于这里只是sqlSession来操作,所以并没有操作上面的重复,因为我们提取出来即可
//代理对象Proxy调用接口中的任意方法时,都会执行底层的invoke方法,从而实现增强,而这个增强就是解决硬编码问题的
List<User> all = mapper.findAll();
System.out.println(all);
User by = mapper.findBy(user);
System.out.println(by);
}
}
public void in(String namespace, List<Element> list, Configuration configuration) throws Exception {
//foreach不能操作null,否则报错,所以这里这样操作
if(list!=null) {
for (Element element : list) { //id的值
String id = element.attributeValue("id");
String paramterType = element.attributeValue("paramterType");
String resultType = element.attributeValue("resultType");
//输入参数class,既然对应的输入和输出是一个具体的类型,且是全路径,自然需要直接得到对应的class类型
Class<?> paramterTypeClass = getClassType(paramterType);
//返回结果class
Class<?> resultTypeClass = getClassType(resultType);
//statementId
String key = namespace + "." + id;
//sql语句,得到内容,并且去除两边的空格
String textTrim = element.getTextTrim();
//封装 mappedStatement
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setParamterType(paramterTypeClass);
mappedStatement.setResultType(resultTypeClass);
mappedStatement.setSql(textTrim);
//填充 configuration
configuration.getMappedStatementMap().put(key, mappedStatement);
}
}
}
public void parse(InputStream inputStream) throws Exception {
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
String namespace = rootElement.attributeValue("namespace");
List<Element> select = rootElement.selectNodes("select");
in(namespace, select, configuration);
List<Element> insert = rootElement.selectNodes("insert");
in(namespace, insert, configuration);
List<Element> delete = rootElement.selectNodes("delete");
in(namespace, delete, configuration);
List<Element> update = rootElement.selectNodes("update");
in(namespace, update, configuration);
}
//因为这三个基本只会返回int,不会考虑是什么类型的,所以这里直接用int即可,那么同样的,也不用在配置文件中操作返回的类型
public int insert(String statementId, Object... params) throws Exception;
public int delete(String statementId, Object... params) throws Exception;
public int update(String statementId, Object... params) throws Exception;
public int update(Configuration configuration, MappedStatement mappedStatement, Object[] param) throws Exception {
connection = configuration.getDataSource().getConnection();
String sql = mappedStatement.getSql();
BoundSql boundsql = getBoundSql(sql);
String finalSql = boundsql.getSqlText();
Class<?> paramterType = mappedStatement.getParamterType();
PreparedStatement preparedStatement = connection.prepareStatement(finalSql);
List<ParameterMapping> parameterMappingList = boundsql.getParameterMappingList();
for (int i = 0; i < parameterMappingList.size(); i++) {
ParameterMapping parameterMapping = parameterMappingList.get(i);
String name = parameterMapping.getContent();
Field declaredField = paramterType.getDeclaredField(name); //这之前,我们并不操作set,只是操作成员变量而已
declaredField.setAccessible(true);
Object o = declaredField.get(param[0]);
preparedStatement.setObject(i + 1, o);
}
int i1 = preparedStatement.executeUpdate();
return i1;
}
public int update(Configuration configuration, MappedStatement mappedStatement, Object[] param) throws Exception;
@Override
public int insert(String statementId, Object... params) throws Exception {
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
int update = simpleExcutor.update(configuration, mappedStatement, params);
return update;
}
@Override
public int delete(String statementId, Object... params) throws Exception {
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
int update = simpleExcutor.update(configuration, mappedStatement, params);
return update;
}
@Override
public int update(String statementId, Object... params) throws Exception {
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
int update = simpleExcutor.update(configuration, mappedStatement, params);
return update;
}
<insert id="insert" paramterType="com.User">
insert into user values(#{id},#{username},#{password},#{birthday})
insert>
<delete id="delete" paramterType="com.User">
delete from user where id = #{id}
delete>
<update id="update" paramterType="com.User">
update user set username = #{username} where id = #{id}
update>
package com;
import com.Res.Resources;
import com.sqlsession.SqlSession;
import com.sqlsession.SqlSessionFactory;
import com.sqlsession.SqlSessionFactoryBuilder;
import java.io.InputStream;
/**
*
*/
public class b {
public static void main(String[] args) throws Exception {
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
//读取配置信息,并通过工厂模式返回一个可以操作的类
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
//每个可以操作的类也有方法,且也是工厂模式来返回操作的类
SqlSession sqlSession = build.openSession();
User user = new User();
user.setId(3);
user.setUsername("m");
int insert = sqlSession.insert("com.dao.UserMapper.insert", user);
System.out.println(insert);
int update = sqlSession.update("com.dao.UserMapper.update", user);
System.out.println(update);
int delete = sqlSession.delete("com.dao.UserMapper.delete", user);
System.out.println(delete);
}
}
private String qian;
public String getQian() {
return qian;
}
public void setQian(String qian) {
this.qian = qian;
}
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setParamterType(paramterTypeClass);
mappedStatement.setResultType(resultTypeClass);
mappedStatement.setSql(textTrim);
mappedStatement.setQian(element.getName()); //这里进行操作,即添加当前标签的类型,如果是select,这里就保存select
//getMapper方法里面的
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(key);
if (configuration.getMappedStatementMap().get(key).getQian() == "select") {
//得到接口方法的返回值类型
Type genericReturnType = method.getGenericReturnType();
//判断是否实现泛型类型参数化,也就是说,如果返回值类型有泛型,那么返回true
if (genericReturnType instanceof ParameterizedType) {
return selectList(key, args);
}
return selectOne(key, args);
} else {
return update(key, args);
}
public int insert(User user) throws Exception;
public int delete(User user) throws Exception;
public int update(User user) throws Exception;
package com;
import com.Res.Resources;
import com.dao.UserMapper;
import com.sqlsession.SqlSession;
import com.sqlsession.SqlSessionFactory;
import com.sqlsession.SqlSessionFactoryBuilder;
import java.io.InputStream;
/**
*
*/
public class b {
public static void main(String[] args) throws Exception {
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
//读取配置信息,并通过工厂模式返回一个可以操作的类
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
//每个可以操作的类也有方法,且也是工厂模式来返回操作的类
SqlSession sqlSession = build.openSession();
User user = new User();
user.setId(3);
user.setUsername("mm");
UserMapper insert = sqlSession.getMapper(UserMapper.class);
int insert1 = insert.insert(user);
System.out.println(insert1);
int update = insert.update(user);
System.out.println(update);
int delete = insert.delete(user);
System.out.println(delete);
}
}
public interface SqlSession extends Closeable {
//..
void clearCache();
//..
}
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
//..
public void clearCache() {
this.executor.clearLocalCache();
}
//..
}
public interface Executor {
//..
void clearLocalCache();
//..
}
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
protected Transaction transaction;
protected Executor wrapper;
protected ConcurrentLinkedQueue<BaseExecutor.DeferredLoad> deferredLoads;
protected PerpetualCache localCache; //这个
protected PerpetualCache localOutputParameterCache;
//..
public void clearLocalCache() {
if (!this.closed) { //是否关闭了对应的对象,比如执行器,一般关闭会有时候抛出异常,如对应的其他地方(query,一般是子类)
this.localCache.clear();
this.localOutputParameterCache.clear(); //这个并非一定是二级缓存,后面会有源码解析的
//注意:SqlSession的clearCache方法一般只能清空一级缓存
}
}
//..
}
public class PerpetualCache implements Cache {
private final String id;
private final Map<Object, Object> cache = new HashMap();
//..
public void clear() {
this.cache.clear();
}
//..
}
//从上面可以看到,他最终是执行了map的clear方法,也就验证了,一级缓存是使用map来保存的,其中key可能是多个结果的总和,而形成的key,value就是缓存信息
public abstract class BaseExecutor implements Executor {
//..
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
CacheKey cacheKey = new CacheKey();
//MappedStatement 的 id
// id就是Sql语句的所在位置的包名+类名+ SQL方法名称,简单来说就是namespace + "." + id
cacheKey.update(ms.getId());
// offset 就是 0
cacheKey.update(rowBounds.getOffset());
// limit 就是 Integer.MAXVALUE
//offset和limit一般代表分页参数,当然,在没有设置时,可能日志不会进行显示
cacheKey.update(rowBounds.getLimit());
//具体的SQL语句
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
Iterator var8 = parameterMappings.iterator();
while(var8.hasNext()) {
ParameterMapping parameterMapping = (ParameterMapping)var8.next();
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
Object value;
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
//后面是update 了 sql中带的参数
cacheKey.update(value);
}
}
if (this.configuration.getEnvironment() != null) {
cacheKey.update(this.configuration.getEnvironment().getId());
}
return cacheKey;
}
}
//..
}
public class CacheKey implements Cloneable, Serializable {
//..
private List<Object> updateList;
//..
public void update(Object object) {
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
++this.count;
this.checksum += (long)baseHashCode;
baseHashCode *= this.count;
this.hashcode = this.multiplier * this.hashcode + baseHashCode;
this.updateList.add(object);
}
//..
}
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
public abstract class BaseExecutor implements Executor {
//..
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存,一般只是代表key,来获取真的结果的value
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql); //到下面
}
//到这里
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 (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
//这个主要是处理存储过程用的
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
return this.doQueryCursor(ms, parameter, rowBounds, boundSql);
}
//..
//这里就是主要的
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//protected PerpetualCache localCache;
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
/*
public void putObject(Object key, Object value) {
this.cache.put(key, value); 也就是说,将CacheKey key缓存,即前面的5个信息,当成一个key放入,只与对应的ExecutionPlaceholder.EXECUTION_PLACEHOLDER我想在应该是默认的,我们继续看后面
}
*/
List list;
try {
// protected abstract List doQuery(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, BoundSql var5) throws SQLException;
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
this.localCache.putObject(key, list); //这里就保存了对应的信息,并且是覆盖的
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
//..
}
//上面最终操作了 list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);,他就是执行sql语句的最终操作,并且将执行的结果进行返回,然后放入map中进行操作,当再次执行时,可以认为:
/*
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
这里就是利用缓存的地方,即 (List)this.localCache.getObject(key),直接返回了,而不执行list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);,当然,某些操作可能会进行清空,即map的清空,所以执行,和清空的确是存在的
至此我们说明的一级缓存说明完毕,简单来说,就是利用map来进行保存的,而key就是利用当前的主要信息来进行保存,即来确定唯一
*/
public class BatchExecutor extends BaseExecutor {
//..
//也就是前面的protected abstract List doQuery(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, BoundSql var5) throws SQLException;
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var10;
try {
this.flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
Connection connection = this.getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
var10 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var10;
}
//..
}
//我们可以发现,他的返回值并不是int类型的,也就是说,他默认是操作查询的结果,所以我大胆的认为,只有查询的操作才会进行缓存的创建,否则没有,我们可以观察他的其他操作,如类似的update里面的protected abstract int doUpdate(MappedStatement var1, Object var2) throws SQLException;,他对应就没有操作缓存(增删改,也是BatchExecutor),并且可以发现,执行改方法之前,update,会默认清空缓存,还有commit也是,虽然在63章博客有具体说明,都是操作了this.clearLocalCache();来清空的(可能包括二级,虽然有些是先清空,然后再次的给二级,但一般没有这样的考虑)
//其中增删改清空缓存,是保证我们查询的数据是对的,而不是错误的,因为我改变了,你若再从缓存拿取,那么你就没有拿取我改变的表数据了,还是原来的数据了
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
protected Transaction transaction;
protected Executor wrapper;
protected ConcurrentLinkedQueue<BaseExecutor.DeferredLoad> deferredLoads;
protected PerpetualCache localCache; //一级
protected PerpetualCache localOutputParameterCache; //二级(第二个map)
protected Configuration configuration;
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-redisartifactId>
<version>1.0.0-beta2version>
dependency>
<cache type="org.mybatis.caches.redis.RedisCache" />
redis.host=192.168.164.128
redis.port=6379
redis.connectionTimeout=5000
redis.password=
redis.database=0
host=192.168.164.128
port=6379
connectionTimeout=5000
password=
database=0
@Test
public void testTwoCache() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
// 第一次查询
User user = userMapper1.findById(11);
System.out.println(user);
sqlSession1.close(); //将redis看成传统二级缓存即可,因为他必然是与原来的二级缓存是对应的,所以这些方法也是对应的作用,反正也是key-value的存储
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = userMapper2.findById(11);
System.out.println(user2);
public final class RedisCache implements Cache {
private final ReadWriteLock readWriteLock = new DummyReadWriteLock();
private String id;
private static JedisPool pool;
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
} else {
this.id = id;
RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();
pool = new JedisPool(redisConfig, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getPassword(), redisConfig.getDatabase(), redisConfig.getClientName());
}
}
//..
}
public class RedisConfig extends JedisPoolConfig {
private String host = "localhost";
private int port = 6379;
private int connectionTimeout = 2000;
private int soTimeout = 2000;
private String password;
private int database = 0;
private String clientName;
public RedisConfig() {
}
}
// RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();
final class RedisConfigurationBuilder {
private static final RedisConfigurationBuilder INSTANCE = new RedisConfigurationBuilder();
private static final String SYSTEM_PROPERTY_REDIS_PROPERTIES_FILENAME = "redis.properties.filename";
private static final String REDIS_RESOURCE = "redis.properties";
private final String redisPropertiesFilename = System.getProperty("redis.properties.filename", "redis.properties");
//..
public static RedisConfigurationBuilder getInstance() {
return INSTANCE;
}
public RedisConfig parseConfiguration() {
return this.parseConfiguration(this.getClass().getClassLoader());
}
//最终到这里
public RedisConfig parseConfiguration(ClassLoader classLoader) {
Properties config = new Properties();
//private final String redisPropertiesFilename = System.getProperty("redis.properties.filename", "redis.properties");,这就是为什么我们需要在资源文件夹中创建redis.properties的原因,System.getProperty方法的两个参数分别代表了要获取的属性的名称和默认值,由于没有名称(或者说并没有进行设置位置),那么这里会获取默认值redis.properties,从而使得classLoader.getResourceAsStream读取了该文件里面的信息,也正好是资源文件夹下面的该文件
InputStream input = classLoader.getResourceAsStream(this.redisPropertiesFilename);
if (input != null) {
try {
//使用Properties对象的 load方法 从字节流中读取配置信息,变成map集合
config.load(input);
} catch (IOException var12) {
throw new RuntimeException("An error occurred while reading classpath property '" + this.redisPropertiesFilename + "', see nested exceptions", var12);
} finally {
try {
input.close();
} catch (IOException var11) {
}
}
}
RedisConfig jedisConfig = new RedisConfig();
this.setConfigProperties(config, jedisConfig); //后面代码比较长,就不给出,但是很明显,他必然是读取配置文件进行设置信息,必然修改了: private String host = "localhost";,private int port = 6379;等等
return jedisConfig;
}
//..
}
//至此,前面的 RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();就得到了具体要连接redis的信息,接下来是:
/*
pool = new JedisPool(redisConfig, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getPassword(), redisConfig.getDatabase(), redisConfig.getClientName());
*/
//他直接给出连接信息,返回private static JedisPool pool;,他可以这样操作:
/*
//得到连接池的连接
Jedis jedis = jedisPool.getResource();
jedis.set("haha","23");
System.out.println("添加成功");
即可以操作redis了
*/
private Object execute(RedisCallback callback) {
Jedis jedis = pool.getResource();
Object var3;
try {
var3 = callback.doWithRedis(jedis);
} finally {
jedis.close();
}
return var3;
}
public interface RedisCallback {
Object doWithRedis(Jedis var1);
}
public void putObject(final Object key, final Object value) {
this.execute(new RedisCallback() {
public Object doWithRedis(Jedis jedis) {
jedis.hset(RedisCache.this.id.toString().getBytes(), key.toString().getBytes(), SerializeUtil.serialize(value));
return null;
}
});
}
public Object getObject(final Object key) {
return this.execute(new RedisCallback() {
public Object doWithRedis(Jedis jedis) {
return SerializeUtil.unserialize(jedis.hget(RedisCache.this.id.toString().getBytes(), key.toString().getBytes()));
}
});
}
//很明显,他们都是操作execute方法,并操作hset和hget,即key value(key-value)
public interface Executor {
//..
}
public interface StatementHandler {
//..
}
public interface ParameterHandler {
//..
}
public interface ResultSetHandler {
//..
}
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement var1) throws SQLException;
}
//一般只有该一个子类
public class DefaultParameterHandler implements ParameterHandler {
}
public class Configuration {
//..
protected final InterceptorChain interceptorChain;
//..
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
//操作了interceptorChain.pluginAll(parameterHandler)
parameterHandler = (ParameterHandler)this.interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
//..
}
public class InterceptorChain {
//..
//主要操作:
public Object pluginAll(Object target) {
Interceptor interceptor;
for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) { //操作拦截,或者说代理了一下,或者增强了一下,有多少,那么增强多少,注意,他只是准备,只有在执行时才会出现(这里格外要注意,在后面会再次的说明,一般都会操作增强方法的)
interceptor = (Interceptor)var2.next(); //得到拦截
}
return target; //如果没有增强,自然直接的返回了
}
//..
}
//上面操作了plugin方法
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
)
})
public class ExeunplePlugin implements Interceptor {
//省略逻辑
}
<plugins>
<plugin interceptor="com.lagou.plugin.ExamplePlugin">plugin>
plugins>
package com.lagou.test;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.util.Properties;
/**
*
*/
@Intercepts({ //注意看这个大花括号,也就这说这⾥可以定义多个@Signature对多个地方拦截,都用这个拦截器
@Signature(
type = StatementHandler.class, //这是指拦截哪个接口,即对象的操作
method = "prepare",//这个接口内的哪个方法名,不要拼错了
args = {Connection.class, Integer.class}), //这是拦截的方法的入参,按顺序写到这,不要多也不要少,如果方法重载,可是要通过方法名和入参来确定唯一的
})
public class MyPlugin implements Interceptor {
//这⾥是每次执行操作的时候,都会进行这个拦截器的方法内
/*
拦截方法:只要被拦截的目标对象的目标方法(一般指Signature对应的方法)被执行时,每次都会执行intercept方法
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("增强了"); //一般分页插件的操作,基本都是这里进行的,所以我们也可以在这里进行分页的操作,前提是拦截的对,至于如何进行改变的,这里就不做说明,可以自己去百度查看
return invocation.proceed(); //执行原方法,这样才算是增强的
}
//主要是为了把这个拦截器⽣成一个代理放到拦截器链中
//target为要拦截的对象
@Override
public Object plugin(Object target) {
//包装目标对象 为目标对象创建代理对象,从而保证在执行时,会执行上面的intercept,所以前面才说明是只是准备,因为执行才会操作上面的增强
//可以看到增强或者不增强,返回的都是原方法的值,实际上在前面就是pluginAll里面的return target;,即我们只是增强,并不改变对象
System.out.println("将要包装的目标对象:" + target);
return Plugin.wrap(target, this); //进行包装,也就是准备增强,使得pluginAll会有操作进行(给出上面的intercept但并没有直接执行哦)
}
//获取配置文件的属性
//插件初始化的时候调用,也只调用一次,插件配置的属性从这⾥设置进来,即最先进行操作
@Override
public void setProperties(Properties properties) {
System.out.println("插件配置的初始化参数:" + properties);
}
}
//操作顺序:setProperties,plugin,intercept
//前面我们只给出了ParameterHandler,也就是说,如果一个类操作Intercepts时,指定了对应的类,如ParameterHandler,那么在pluginAll中会操作一次,如果有多个类或多个对应的@Signature注解(即多个增强),那么操作多次内部代码(因为是循环)
//注意:这里我们来说明前面的格外注意:我们其实要知道,对应的pluginAll实际上会操作四次,即对应的Executor,StatementHandler,ParameterHandler,ResultSetHandler
//我们在执行时,可以看到System.out.println("将要包装的目标对象:" + target);打印了四次,分别是:
/*
将要包装的目标对象:org.apache.ibatis.executor.CachingExecutor@27406a17
//可以出现"增强了"的打印
将要包装的目标对象:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@1750fbeb
//可以出现"增强了"的打印
将要包装的目标对象:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@6c284af
//可以出现"增强了"的打印
将要包装的目标对象:org.apache.ibatis.executor.statement.RoutingStatementHandler@5890e879
//可以出现"增强了"的打印
其中StatementHandler是最后的,可以发现,谁先执行pluginAll,那么intercept谁先执行
在前面虽然说是准备,但是其实是随时操作的,因为他操作的就是执行时,进行的准备,所以你可以选择不执行sql,就会发现,一般只有将要包装的目标对象:org.apache.ibatis.executor.CachingExecutor@27406a17了,即四大对象的确是操作执行的哦
所以操作顺序应该是:setProperties,(plugin,pluginAll,一般是pluginAll,plugin,看源码就知道了,因为他们是一体的),intercept,他们是连贯的,一个sql的完全执行,他们四个对象都会操作,后三者是可以操作多次的,而对应的在何处会操作,就不做说明了,可以自己查看,我只是给出大致流程,总不能所有的类都看一遍的,那么是没有必要的,因为世界上那么多框架,(基本)终其一生也是看不完的,我们只需要知道原理即可
//综上所述:四大对象,都有@Intercepts来针对拦截,并且是执行过程中的拦截,或者说sql形成过程中的拦截,分别是初始,参数,返回参数,sql形成的顺序,最终执行sql,所以有些时候,我们进行的增强可能是要确定是那个方面的哦,就如分页插件他是指向Executor的,因为sql执行之前,默认有
// offset 就是 0
cacheKey.update(rowBounds.getOffset());
// limit 就是 Integer.MAXVALUE
//offset和limit一般代表分页参数,当然,在没有设置时,可能日志不会进行显示
cacheKey.update(rowBounds.getLimit());
//即limit,所以先设置
当然,并非一定在Executor里面的,其他的操作对象也可以,所以大多数插件,基本上@Intercepts可以随便写,即随便拦截
//一般来说,缓存也是包括四大对象的,所以也会因为执行不同,而导致是否继续包装的目标对象,但一般的将要包装的目标对象:org.apache.ibatis.executor.CachingExecutor@19e4653c通常只会加载一起,除非是不同的SqlSession,因为Executor实例会在创建 SqlSession 的过程中被创建
*/
<plugins>
<plugin interceptor="com.lagou.test.MyPlugin">
<property name="name" value="Bob"/>
plugin>
plugins>
public class Plugin implements InvocationHandler {
//..
//Proxy.newProxyInstance中可以操作了InvocationHandler参数,Plugin.wrap(target, this)里面可以看到
//获取被拦截方法列表,⽐如:signatureMap.get(Executor.class), 可能返回 [query, update, commit]
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
//检测方法列表是否包含被拦截的方法
return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
//执行插件逻辑:this.interceptor.intercept(new Invocation(this.target, method, args))
//而逻辑中增强,并又返回,所以Plugin.wrap的确是执行了增强,即我们的plugin也是执行了增强
//执行被拦截的方法:method.invoke(this.target, args)
} catch (Exception var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
//..
}
public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
//..
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return this.method.invoke(this.target, this.args);
}
}
//所以之前的:
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("增强了"); //一般分页插件的操作,基本都是这里进行的,所以我们也可以在这里进行分页的操作,前提是拦截的对
return invocation.proceed(); //执行原方法,这样才算是增强的
}
//上面proceed就是返回原方法
<dependency>
<groupId>tk.mybatisgroupId>
<artifactId>mapperartifactId>
<version>3.1.2version>
dependency>
<plugins>
<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<property name="mappers" value="com.lagou.mapper.UserMapper"/>
plugin>
plugins>
//假设有这个类,这里主要看对应的注解的作用是什么,至于是否存在这个类,并不需要了解
//看对应那个表名
@Table(name = "t_user") //mp中就有这个javax.persistence.Table;,所以也证明了mp是利用了增强,即自定义插件
//实际上直接导入javax.persistence.Table;也行,看依赖就知道了,对应的tk.mybatis基本只有他一个(依赖到顶,说明没有其他的包导入了,但并不代表没有到顶的没有类的代码,maven只是看导入的而已,自身的还是有的,自己看包就知道了)
public class User {
@Id //表示主键id
@GeneratedValue(strategy = GenerationType.IDENTITY) //表示:主键的生成策略,操作返回SELECT LAST_INSERT_ID()(一般指: insert 进去记录的主键值,但是有时为1,即返回行数,具体解决可以百度),这里建议不操作这个,他可能导致原来的对象id变成0
private Integer id;
@Column(name = "username") //表示:对应的表字段名称,一般不写是有默认值的,具体自己测试,但只要变量名称与表字段对应,通常不会出现错误(一般也包括驼峰,他通常只是将大写前面加上_,而不会在加上_后将大写变成小写,即还是大写,与mp是不同的)
private String username;
}
public interface UserMapper extends Mapper<User> {
//只是增强,配置接口和读取配置文件可以不用改变,只需要继承即可
//一般来说不能与插件的方法一致,因为对方注解的原因,不能给覆盖,所以使得写上编译会报错,这里了解即可
//而在mp中可能是配置文件优先
}
package com.lagou.test;
import com.lagou.domain.User;
import com.lagou.mapper.UserMapper;
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 org.junit.Test;
import tk.mybatis.mapper.entity.Example;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class UserTest {
@Test
public void test1() 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 = new User();
user.setId(4);
//mapper基础接口
根据实体中的属性进行查询,只能有—个返回值
User user1 = userMapper.selectOne(user);
List<User> users = userMapper.select(null); //查询全部结果
//根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号(如where后面的id=1,的这样的=的等号)
userMapper.selectByPrimaryKey(1);
//根据实体中的属性查询总数,查询条件使用等号
userMapper.selectCount(user);
//insert 接口
//保存一个实体,null值也会保存,不会使用数据库默认值
user.setUsername("null");
int insert = userMapper.insert(user);
System.out.println(insert); //1
System.out.println(user); //id通常会变成0
sqlSession.commit(); //没有整合什么,如spring,或者spring boot,那么没有自动提交,即我们操作提交的
user = new User();
user.setId(44);
// user.setUsername("null"); ,会报错
//保存实体,null的属性不会保存即报错,即只会使用数据库默认值
int i = userMapper.insertSelective(user);
sqlSession.commit();
// update 接口
user = new User();
user.setId(44);
user.setUsername("1");
int i1 = userMapper.updateByPrimaryKey(user);//根据主键更新实体全部字段,null值会被更新
sqlSession.commit();
// delete 接口
user = new User();
user.setId(44);
int delete = userMapper.delete(user); //根据实体属性作为条件进行删除,查询条件使用等号
sqlSession.commit();
userMapper.deleteByPrimaryKey(1); //根据主键字段进行删除,方法参数必须包含完整的主键属性
sqlSession.commit();
//example方法
Example example = new Example(User.class);
//手动的设置对应的值,其中id和username需要存在,否则报错
example.createCriteria().andEqualTo("id", 1); //如果EqualTo存在,一般like不会操作,因为基本只能是一个createCriteria()操作(代表查询所有,后面的就是操作where条件,所以也可以直接的example.createCriteria();),所以如果需要那么这样即可: example.createCriteria().andLike("username", "1").andEqualTo("id", 1);,因为他们返回本身的,一般我们最好使用这个自定义来操作,比较方便(前面的操作最终都是自定义形成的,就如mp可能也是如此)
example.createCriteria().andLike("username", "1");
//自定义查询
List<User> users1 = userMapper.selectByExample(example);
System.out.println(users1);
}
}
//读取配置文件,得到字节输入流,但是还没有进行解析
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
/*
public class Resources {
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
//..
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream((ClassLoader)null, resource);
}
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
} else {
return in;
}
}
//至于后面如何操作的,这里不做说明了
//..
}
*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//要进行剖析,自然是看如何操作的,因为只有操作了,我们才能知道他是如何利用的,当然,如果你水平足够,可以直接的选择去依赖里查看源码,但是这并不是好的办法,所以我们选择从使用开始,然后到解析的方式来进行剖析
public class SqlSessionFactoryBuilder {
//..
//我们最初调用的build
public SqlSessionFactory build(InputStream inputStream) {
//调用了重载方法
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
//..
//到这里来
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
// XMLConfigBuilder是专⻔解析mybatis的配置文件的类
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//这⾥⼜调用了一个重载方法,parser.parse()的返回值是Configuration对象
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
//..
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config); //工厂模式,但是其实这里也有个设计模式,我们一般称为构建者设计模式,那么这个构建者设计模式是什么呢,其实在前面我们也有说明:实际上工厂模式除了将创建对象给方法外,还有一个最为重要的操作,即可以将操作分开,使得一个类不会有多个操作
//构建者设计模式是指:将操作进行分开,而不用返回整个对象或者多余操作的对象,只需要返回具体需要用到变量的对象,就如你只要操作一个变量,我创建类来保存这个变量并返回这个类对象,而不是返回了当前或者其他有这个变量,但是还有其他变量的当前类或者其他类对象,当然不只是变量,方法也是可以包括的哦
}
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
//..
}
/*
Configuration对象的结构和xml配置文件的对象几乎相同
回顾一下xml中的配置标签有哪些:
properties (属性)
settings (设置)
typeAliases (类型别名)
typeHandlers (类型处理器)
objectFactory (对象工厂)
mappers (映射器)
上面的等等,Configuration也有对应的对象属性来封装它们
也就是说,初始化配置文件信息的本质就是创建Configuration对象,将解析的xml数据封装到Configuration内部属性中
这里我们参照之前的手写的代码(自定义的mybatis框架):
*/
XMLConfigerBuilder xmlConfigerBuilder = new XMLConfigerBuilder(configuration); //该configuration是创建的
Configuration configuration = xmlConfigerBuilder.parseConfiguration(inputStream);
SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
//可以发现,最终的xmlConfigerBuilder.parseConfiguration(inputStream);就类似于上面源码中的parser.parse()
//所以现在我们来看看parser.parse()
public class XMLConfigBuilder extends BaseBuilder {
//..
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration()); //创建新的new Configuration(),利用自己的父类哦
this.localReflectorFactory = new DefaultReflectorFactory();
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
//..
//解析 XML 成 Configuration 对象
public Configuration parse() {
//若已解析,抛出BuilderException异常,当然,由于 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);即是新创建的,所以这里基本不会出现抛出异常
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
//标记已解析
this.parsed = true; //设置为true
// 解析 XML configuration 节点,也就是读取对应的信息
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration; //返回创建的该类
}
}
//解析XML
private void parseConfiguration(XNode root) {
try {
// 解析 标签
this.propertiesElement(root.evalNode("properties"));
// 解析 标签
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
//加载自定义的VFS实现类和Log的实现类(一般是mybatis的哦)
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
//解析 标签
this.typeAliasesElement(root.evalNode("typeAliases"));
//解析 标签
this.pluginElement(root.evalNode("plugins"));
// 解析 标签
this.objectFactoryElement(root.evalNode("objectFactory"));
// 解析 标签
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析 标签
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 赋值 ⾄ Configuration 属性
this.settingsElement(settings); //这里操作了很多,特别是后面的ExecutorType的获取,他操作了setDefaultExecutorType,至于得到了什么,自己可以选择去看,但只要了解即可,可以不用去看,从而在后面的说明中可以通过getDefaultExecutorType来得到
// 解析 标签
this.environmentsElement(root.evalNode("environments"));
// 解析 标签
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析 标签
this.typeHandlerElement(root.evalNode("typeHandlers"));
//解析 标签
this.mapperElement(root.evalNode("mappers")); //会进一步解析的
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
//..
}
XMLConfigerBuilder xmlConfigerBuilder = new XMLConfigerBuilder(configuration); //该configuration是创建的
Configuration configuration = xmlConfigerBuilder.parseConfiguration(inputStream);
SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
//并且对应也返回了DefaultSqlSessionFactory,他用来生成SqlSession,一般有很多重载的openSession方法
<select id="getUser" resultType="user" >
select * from user where id=#{id}
select>
public class Configuration {
//..
protected final Map<String, MappedStatement> mappedStatements;
//..
}
//而前面 this.mapperElement(root.evalNode("mappers"));就操作了这些
public interface SqlSession extends Closeable {
//..
//..
}
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
//..
//最终是这里
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
//..
}
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
//..
//..
}
public interface Executor {
}
public class BatchExecutor extends BaseExecutor {
}
public class ReuseExecutor extends BaseExecutor {
}
public class SimpleExecutor extends BaseExecutor {
}
public abstract class BaseExecutor implements Executor {
}
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
//从这里开始了
SqlSession sqlSession = build.openSession();
List<User> objects = sqlSession.selectList("userMapper.findAll");
//之前返回的是:return new DefaultSqlSessionFactory(config);
public class Configuration {
//..
protected ExecutorType defaultExecutorType;
//..
public ExecutorType getDefaultExecutorType() {
return this.defaultExecutorType;
}
public void setDefaultExecutorType(ExecutorType defaultExecutorType) {
this.defaultExecutorType = defaultExecutorType;
}
//..
}
public interface SqlSessionFactory {
//..
//..
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
//..
public SqlSession openSession() {
//getDefaultExecutorType()得到的是ExecutorType,一般是SIMPLE
/*
this.settingsElement(settings); //这里操作了很多,特别是后面的ExecutorType的获取,他操作了setDefaultExecutorType,从而在后面的说明中可以通过getDefaultExecutorType来得到
*/
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
//..
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
//进入openSessionFromDataSource
//TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务自动提交(增删改是默认开启事务的),这里就是false,那么不自动提交,这就是为什么增删改,需要我们自动提交的原因
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据参数创建指定类型的Executor
Executor executor = this.configuration.newExecutor(tx, execType);
//返回的是 DefaultSqlSession
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
//..
}
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
//..
//List objects = sqlSession.selectList("userMapper.findAll");
public <E> List<E> selectList(String statement) {
return this.selectList(statement, (Object)null);
}
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
//根据传入的全限定名+方法名从映射的Map中取出MappedStatement对象
MappedStatement ms = this.configuration.getMappedStatement(statement);
//调用Executor中的方法处理,private final Executor executor;
//RowBounds是用来逻辑分⻚
//wrapCollection(parameter)是用来装饰集合或者数组参数
//Executor接口的 List query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); //他中间进行了一系列的操作,而不是我们自定义框架的一步到位的,这里我只是以查询为例子,具体的增删改等等操作,可以自己去看看,但大体是类似的
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
//..
}
//一般是BaseExecutor(是SimpleExecutor的父类,他没有query,所以要执行BaseExecutor的query),最终操作的是SIMPLE,得到了executor = new SimpleExecutor(this, transaction);
//Executor executor = this.configuration.newExecutor(tx, execType);,execType最终操作的是SIMPLE
public abstract class BaseExecutor implements Executor {
//..
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示
BoundSql boundSql = ms.getBoundSql(parameter);
//为本次查询创建缓存的Key
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
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 (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//如果缓存中没有本次查找的值,那么从数据库中查询
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
//..
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
//查询的方法,到SimpleExecutor里面去执行
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
//将查询结果放入缓存
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
//..
}
public class SimpleExecutor extends BaseExecutor {
//..
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
//传入参数创建StatementHanlder对象来执行查询
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//创建jdbc中的statement对象
stmt = this.prepareStatement(handler, ms.getStatementLog());
// StatementHandler 进行处理
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
//..
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
//这里的代码中的getConnection方法经过重重调用最后会调用openConnection方法,从连接池中获得连接
//JdbcTransaction里面的getConnection方法,最终操作的是tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Connection connection = this.getConnection(statementLog);
//public interface PreparedStatement extends Statement {
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
//设置sql,如操作占位符
handler.parameterize(stmt);
return stmt;
}
}
/*
根据传递的参数,完成SQL语句的动态解析,⽣成BoundSql对象,供StatementHandler使用
为查询创建缓存,以提⾼性能
创建JDBC的Statement连接对象,传递给StatementHandler对象,最终返回List查询结果
*/
//上面的handler.parameterize(stmt);,下面先是RoutingStatementHandler,然后才是PreparedStatementHandler
//PreparedStatementHandler类的(PREPARED),至于为什么就不做说明,可以自己调试,或者自己找对应的代码
public void parameterize(Statement statement) throws SQLException {
this.parameterHandler.setParameters((PreparedStatement)statement);
}
public class DefaultParameterHandler implements ParameterHandler {
//..
//ParameterHandler 类(一般这样说明的接口,通常代表其实现类)的 setParameters(PreparedStatement ps) 实现对某一个Statement进行设置参数
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
if (parameterMappings != null) {
for(int i = 0; i < parameterMappings.size(); ++i) {
ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
Object value;
if (this.boundSql.hasAdditionalParameter(propertyName)) {
value = this.boundSql.getAdditionalParameter(propertyName);
} else if (this.parameterObject == null) {
value = null;
} else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
value = this.parameterObject;
} else {
MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
value = metaObject.getValue(propertyName);
}
// 每一个 Mapping都有一个 TypeHandler,根据 TypeHandler 来对 preparedStatement 进 行设置参数
TypeHandler typeHandler = parameterMapping.getTypeHandler();
//获取jdbc类型,这里就是mysql
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = this.configuration.getJdbcTypeForNull();
}
try {
//设置参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (SQLException | TypeException var10) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
}
}
}
}
}
}
//也是PreparedStatementHandler:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//调用preparedStatemnt.execute()方法,然后将resultSet交给ResultSetHandler处理
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
//使用 ResultHandler 来处理 ResultSet
return this.resultSetHandler.handleResultSets(ps);
}
/*
从上述代码我们可以看出,StatementHandler 的query方法的实现
是调用了 ResultSetHandler 的handleResultSets(Statement)方法
ResultSetHandler 的 handleResultSets(Statement)方法
会将 Statement 语句执行后⽣成的 resultSet结果集转换成List结果集
//public interface PreparedStatement extends Statement {
*/
public class DefaultResultSetHandler implements ResultSetHandler {
//..
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
//多ResultSet的结果集合,每个ResultSet对应一个Object对象,而实际上,每个 Object 相当于是 List
//在不考虑存储过程的多ResultSet的情况(因为存储过程可以执行多个查询,而不是一个,具体jdbc怎么获取存储过程的数据,可以选择百度查看)下,那么普通的查询,实际就一个ResultSet,也就是说,multipleResults最多就一个元素
List<Object> multipleResults = new ArrayList();
int resultSetCount = 0;
//获得⾸个ResultSet对象,并封装成ResultSetWrapper对象
ResultSetWrapper rsw = this.getFirstResultSet(stmt);
//获得ResultMap数组
//在不考虑存储过程的多ResultSet的情况,普通的查询,实际就一个ResultSet,也就是说,resultMaps就一个元素
List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
this.validateResultMapsCount(rsw, resultMapCount); // 校验
while(rsw != null && resultMapCount > resultSetCount) {
//获得ResultMap对象
ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
//处理ResultSet,将结果添加到multipleResults中
this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
//获得下一个ResultSet对象,并封装成ResultSetWrapper对象
rsw = this.getNextResultSet(stmt);
//清理
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
//很明显,上面就是得到信息,最后会变成list集合的
//因为mappedStatement.resultSets只在存储过程中使用,本系列暂时不考虑,忽略即可
String[] resultSets = this.mappedStatement.getResultSets();
if (resultSets != null) {
while(rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
}
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
}
//如果是multipleResults单元素,则取⾸元素返回,否则都返回,这里是考虑存储过程
return this.collapseSingleResultList(multipleResults);
}
//..
}
//假设是如下:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.getUserByName("tom");
<mappers>
<package name="com.lagou.mapper"/>
mappers>
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
//..
//最终到这里
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//得到对应类的所有接口方法信息
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
//通过动态代理工厂⽣成示例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
//..
}
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();
//..
//..
//最终到这里,很明显是jdk代理,与我们自定义的mybatis框架是使用同一种代理
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//创建了 JDK动态代理的Handler类
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
//调用了重载方法
return this.newInstance(mapperProxy);
}
}
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -4724728412955527868L;
private static final int ALLOWED_MODES = 15;
private static final Constructor<Lookup> lookupConstructor;
private static final Method privateLookupInMethod;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperProxy.MapperMethodInvoker> methodCache;
//构造,传入了 SqlSession,说明不同的session中的代理对象的不同的,即最终的sql操作必然不同,因为SqlSession不同,那么DefaultSqlSession也就不同哦,那么对应的传统代码信息必然也是不同的,虽然读取的配置文件可能相同,但也保证了 var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);是不同的,即不会操作到同一个,那么缓存的一级和二级怎么对比呢,实际上他根据id的,并且有确定是否对应的配置文件,所以才使得一级和二级共用
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperProxy.MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
//..
//..
}
//MapperProxy类里面的
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//增强是增强,我们调用对应方法返回的结果自然也是invoke的返回值哦
//如果是Object定义的方法,直接调用
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
//this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
public class MapperProxy<T> implements InvocationHandler, Serializable {
//..
//操作this.cachedInvoker(method)
private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return (MapperProxy.MapperMethodInvoker)this.methodCache.computeIfAbsent(method, (m) -> {
if (m.isDefault()) {
try {
return privateLookupInMethod == null ? new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava9(method));
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
throw new RuntimeException(var4);
}
} else {
return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
}
});
} catch (RuntimeException var4) {
Throwable cause = var4.getCause();
throw (Throwable)(cause == null ? var4 : cause);
}
}
//..
}
//最终到这里(调试就知道了):
//MapperProxy的内部类
private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
this.mapperMethod = mapperMethod;
}
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
//执行了这个方法
return this.mapperMethod.execute(sqlSession, args);
}
}
public class MapperMethod {
//..
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
//判断mapper中的方法类型,可以发现最终调用的还是SqlSession中的方法
switch(this.command.getType()) {
case INSERT:
//转换参数
param = this.method.convertArgsToSqlCommandParam(args);
//执行INSERT操作
// 转换 rowCount
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
//⽆返回,并且有ResultHandler方法参数,则将查询的结果,提交给 ResultHandler 进行处理
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
//执行查询,返回列表
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
//执行查询,返回Map
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
//执行查询,返回Cursor
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
//执行查询,返回单个对象
} else {
param = this.method.convertArgsToSqlCommandParam(args);
//查询单条
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
//返回结果为null,并且返回类型为基本类型,则抛出BindingException异常
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
//返回结果
return result;
}
}
//..
}
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
<cache>cache>
<select id="findById" resultType="com.lagou.pojo.User" useCache="true">
select * from user where id = #{id}
select>
public class XMLConfigBuilder extends BaseBuilder {
//..
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
// 就是这⾥
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
//..
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
// 假设按照对应的配置到这里,则直接⾛该if判断(因为走包找类的话,那么基本就是先找路径,最后找标签,而不是直接的找标签,所以不好查找,但是最终肯定是类似的,所以我们直接看这里即可)
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
// ⽣成XMLMapperBuilder,并执行其parse方法
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
//..
}
public class XMLMapperBuilder extends BaseBuilder {
//..
public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
// 解析mapper属性
this.configurationElement(this.parser.evalNode("/mapper"));
this.configuration.addLoadedResource(this.resource);
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}
//..
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
this.builderAssistant.setCurrentNamespace(namespace);
this.cacheRefElement(context.evalNode("cache-ref"));
// 最终在这⾥看到了关于cache属性的处理
this.cacheElement(context.evalNode("cache"));
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
this.sqlElement(context.evalNodes("/mapper/sql"));
// 这⾥会将⽣成的Cache包装到对应的MappedStatement
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} else {
throw new BuilderException("Mapper's namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
}
}
//..
private void cacheElement(XNode context) {
if (context != null) {
//解析 标签的type属性,这⾥我们可以自定义cache的实现类,⽐如redisCache,如果没有自定义,这⾥使用和一级缓存相同的PERPETUAL
/*
public String getStringAttribute(String name, String def) {
String value = this.attributes.getProperty(name);
return value == null ? def : value; //有的话,就操作我们设置的,否则默认就是PERPETUAL(def变量)
}
*/
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = this.typeAliasRegistry.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = this.typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
// 构建Cache对象
this.builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
//..
}
public class MapperBuilderAssistant extends BaseBuilder {
//..
public Cache useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props) {
//⽣成Cache对象,他一般代表SynchronizedCache,但是最终还是会到PerpetualCache,虽然他只是存在,但并不一定操作,其他的如SynchronizedCache最终的LoggingCache可能就是操作唯一的key的
Cache cache = (new CacheBuilder(this.currentNamespace)).implementation((Class)this.valueOrDefault(typeClass, PerpetualCache.class)).addDecorator((Class)this.valueOrDefault(evictionClass, LruCache.class)).clearInterval(flushInterval).size(size).readWrite(readWrite).blocking(blocking).properties(props).build();
//添加到Configuration中
this.configuration.addCache(cache);
this.currentCache = cache; //还进行了赋值
return cache;
}
//..
}
// 最终在这⾥看到了关于cache属性的处理
this.cacheElement(context.evalNode("cache"));
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
this.sqlElement(context.evalNodes("/mapper/sql"));
// 这⾥会将⽣成的Cache包装到对应的MappedStatement
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
// XMLMapperBuilder类里面的
private void buildStatementFromContext(List<XNode> list) {
if (this.configuration.getDatabaseId() != null) {
this.buildStatementFromContext(list, this.configuration.getDatabaseId());
}
this.buildStatementFromContext(list, (String)null);
}
//无论对应的判断是否为null,都会到这里来
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
Iterator var3 = list.iterator();
while(var3.hasNext()) {
XNode context = (XNode)var3.next();
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
// 每一条执行语句转换成一个MappedStatement
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser);
}
}
}
public class XMLStatementBuilder extends BaseBuilder {
//..
public void parseStatementNode() {
String id = this.context.getStringAttribute("id");
String databaseId = this.context.getStringAttribute("databaseId");
if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
String nodeName = this.context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
includeParser.applyIncludes(this.context.getNode());
String parameterType = this.context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = this.resolveClass(parameterType);
String lang = this.context.getStringAttribute("lang");
LanguageDriver langDriver = this.getLanguageDriver(lang);
this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
String keyStatementId = id + "!selectKey";
keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
Object keyGenerator;
if (this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = this.context.getIntAttribute("fetchSize");
Integer timeout = this.context.getIntAttribute("timeout");
String parameterMap = this.context.getStringAttribute("parameterMap");
String resultType = this.context.getStringAttribute("resultType");
Class<?> resultTypeClass = this.resolveClass(resultType);
String resultMap = this.context.getStringAttribute("resultMap");
String resultSetType = this.context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = this.configuration.getDefaultResultSetType();
}
String keyProperty = this.context.getStringAttribute("keyProperty");
String keyColumn = this.context.getStringAttribute("keyColumn");
String resultSets = this.context.getStringAttribute("resultSets");
// 创建MappedStatement对象
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
//..
}
public class MapperBuilderAssistant extends BaseBuilder {
//..
public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) {
if (this.unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
} else {
id = this.applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//创建MappedStatement对象
org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = (new org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType)).resource(this.resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(this.getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired((Boolean)this.valueOrDefault(flushCache, !isSelect)).useCache((Boolean)this.valueOrDefault(useCache, isSelect)).cache(this.currentCache);
// 上面最后的.cache(this.currentCache);在这⾥将之前⽣成的Cache封装到MappedStatement
ParameterMap statementParameterMap = this.getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
this.configuration.addMappedStatement(statement);
return statement;
}
}
//..
}
//public abstract class BaseExecutor implements Executor {,不是这一个的,这是因为在操作二级缓存时,他不会操作他的,或者说对应的对象发生了改变,具体为什么,那么需要很多代码的查看,这里就不给出了,可以认为最终之前的缓存读取会改变这个对象即可,既然改变了,那么也就说明之前的clearLocalCache()是不同的,即对应的this.localOutputParameterCache.clear();大概率不是二级缓存,且也就基本不是resultHandler的原因了
/*
具体原因是之前的:
this.configuration.newExecutor(tx, execType);里面的
if (this.cacheEnabled) { //操作二级缓存,就会操作如下,并给出原来的在这之前赋值的executor
executor = new CachingExecutor((Executor)executor);
}
可以全局搜索
*/
public class CachingExecutor implements Executor {
//..
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//..
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 从 MappedStatement 中获取 Cache,注意这⾥的 Cache 是从MappedStatement中获取的
// 也就是我们上面解析Mapper中 标签中创建的,它保存在Configration中
// 我们在上面分析过每一个MappedStatement都有一个相同Cache对象,就是这⾥
Cache cache = ms.getCache();
// 如果配置文件中没有配置 ,则 cache 为空
if (cache != null) {
//如果需要刷新缓存的话就刷新:flushCache="true"(实际上是清空,最终也会导致一级缓存清空的,在执行操作之前进行清空)
/*
对应的一级缓存对应的操作中存在这个(后面的this.delegate.query)
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache(); //也会清空
}
*/
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql);
// 访问⼆级缓存,注意是操作tcm的,即二级缓存最终是tcm操作,或者说是存放在tcm中
List<E> list = (List)this.tcm.getObject(cache, key);
// 缓存未命中
if (list == null) {
// 如果没有值,则执行查询,这个查询实际也是先⾛一级缓存查询,一级缓存也没有的话,则进行DB查询
//this.delegate就是原来的executor,对应于executor = new BatchExecutor(this, transaction);,也更加的证明了,之前说明的二级缓存的确是上层的,并且一级缓存那里的确没有操作二级缓存
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 缓存查询结果
this.tcm.putObject(cache, key, list);
}
return list;
}
}
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//..
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
this.tcm.clear(cache); //清除缓存
}
}
//..
}
<select id="findbyId" resultType="com.lagou.pojo.user" useCache="true"
flushCache="true">
select * from user
select>
// private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public class TransactionalCacheManager {
// Cache 与 TransactionalCache 的映射关系表
private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap();
public TransactionalCacheManager() {
}
public void clear(Cache cache) {
// 获取 TransactionalCache 对象,并调用该对象的 clear 方法
this.getTransactionalCache(cache).clear();
}
public Object getObject(Cache cache, CacheKey key) {
// 直接从TransactionalCache中获取缓存
return this.getTransactionalCache(cache).getObject(key);
}
public void putObject(Cache cache, CacheKey key, Object value) {
// 直接存入TransactionalCache的缓存中
this.getTransactionalCache(cache).putObject(key, value);
}
public void commit() {
Iterator var1 = this.transactionalCaches.values().iterator();
while(var1.hasNext()) {
TransactionalCache txCache = (TransactionalCache)var1.next();
txCache.commit();
}
}
public void rollback() {
Iterator var1 = this.transactionalCaches.values().iterator();
while(var1.hasNext()) {
TransactionalCache txCache = (TransactionalCache)var1.next();
txCache.rollback();
}
}
private TransactionalCache getTransactionalCache(Cache cache) {
// 从映射表中获取 TransactionalCache
// TransactionalCache 也是一种装饰类,为 Cache 增加事务功能
// 创建一个新的TransactionalCache,并将真正的Cache对象存进去,传递的参数Cache只是用来对应真正的Cache的
return (TransactionalCache)this.transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
}
}
public class TransactionalCache implements Cache {
private static final Log log = LogFactory.getLog(TransactionalCache.class);
//真正的缓存对象,和上面的Map中的Cache是同一个
private final Cache delegate;
private boolean clearOnCommit;
// 在事务被提交前,所有从数据库中查询的结果将缓存在此集合中
private final Map<Object, Object> entriesToAddOnCommit;
// 在事务被提交前,当缓存未命中时,CacheKey 将会被存储在此集合中,单纯的保存key,来操作上面的entriesToAddOnCommit的
private final Set<Object> entriesMissedInCache;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap();
this.entriesMissedInCache = new HashSet();
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
public Object getObject(Object key) {
// 查询的时候是直接从delegate中去查询的,也就是从真正的缓存对象中查询
Object object = this.delegate.getObject(key);
if (object == null) {
// 缓存未命中,则将 key 存入到 entriesMissedInCache 中
this.entriesMissedInCache.add(key);
}
return this.clearOnCommit ? null : object;
}
public void putObject(Object key, Object object) {
// 将键值对存入到 entriesToAddOnCommit 这个Map中中,而⾮真实的缓存对象 delegate 中
this.entriesToAddOnCommit.put(key, object);
}
public Object removeObject(Object key) {
return null;
}
public void clear() {
this.clearOnCommit = true;
// 清空 entriesToAddOnCommit,但不清空 delegate 缓存
this.entriesToAddOnCommit.clear();
}
public void commit() {
// 根据 clearOnCommit 的值决定是否清空 delegate
if (this.clearOnCommit) {
this.delegate.clear();
}
// 刷新未缓存的结果到 delegate 缓存中
this.flushPendingEntries();
// 重置 entriesToAddOnCommit 和 entriesMissedInCache
this.reset();
}
public void rollback() {
this.unlockMissedEntries();
this.reset();
}
private void reset() {
this.clearOnCommit = false;
// 清空集合
this.entriesToAddOnCommit.clear();
this.entriesMissedInCache.clear();
}
private void flushPendingEntries() {
Iterator var1 = this.entriesToAddOnCommit.entrySet().iterator();
while(var1.hasNext()) {
Entry<Object, Object> entry = (Entry)var1.next();
// 将 entriesToAddOnCommit 中的内容转存到 delegate 中
this.delegate.putObject(entry.getKey(), entry.getValue());
}
var1 = this.entriesMissedInCache.iterator();
while(var1.hasNext()) {
Object entry = var1.next();
if (!this.entriesToAddOnCommit.containsKey(entry)) {
// 存入空值
this.delegate.putObject(entry, (Object)null);
}
}
}
private void unlockMissedEntries() {
Iterator var1 = this.entriesMissedInCache.iterator();
while(var1.hasNext()) {
Object entry = var1.next();
try {
// 调用 removeObject 进行解锁
this.delegate.removeObject(entry);
} catch (Exception var4) {
log.warn("Unexpected exception while notifiying a rollback to the cache adapter. Consider upgrading your cache adapter to the latest version. Cause: " + var4);
}
}
}
}
public class DefaultSqlSession implements SqlSession {
//..
public void commit(boolean force) {
try {
// 主要是这句
this.executor.commit(this.isCommitOrRollbackRequired(force));
this.dirty = false;
} catch (Exception var6) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + var6, var6);
} finally {
ErrorContext.instance().reset();
}
}
//..
}
public class CachingExecutor implements Executor {
//..
public void commit(boolean required) throws SQLException {
this.delegate.commit(required);
this.tcm.commit(); //这里
}
//..
}
//TransactionalCacheManager类里面的
public void commit() {
Iterator var1 = this.transactionalCaches.values().iterator();
while(var1.hasNext()) {
TransactionalCache txCache = (TransactionalCache)var1.next();
txCache.commit();
}
}
//TransactionalCache
public void commit() {
if (this.clearOnCommit) {
this.delegate.clear(); //清空
}
this.flushPendingEntries(); //刷新,当然,他内部是将原来保存的放入delegate中,而这样如此,其他事务操作时会操作他的,但是要注意:他是提交的,自然也就导致其他事务获取了他,所以也就解决了脏读(读已提交)
this.reset();
}
//..
private void flushPendingEntries() {
Iterator var1 = this.entriesToAddOnCommit.entrySet().iterator();
while(var1.hasNext()) {
Entry<Object, Object> entry = (Entry)var1.next();
// 在这⾥真正的将entriesToAddOnCommit的对象逐个添加到delegate中,只有这时,二级缓存才真正的⽣效
this.delegate.putObject(entry.getKey(), entry.getValue());
}
var1 = this.entriesMissedInCache.iterator();
while(var1.hasNext()) {
Object entry = var1.next();
if (!this.entriesToAddOnCommit.containsKey(entry)) {
this.delegate.putObject(entry, (Object)null);
}
}
}
//DefaultSqlSession
public int update(String statement, Object parameter) {
int var4;
try {
this.dirty = true;
MappedStatement ms = this.configuration.getMappedStatement(statement);
var4 = this.executor.update(ms, this.wrapCollection(parameter));
} catch (Exception var8) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + var8, var8);
} finally {
ErrorContext.instance().reset();
}
return var4;
}
//CachingExecutor
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
this.flushCacheIfRequired(ms);
return this.delegate.update(ms, parameterObject); //里面清空一级缓存
}
//..
private void flushCacheIfRequired(MappedStatement ms) {
//获取MappedStatement对应的Cache,进行清空
Cache cache = ms.getCache();
//SQL需设置flushCache="true" 才会执行清空
if (cache != null && ms.isFlushCacheRequired()) {
this.tcm.clear(cache);
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.apache.ibatis.executor.loader;
/** @deprecated */
@Deprecated
public final class JavassistProxyFactory extends org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory {
public JavassistProxyFactory() {
}
}
//org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory
public class JavassistProxyFactory implements ProxyFactory {
//..
}
@Deprecated
public class CglibProxyFactory extends org.apache.ibatis.executor.loader.cglib.CglibProxyFactory {
public CglibProxyFactory() {
}
}
//org.apache.ibatis.executor.loader.cglib.CglibProxyFactory
public class CglibProxyFactory implements ProxyFactory {
//..
}
public interface ProxyFactory {
default void setProperties(Properties properties) {
}
Object createProxy(Object var1, ResultLoaderMap var2, Configuration var3, ObjectFactory var4, List<Class<?>> var5, List<Object> var6);
}
public class Configuration {
//..
//aggressiveLazyLoading:
/*
当开启时,任何方法的调用都会加载该对象的所有延迟加载属性,否则,每个属性会按需加载(参考lazyLoadTriggerMethods)
换句话说,如果没有设置,也就是说没有lazyLoadTriggerMethods,那么默认对应的四个属性都操作延迟的,而加上了lazyLoadTriggerMethods那么设置的如上面的toString不延迟加载了,即lazyLoadTriggerMethods可以使得被的sql不用保存,但他也默认存在对应的四个,所以说,这个true相当于默认的lazyLoadTriggerMethods,而lazyLoadTriggerMethods导致他为false,但他由于是默认的四个,所以通常如果没有设置,那么还是相同的,而只有设置了才不会
*/
//默认为true
protected boolean aggressiveLazyLoading;
//..
public Configuration() {
//..
this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
//..
//是否开启延迟加载,默认是false的
this.lazyLoadingEnabled = false;
//..
}
//..
//默认使用Javassist代理工厂
public void setProxyFactory(ProxyFactory proxyFactory) {
if (proxyFactory == null) {
proxyFactory = new JavassistProxyFactory();
}
this.proxyFactory = (ProxyFactory)proxyFactory;
}
//..
}
public class DefaultResultSetHandler implements ResultSetHandler {
//..
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false;
List<Class<?>> constructorArgTypes = new ArrayList();
List<Object> constructorArgs = new ArrayList();
//创建返回的结果映射的真实对象,比如User的对象(相当于new User(),没有任何的赋值),他主要用来给赋值的,但不是这里,是上层方法getRowValue进行的后续操作(自己调试即可)
Object resultObject = this.createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
Iterator var9 = propertyMappings.iterator();
while(var9.hasNext()) {
ResultMapping propertyMapping = (ResultMapping)var9.next();
// 判断属性有没配置嵌套查询,如果有就创建代理对象,propertyMapping.isLazy()看看有没有设置对应的属性fetchType="lazy",一般只有特点的标签才有他哦,否则报错(约束,collection和association一般可以)
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
//创建延迟加载代理对象
resultObject = this.configuration.getProxyFactory().createProxy(resultObject, lazyLoader, this.configuration, this.objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
return resultObject;
}
//创建返回的结果映射的真实对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {
Class<?> resultType = resultMap.getType();
MetaClass metaType = MetaClass.forClass(resultType, this.reflectorFactory);
List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (this.hasTypeHandlerForResultObject(rsw, resultType)) {
return this.createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return this.createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (!resultType.isInterface() && !metaType.hasDefaultConstructor()) {
if (this.shouldApplyAutomaticMappings(resultMap, false)) {
return this.createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
} else {
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
} else {
return this.objectFactory.create(resultType);
}
}
//..
}
//上面的最终方法是前面的this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);里面的,自己可以进行调试(前面并没有说明)
//创建延迟加载代理对象,主要看看createProxy
resultObject = this.configuration.getProxyFactory().createProxy(resultObject, lazyLoader, this.configuration, this.objectFactory, constructorArgTypes, constructorArgs);
//Configuration的
public ProxyFactory getProxyFactory() {
return this.proxyFactory; //在前面proxyFactory = new JavassistProxyFactory();
}
public class JavassistProxyFactory implements ProxyFactory {
//..
/*
target 目标结果对象
lazyLoader 延迟加载对象
configuration 配置
objectFactory 对象工厂
constructorArgTypes 构造参数类型
constructorArgs 构造参数值
*/
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
//进入
return JavassistProxyFactory.EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}
//..
static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
org.apache.ibatis.javassist.util.proxy.ProxyFactory enhancer = new org.apache.ibatis.javassist.util.proxy.ProxyFactory();
/*
package org.apache.ibatis.javassist.util.proxy;
不是对应的package org.apache.ibatis.executor.loader;里的ProxyFactory接口,这个接口才是操作我们的JavassistProxyFactory的
public class ProxyFactory {
//..
}
*/
enhancer.setSuperclass(type);
try {
//通过获取对象方法,判断是否存在该方法
type.getDeclaredMethod("writeReplace");
if (JavassistProxyFactory.LogHolder.log.isDebugEnabled()) {
JavassistProxyFactory.LogHolder.log.debug("writeReplace method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException var10) {
//没找到该方法,实现接口
enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
} catch (SecurityException var11) {
}
Class<?>[] typesArray = (Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
Object enhanced;
try {
//创建新的代理对象
enhanced = enhancer.create(typesArray, valuesArray);
} catch (Exception var9) {
throw new ExecutorException("Error creating lazy proxy. Cause: " + var9, var9);
}
//设置代理执行器
((Proxy)enhanced).setHandler(callback);
return enhanced; //返回的他最终会操作invoke方法的
}
//..
//上面最终到这里:
private static class EnhancedResultObjectProxyImpl implements MethodHandler {
//..
private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
this.type = type;
this.lazyLoader = lazyLoader;
this.aggressive = configuration.isAggressiveLazyLoading();
this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
this.objectFactory = objectFactory;
this.constructorArgTypes = constructorArgTypes;
this.constructorArgs = constructorArgs;
}
/*
target 目标结果对象
lazyLoader 延迟加载对象
configuration 配置
objectFactory 对象工厂
constructorArgTypes 构造参数类型
constructorArgs 构造参数值
*/
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
Class<?> type = target.getClass();
//初始化
JavassistProxyFactory.EnhancedResultObjectProxyImpl callback = new JavassistProxyFactory.EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
//操作前面的crateProxy
Object enhanced = JavassistProxyFactory.crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
}
//操作是否延迟的主要操作,考虑保留sql,以后继续执行的
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
String methodName = method.getName();
try {
synchronized(this.lazyLoader) {
if ("writeReplace".equals(methodName)) {
//忽略暂未找到具体作用
Object original;
if (this.constructorArgTypes.isEmpty()) {
original = this.objectFactory.create(this.type);
} else {
original = this.objectFactory.create(this.type, this.constructorArgTypes, this.constructorArgs);
}
PropertyCopier.copyBeanProperties(this.type, enhanced, original);
if (this.lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, this.lazyLoader.getProperties(), this.objectFactory, this.constructorArgTypes, this.constructorArgs);
}
return original;
}
//延迟加载数量大于0
if (this.lazyLoader.size() > 0 && !"finalize".equals(methodName)) {
//aggressive一次性加载所有延迟加载属性,代表之前的aggressiveLazyLoading的值,如果没有配置lazyLoadTriggerMethods,那么该值默认为true,而配置了(在配置类中操作,这里就不给出具体的代码了),那么他就是false,自然判断后面是否存在了,而考虑是否不操作延迟(这里会到true的,因为为false,即我们设置的主要是里面的操作,这里要注意)
if (!this.aggressive && !this.lazyLoadTriggerMethods.contains(methodName)) {
String property;
//判断是否为set方法,set方法不需要延迟加载
if (PropertyNamer.isSetter(methodName)) {
property = PropertyNamer.methodToProperty(methodName);
this.lazyLoader.remove(property);
} else if (PropertyNamer.isGetter(methodName)) {
property = PropertyNamer.methodToProperty(methodName);
if (this.lazyLoader.hasLoader(property)) {
//延迟加载单个属性,即真的执行的地方
this.lazyLoader.load(property);
}
}
} else {
//一次全部加载
this.lazyLoader.loadAll();
}
}
}
//执行原来的方法
return methodProxy.invoke(enhanced, args);
} catch (Throwable var10) {
throw ExceptionUtil.unwrapThrowable(var10);
}
}
}
//..
}
//对应的上层getRowValue里面的foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
//是invoke执行的地方,从而判断是否延迟的主要操作
package test;
/**
*
*/
public class Computer {
private String displayer; //显示器
private String mainUnit; //主机
private String mouse; //鼠标
private String keyboard; //键盘
public String getDisplayer() {
return displayer;
}
public void setDisplayer(String displayer) {
this.displayer = displayer;
}
public String getMainUnit() {
return mainUnit;
}
public void setMainUnit(String mainUnit) {
this.mainUnit = mainUnit;
}
public String getMouse() {
return mouse;
}
public void setMouse(String mouse) {
this.mouse = mouse;
}
public String getKeyboard() {
return keyboard;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
@Override
public String toString() {
return "computer{" +
"displayer='" + displayer + '\'' +
", mainUnit='" + mainUnit + '\'' +
", mouse='" + mouse + '\'' +
", keyboard='" + keyboard + '\'' +
'}';
}
}
package test;
/**
*
*/
public class ComputerBuilder {
private Computer target = new Computer();
public void installDisplayer(String displayer) {
target.setDisplayer(displayer);
}
public void installMainUnit(String mainUnit) {
target.setMainUnit(mainUnit);
}
public void installMouse(String mouse) {
target.setMouse(mouse);
}
public void installKeybord(String keyboard) {
target.setKeyboard(keyboard);
}
public Computer build() {
return target;
}
}
package test;
/**
*
*/
public class test {
public static void main(String[] args) {
ComputerBuilder computerBuilder = new ComputerBuilder();
computerBuilder.installDisplayer("显万器");
computerBuilder.installMainUnit("主机");
computerBuilder.installKeybord("键盘");
computerBuilder.installMouse("⿏标");
Computer computer = computerBuilder.build();
System.out.println(computer);
}
}
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper")); //this.parser: private final XPathParser parser;
this.configuration.addLoadedResource(this.resource);
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements(); //如这里
}
package test1;
/**
*
*/
public abstract class Computer {
//产品的抽象方法,由具体的产品类去实现
public abstract void start();
}
package test1;
/**
*
*/
public class LenovoComputer extends Computer {
@Override
public void start() {
System.out.println("联想电脑启动");
}
}
package test1;
/**
*
*/
public class HpComputer extends Computer {
@Override
public void start() {
System.out.println("惠普电脑启动");
}
}
package test1;
/**
*
*/
public class ComputerFactory {
public static Computer createComputer(String type) {
Computer mComputer = null;
switch (type) {
case "lenovo":
mComputer = new LenovoComputer();
break;
case "hp":
mComputer = new HpComputer();
break;
}
return mComputer;
}
}
package test1;
/**
*
*/
public class CreatComputer {
public static void main(String[] args) {
Computer lenovo = ComputerFactory.createComputer("lenovo");
System.out.println(lenovo);
Computer hp = ComputerFactory.createComputer("hp");
System.out.println(hp);
}
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据参数创建制定类型的Executor
Executor executor = this.configuration.newExecutor(tx, execType);
//返回的是 DefaultSqlSession
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
package test2;
/**
* 抽象类人
*/
public interface Person {
void doSomething();
}
package test2;
/**
*
*/
public class Bob implements Person {
@Override
public void doSomething() {
System.out.println("你好,我的贵人");
}
}
package test2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理,需实现 InvocationHandler 接口
*/
public class JDKDynamicProxy implements InvocationHandler {
//被代理的对象
Person target;
// JDKDynamicProxy 构造函数
public JDKDynamicProxy(Person person) {
this.target = person;
}
//获取代理对象
public Person getTarget() {
//可以写成this,因为有实现类,不用操作匿名内部类了,只是需要自己写invoke方法了
return (Person) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//动态代理invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//被代理方法前执行
System.out.println("你好吗");
//执行被代理的方法,自然也会给我们操作代理的返回了
Person result = (Person) method.invoke(target, args);
//被代理方法后执行
System.out.println("我很好");
return result; //执行对应代理方法后的返回值
}
}
package test2;
/**
* JDK动态代理测试
*/
public class JDKDynamicTest {
public static void main(String[] args) {
System.out.println("不使用代理类,调用doSomething方法");
//不使用代理类
Person person = new Bob();
// 调用 doSomething 方法
person.doSomething();
System.out.println("分割线-----------");
System.out.println("使用代理类,调用doSomething方法");
//获取代理类
Person proxyPerson = new JDKDynamicProxy(new Bob()).getTarget();
// 调用 doSomething 方法
proxyPerson.doSomething();
}
}
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy