package com.kkb.mybatis.test;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import org.dom4j.io.SAXReader;
import org.junit.Test;
import com.kkb.mybatis.framework.config.Configuration;
import com.kkb.mybatis.framework.config.MappedStatement;
import com.kkb.mybatis.framework.sqlnode.IfSqlNode;
import com.kkb.mybatis.framework.sqlnode.MixedSqlNode;
import com.kkb.mybatis.framework.sqlnode.StaticTextSqlNode;
import com.kkb.mybatis.framework.sqlnode.TextSqlNode;
import com.kkb.mybatis.framework.sqlnode.iface.SqlNode;
import com.kkb.mybatis.framework.sqlsource.BoundSql;
import com.kkb.mybatis.framework.sqlsource.DynamicSqlSource;
import com.kkb.mybatis.framework.sqlsource.ParameterMapping;
import com.kkb.mybatis.framework.sqlsource.RawSqlSource;
import com.kkb.mybatis.framework.sqlsource.iface.SqlSource;
import com.kkb.mybatis.framework.utils.SimpleTypeRegistry;
import com.kkb.mybatis.po.User;
/**
* 目的是使用XML来表达mybatis的全局配置信息,和业务相关的SQL映射信息 (映射文件) 其次,优化数据连接的创建(使用连接池)
*
* @author 灭霸詹
*
*/
public class MybatisV2 {
/**
* 封装了全局配置文件和映射文件中的所有信息
*/
private Configuration configuration = new Configuration();
private String namespace;
private boolean isDynamic = false;
@Test
public void test() {
loadConfiguration();
User user = new User();
user.setId(1);
user.setUsername("王五");
List<User> users = selectList("test.findUserById", user);
System.out.println(users);
}
private void loadConfiguration() {
// 指定全局配置文件的路径
String location = "mybatis-config.xml";
// 获取对应的InputStream对象
InputStream inputStream = getInputStream(location);
// 获取Document对象
Document document = createDocument(inputStream);
// 按照全局配置文件和映射文件的XML语义进行解析
loadConfigurationElement(document.getRootElement());
}
/**
*
* @param rootElement
* <configuration>
*/
private void loadConfigurationElement(Element rootElement) {
Element environments = rootElement.element("environments");
parseEnvironments(environments);
Element mappers = rootElement.element("mappers");
parseMappers(mappers);
}
/**
*
* @param mappers
* <mappers>
*/
@SuppressWarnings("unchecked")
private void parseMappers(Element mappers) {
List<Element> mapperElements = mappers.elements("mapper");
for (Element mapperElement : mapperElements) {
parseMapper(mapperElement);
}
}
private void parseMapper(Element mapperElement) {
String resource = mapperElement.attributeValue("resource");
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(resource);
Document document = createDocument(inputStream);
// 按照映射文件的语义进行解析
parseXmlMapper(document.getRootElement());
}
@SuppressWarnings("unchecked")
private void parseXmlMapper(Element rootElement) {
// 为了方便管理statement,需要使用statement唯一标识
namespace = rootElement.attributeValue("namespace");
List<Element> selectElements = rootElement.elements("select");
for (Element selectElement : selectElements) {
parseStatementElement(selectElement);
}
}
private void parseStatementElement(Element selectElement) {
String statementId = selectElement.attributeValue("id");
if (statementId == null || selectElement.equals("")) {
return;
}
// 一个CURD标签对应一个MappedStatement对象
// 一个MappedStatement对象由一个statementId来标识,所以保证唯一性
// statementId = namespace + "." + CRUD标签的id属性
statementId = namespace + "." + statementId;
String parameterType = selectElement.attributeValue("parameterType");
Class<?> parameterClass = resolveType(parameterType);
String resultType = selectElement.attributeValue("resultType");
Class<?> resultClass = resolveType(resultType);
String statementType = selectElement.attributeValue("statementType");
statementType = statementType == null || statementType == "" ? "prepared" : statementType;
// SqlSource的封装过程
SqlSource sqlSource = createSqlSource(selectElement);
// TODO 建议使用构建者模式去优化
MappedStatement mappedStatement = new MappedStatement(statementId, parameterClass, resultClass, statementType,
sqlSource);
configuration.addMappedStatement(statementId, mappedStatement);
}
private SqlSource createSqlSource(Element selectElement) {
SqlSource sqlSource = parseScriptNode(selectElement);
return sqlSource;
}
private SqlSource parseScriptNode(Element selectElement) {
// 解析select标签下的所有SQL脚本信息,比如sql文本片段、动态标签等,最终封装成SqlNode集合
MixedSqlNode rootSqlNode = parseDynamicTags(selectElement);
// 在解析SqlNode的时候,我们需要知道这些Sql文本中是否包含${},或者sql脚本中是否包含动态标签
// 包含${}和动态标签的这种SQL信息,我们封装到DynamicSqlSource
SqlSource sqlSource = null;
if (isDynamic) {
sqlSource = new DynamicSqlSource(rootSqlNode);
} else {
sqlSource = new RawSqlSource(rootSqlNode);
}
// 如果整个SQL信息中只包含#{}或者说什么特殊字符都没有,那么我们将这些SQL信息封装到RawSqlSource
return sqlSource;
}
private MixedSqlNode parseDynamicTags(Element selectElement) {
List<SqlNode> sqlNodes = new ArrayList<>();
// 获取select并且的子节点
int nodeCount = selectElement.nodeCount();
// 遍历所有节点
for (int i = 0; i < nodeCount; i++) {
Node node = selectElement.node(i);
// 判断节点是文本节点还是Element节点,如果是文本节点
if (node instanceof Text) {
// 获取SQL文本
String sqlText = node.getText().trim();
if (sqlText == null || "".equals(sqlText)) {
continue;
}
// 判断文本节点中是否包含${}
TextSqlNode textSqlNode = new TextSqlNode(sqlText);
// 如果包含${},那么将文本节点的SQL文本封装到TextSqlNode
if (textSqlNode.isDynamic()) {
sqlNodes.add(textSqlNode);
isDynamic = true;
} else {
// 如果不包含${},那么将文本节点的SQL文本封装到StaticTextSqlNode
sqlNodes.add(new StaticTextSqlNode(sqlText));
}
} else if (node instanceof Element) {
// 如果是Element节点,那么需要获取节点名称
Element element = (Element) node;
String name = element.getName();
// TODO 此处使用策略模式去优化
// 根据名称创建对应Element的SqlNode对象,封装对应Element的节点数据,比如if标签对应IfSqlNode
if ("if".equals(name)) {
// 获取判断表达式
String test = element.attributeValue("test");
// 递归给if标签动态解析,获取它的子标签SqlNode节点集合
MixedSqlNode mixedSqlNode = parseDynamicTags(element);
IfSqlNode ifSqlNode = new IfSqlNode(test, mixedSqlNode);
sqlNodes.add(ifSqlNode);
} else if ("where".equals(name)) {
// TODO 。。。。
}
}
}
return new MixedSqlNode(sqlNodes);
}
private Class<?> resolveType(String parameterType) {
try {
Class<?> clazz = Class.forName(parameterType);
return clazz;
} catch (ClassNotFoundException e) {
Java码库
- 粉丝: 2209
- 资源: 6175
最新资源
- (源码)基于C语言的系统服务框架.zip
- (源码)基于Spring MVC和MyBatis的选课管理系统.zip
- (源码)基于ArcEngine的GIS数据处理系统.zip
- (源码)基于JavaFX和MySQL的医院挂号管理系统.zip
- (源码)基于IdentityServer4和Finbuckle.MultiTenant的多租户身份认证系统.zip
- (源码)基于Spring Boot和Vue3+ElementPlus的后台管理系统.zip
- (源码)基于C++和Qt框架的dearoot配置管理系统.zip
- (源码)基于 .NET 和 EasyHook 的虚拟文件系统.zip
- (源码)基于Python的金融文档智能分析系统.zip
- (源码)基于Java的医药管理系统.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈