MyBatis——模拟MyBatis框架

一、dom4j 解析 XML 文件

在 dom4j 中,DOMReader SAXReader 是两种不同的 XML 解析器。

它们的主要区别在于解析 XML 的方式和所提供的功能:

  1. DOMReader

    • DOMReader 使用 DOM(Document Object Model)模型来表示整个 XML 文档,将整个 XML 文档加载到内存中,以树形结构的方式表示整个文档。

    • 优点:可以随机访问和修改文档中的任何部分,方便对文档进行增删改查操作。

    • 缺点:由于将整个文档加载到内存中,对于大型 XML 文档会占用较多的内存,可能导致性能问题。

  2. SAXReader

    • SAXReader 使用 SAX(Simple API for XML)解析器,采用事件驱动的方式逐行读取和解析 XML 文档,不需要将整个文档加载到内存中。

    • 优点:适合处理大型 XML 文档,因为不需要一次性加载整个文档,可以减少内存占用。

    • 缺点:相对于 DOM 模型,SAX 模型不支持随机访问和修改文档的能力,只能顺序读取文档内容并响应特定事件。

选择使用 DOMReader 还是 SAXReader 取决于具体的需求。

如果需要频繁地对文档进行修改或随机访问,适合使用 DOMReader

而如果处理大型文档或只需顺序读取文档内容,那么 SAXReader 是更好的选择。

 

  • 解析核心配置文件

// 创建 SAXReader 对象
SAXReader saxReader = new SAXReader();
// 通过 ClassLoader 加载 xml 文件
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
// 读 xml 文件,返回 document 对象,document 对象是文档对象,代表整个 xml 文件
Document document = saxReader.read(is);// 获取文档中的根标签
/*Element rootElement = document.getRootElement();String rootElementName = rootElement.getName();System.out.println("根结点:" + rootElementName);
*/// 获取 default 环境 id
// xpath 是做标签路径匹配的,能够快速定位 xml 中的元素
// 从根下找 configuration 标签,然后找 configuration 下的 environments 标签
String xpath = "/configuration/environments";
// Element 是 Node 的子类,方法更多,使用更便捷
Element environments = (Element) document.selectSingleNode(xpath);
// System.out.println(environments);
// 获取属性值
String defaultEnvironmentId = environments.attributeValue("default");
// System.out.println("默认环境id :" + defaultEnvironmentId);// 获取具体环境
xpath = "//configuration/environments/environment[@id='" + defaultEnvironmentId + "']";
Element environment = (Element) document.selectSingleNode(xpath);
// System.out.println(environment);// 获取 environment 下的 transactionManager 结点
// element - 获取孩子结点
Element transactionManager = environment.element("transactionManager");
String transactionManagerType = transactionManager.attributeValue("type");
System.out.println("transactionManagerType : " + transactionManagerType);// 获取 dataSource 结点
Element dataSource = environment.element("dataSource");
String dataSourceType = dataSource.attributeValue("type");
System.out.println("dataSourceType : " + dataSourceType);// 获取 dataSource 下的所有子节点
List<Element> propertyEles = dataSource.elements();
// 遍历
propertyEles.forEach(propertyEle -> {String name = propertyEle.attributeValue("name");String value = propertyEle.attributeValue("value");System.out.println(name + " : " + value);
});// 获取所有 mapper 标签
// 不想从根下开始,想从任意位置开始获取所有标签需要这样写
xpath = "//mapper";
List<Node> mappers = document.selectNodes(xpath);
// 遍历
mappers.forEach(mapper -> {Element mapperEle= (Element) mapper;String resource = mapperEle.attributeValue("resource");System.out.println(resource);
});

 

  • 解析 SqlMapper 文件 
SAXReader saxReader = new SAXReader();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("CarMapper.xml");
Document document = saxReader.read(is);// 获取 namespace
String xpath = "/mapper";
Element mapper = (Element) document.selectSingleNode(xpath);
String namespace = mapper.attributeValue("namespace");
System.out.println(namespace);// 获取 mapper 下的所有子节点
List<Element> elements = mapper.elements();
elements.forEach(element -> {String id = element.attributeValue("id");// 没有该属性的 Sql 语句则会返回一个 “null”String resultType = element.attributeValue("resultType");System.out.println("id : " + id + ",resultType : " + resultType);// 获取 Sql 语句(获取标签中的文本内容,去除前后空白)String sql = element.getTextTrim();System.out.println(sql);/*** MyBatis 封装了 JDBC,需要执行的是带 ? 的 SQL 语句,所以需要将以下 SQL 语句做转化* insert into t_car * values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})** insert into t_car values(null,?,?,?,?,?)*/String newSql = sql.replaceAll("#\\{[0-9A-Za-z_$]*}", "?");System.out.println(newSql);
}

 

二、手写 GodBatis 

Jaxen 是一个用于 Java 平台的开源 XPath 库,它提供了在 XML 文档中执行 XPath 查询的功能。

Jaxen 的目标是提供一个简单、易用且高效的方式来解析和查询 XML 文档,使开发人员能够轻松地使用 XPath 表达式来定位和提取 XML 文档中的数据。

一些 Jaxen 库的特点包括:

  • 支持标准的 XPath 语法:Jaxen 遵循标准的 XPath 语法规范,可以执行常见的 XPath 查询操作,如按路径查找节点、筛选节点、使用谓词等。

  • 跨平台性:作为一个 Java 库,Jaxen 可以在不同的 Java 平台上运行,提供了对 XML 文档的跨平台查询能力。

  • 易于集成:Jaxen 提供了简洁的 API,使得开发人员可以轻松地将 XPath 功能集成到 Java 应用程序中。

  • 灵活性:Jaxen 支持不同类型的 XML 文档,如 DOM、SAX、JDOM 等,使得开发人员可以根据需求选择合适的 XML 解析器来进行 XPath 查询。

总的来说,Jaxen 是一个方便、灵活且功能丰富的 Java XPath 库,适用于需要在 Java 应用程序中对 XML 文档进行复杂查询和处理的场景。

 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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"><modelVersion>4.0.0</modelVersion><groupId>org.god.ibatis</groupId><artifactId>godBatis</artifactId><version>1.0</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><!--dom4j--><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.1</version></dependency><!--jexen--><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.2.0</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency></dependencies></project>

Resources工具类  

package org.god.ibatis.utils;import java.io.InputStream;/*** 工具类:加载类路径中资源** @author 秋玄* @version 1.0* @package org.god.ibatis.utils* @date 2022-09-26-07:50* @since 1.0*/
public class Resources {/*** 工具类构造方法都是私有的* 因为工具类中的方法都是静态的,不需要创建对象就可以调用*/private Resources() {}/*** 从类路径中加载资源* @param resource  类路径中的资源文件* @return          指向资源文件的输入流*/public static InputStream getResourceAsStream(String resource){return ClassLoader.getSystemClassLoader().getResourceAsStream(resource);}
}

SqlSessioniFactoryBuilder  

package org.god.ibatis.core;import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.god.ibatis.utils.Resources;import javax.sql.DataSource;
import java.io.InputStream;
import java.util.*;/*** SqlSessionFactory 构建器对象* 通过 SqlSessioniFactoryBuilder 的 build 方法解析* godbatis-config.xml 文件,创建 SqlSessionFactory 对象** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-07:55* @since 1.0*/
public class SqlSessioniFactoryBuilder {/*** 无参数构造方法*/public SqlSessioniFactoryBuilder() {}/*** 解析 godbatis-config.xml 文件,构建 SqlSesionFactory 对象* @param in    指向 godbatis-config.xml 文件的一个输入流* @return      SqlSesionFactory 对象*/public SqlSessionFactory build(InputStream in){SqlSessionFactory factory = null;try {// 解析核心配置文件 godbatis-config.xmlSAXReader reader = new SAXReader();Document document = reader.read(in);Element environments = (Element) document.selectSingleNode("/configuration/environments");String dafaultId = environments.attributeValue("default");Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='"+ dafaultId +"']");Element transactionEle = environment.element("transactionManager");Element dataSourceEle = environment.element("dataSource");List<String> sqlMapperXMLPathList = new ArrayList<>();// “//mapper” -- 获取整个配置文件中的 mapperList<Node> nodes = document.selectNodes("//mapper");nodes.forEach(node -> {Element mapper = (Element) node;String resource = mapper.attributeValue("resource");sqlMapperXMLPathList.add(resource);});// 获取数据源DataSource dataSource = getDataSource(dataSourceEle);// 获取事务管理器Transaction transaction = getTransaction(transactionEle,dataSource);// 获取 mappedStatementsMap<String,MappedStatement> mappedStatements = getMappedStatements(sqlMapperXMLPathList);// 构建 SqlSessionFactory 对象factory = new SqlSessionFactory(transaction,mappedStatements);} catch (Exception e) {e.printStackTrace();}return factory;}/*** 解析所有的 SqlMapper 文件,构建 Map 集合* @param sqlMapperXMLPathList* @return*/private Map<String, MappedStatement> getMappedStatements(List<String> sqlMapperXMLPathList) {Map<String,MappedStatement> mappedStatements = new HashMap<>();sqlMapperXMLPathList.forEach(sqlMapperXMLPath -> {try {SAXReader reader = new SAXReader();Document document = reader.read(Resources.getResourceAsStream(sqlMapperXMLPath));Element mapper  = (Element) document.selectSingleNode("mapper");String namespace = mapper.attributeValue("namespace");List<Element> elements = mapper.elements();elements.forEach(element -> {String id = element.attributeValue("id");String sqlId = namespace + "." + id;String resultType = element.attributeValue("resultType");String sql = element.getTextTrim();MappedStatement mappedStatement = new MappedStatement(sql, resultType);mappedStatements.put(sqlId,mappedStatement);});} catch (Exception e) {e.printStackTrace();}});return mappedStatements;}/*** 获取事务管理器* @param transactionEle    事务管理器标签元素* @param dataSource        数据源对象* @return                  事务管理器标签元素对应的事务管理器对象*/private Transaction getTransaction(Element transactionEle, DataSource dataSource) {Transaction transaction = null;// type 可能的值:JDBC MANAGEDString type = transactionEle.attributeValue("type").trim().toUpperCase();switch (type){case Const.JDBC_TRANSACTION:/* false:默认开启事务,需要手动提交 */transaction = new JdbcTransaction(dataSource,false);break;case Const.MANAGED_TRANSACTION:transaction = new ManagedTransaction();break;}return transaction;}/*** 获取数据源* @param dataSourceEle 数据源标签元素* @return              数据源标签元素对应的数据源对象*/private DataSource getDataSource(Element dataSourceEle) {Map<String,String> map = new HashMap<>();// 获取所有 propertyList<Element> propertys = dataSourceEle.elements("property");propertys.forEach(propertyEle -> {String name = propertyEle.attributeValue("name");String value = propertyEle.attributeValue("value");map.put(name,value);});DataSource dataSource = null;// type 可能的值:UNPOOLED POOLED JNDIString type = dataSourceEle.attributeValue("type").trim().toUpperCase();switch (type){case Const.UN_POOLED_DATASOURCE:dataSource = new UnPooledDataSource(map.get("driver"),map.get("url"),map.get("username"), map.get("password"));break;case Const.POOLED_DATASOURCE:dataSource = new PooledDataSource();break;case Const.JNDI_DATASOURCE:dataSource = new JndiDataSource();break;}return dataSource;}
}

SqlSessionFactory  

package org.god.ibatis.core;import java.util.Map;/*** 一个数据库一般对应一个 SqlSessionFactory 对象* 通过 SqlSessionFactory 对象可以获取 SqlSession 对象(开启会话)* 一个 SqlSessionFactory 对象可以开启多个 SqlSession 会话** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-08:01* @since 1.0*/
public class SqlSessionFactory {/*** 事务管理器* 事务管理器是可以灵活切换的* SqlSessionFactory 类中的事务管理器应该是面向接口编程*/private  Transaction transaction;/*** 存放 SQL 语句的 Map 集合* key 是 sqlId* value 是对应的 SQL 标签信息对象*/private Map<String,MappedStatement> mappedStatements;public Transaction getTransaction() {return transaction;}public void setTransaction(Transaction transaction) {this.transaction = transaction;}public Map<String, MappedStatement> getMappedStatements() {return mappedStatements;}public void setMappedStatements(Map<String, MappedStatement> mappedStatements) {this.mappedStatements = mappedStatements;}/*** 获取 SqlSession 对象* @return SqlSession 对象*/public SqlSession openSession(){// 开启连接transaction.openConnection();// 创建 SqlSession 对象/*this 指的是当前的 SqlSessionFactory 对象它包含了 transaction 和 mappedStatements同时对外提供了 getter 方法*/SqlSession sqlSession = new SqlSession(this);return sqlSession;}public SqlSessionFactory(Transaction transaction, Map<String, MappedStatement> mappedStatements) {this.transaction = transaction;this.mappedStatements = mappedStatements;}public SqlSessionFactory() {}
}

全局常量  

package org.god.ibatis.core;/*** 整个框架的常量类** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-27-07:15* @since 1.0*/
public class Const {public static final String UN_POOLED_DATASOURCE = "UNPOOLED";public static final String POOLED_DATASOURCE = "POOLED";public static final String JNDI_DATASOURCE = "JNDI";public static final String JDBC_TRANSACTION = "JDBC";public static final String MANAGED_TRANSACTION = "MANAGED";
}

Transaction 接口  

package org.god.ibatis.core;import java.sql.Connection;/*** 事务管理器接口* 所有的事务管理器都应该遵循此规范*      JDBC 事务管理器*      MANAGED 事务管理器* 事务管理器:提供控制事务的方法** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-17:58* @since 1.0*/
public interface Transaction {/*** 提交事务*/void commit();/*** 回滚事务*/void rollback();/*** 关闭事务*/void close();/*** 开启数据库连接*/void openConnection();/*** 获取数据库连接对象*/Connection getConnection();
}

Transaction 接口实现类  

package org.god.ibatis.core;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;/*** JDBC 事务管理器** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-18:03* @since 1.0*/
public class JdbcTransaction  implements Transaction{/*** 数据源属性* 所有的数据源都要实现 JDK 自带的规范:javax.sql.DataSource*/private DataSource dataSource;/*** 自动提交标志*      true:自动提交*      false:不自动提交*/private boolean autoCommit;/*** 连接对象*/private Connection connection;@Overridepublic Connection getConnection() {return connection;}/*** 创建事务管理器对象* @param dataSource* @param autoCommit*/public JdbcTransaction(DataSource dataSource, boolean autoCommit) {this.dataSource = dataSource;this.autoCommit = autoCommit;}@Overridepublic void commit() {try {connection.commit();} catch (SQLException e) {e.printStackTrace();}}@Overridepublic void rollback() {try {connection.rollback();} catch (SQLException e) {e.printStackTrace();}}@Overridepublic void close() {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}@Overridepublic void openConnection(){if (connection == null) {try {connection = dataSource.getConnection();// 开启事务connection.setAutoCommit(autoCommit);} catch (SQLException e) {e.printStackTrace();}}}
}
package org.god.ibatis.core;import java.sql.Connection;/*** MANAGED 事务管理器(不实现)** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-18:04* @since 1.0*/
public class ManagedTransaction implements Transaction{@Overridepublic void commit() {}@Overridepublic void rollback() {}@Overridepublic void close() {}@Overridepublic void openConnection() {}@Overridepublic Connection getConnection() {return null;}
}

数据源  

package org.god.ibatis.core;import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;/*** 数据源实现类 UNPOOLED* 不使用连接池,每次都新建 Connection 对象** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-18:27* @since 1.0*/
public class UnPooledDataSource implements DataSource {private String url;private String username;private String password;/*** 创建一个数据源对象* @param driver* @param url* @param username* @param password*/public UnPooledDataSource(String driver, String url, String username, String password) {try {// 直接注册驱动Class.forName(driver);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}this.url = url;this.username = username;this.password = password;}@Overridepublic Connection getConnection() throws SQLException {return DriverManager.getConnection(url,username,password);}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}
}
package org.god.ibatis.core;import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;/*** 数据源实现类:POOLED* 使用 godbatis 框架内置的数据库连接池来获取 Connection 对象(不实现)** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-18:28* @since 1.0*/
public class PooledDataSource implements DataSource {/*** 从数据连接池中获取 Connection 对象* 这个数据库连接池 godbatis 框架可以自己写一个连接池* @return* @throws SQLException*/@Overridepublic Connection getConnection() throws SQLException {return null;}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}
}
package org.god.ibatis.core;import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;/*** 数据源的实现:JNDI* 使用第三方的数据库连接池获取 Connection 对象(不实现)** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-18:28* @since 1.0*/
public class JndiDataSource implements DataSource {@Overridepublic Connection getConnection() throws SQLException {return null;}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}
}

封装 SQL 标签

package org.god.ibatis.core;/*** 普通 Java 类,用于封装一个 SQL 标签* 一个 MappedStatement 对象对应一个 SQL 标签** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-26-08:21* @since 1.0*/
public class MappedStatement {/*** sql 语句*/private String sql;/*** 要封装的结果集类型* 当 sql 语句是 select 语句时 resultType 才有值* 其他情况都是 null*/private String resultType;@Overridepublic String toString() {return "MappedStatement{" +"sql='" + sql + '\'' +", resultType='" + resultType + '\'' +'}';}public String getSql() {return sql;}public void setSql(String sql) {this.sql = sql;}public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;}public MappedStatement(String sql, String resultType) {this.sql = sql;this.resultType = resultType;}public MappedStatement() {}
}

执行 SQL 语句  

package org.god.ibatis.core;import java.lang.reflect.Method;
import java.sql.*;/*** 执行 SQL 语句的会话对象** @author 秋玄* @version 1.0* @package org.god.ibatis.core* @date 2022-09-27-08:29* @since 1.0*/
public class SqlSession {private SqlSessionFactory factory;public SqlSession(SqlSessionFactory factory) {this.factory = factory;}/*** 执行 insert 语句,向数据库表中插入记录* @param id        sql 语句的 id* @param pojo      插入的数据* @return          插入记录的数量*/public int insert(String id,Object pojo){int count = 0;try {Connection connection = factory.getTransaction().getConnection();// insert into t_user values(#{id},#{name},#{age});String godBatisSql = factory.getMappedStatements().get(id).getSql();String sql = godBatisSql.replaceAll("#\\{[0-9A-Za-z_$]*}","?");PreparedStatement ps = connection.prepareStatement(sql);// 给占位符传值(局限性:这里都是 setString,所以要求数据库表中的字段类型都是 varchar 类型)// 将 pojo 的属性与占位符对应// 获取占位符的数量int fromIndex = 0;// 问号下标int index = 1;while (true){// # 的下标int jingIndex = godBatisSql.indexOf("#",fromIndex);// 找不到 # 结束循环if(jingIndex < 0){break;}// } 的下标,# 与 } 中间的字符串包含了一个属性名int youKuoHaoIndex = godBatisSql.indexOf("}",fromIndex);String propertyName = godBatisSql.substring(jingIndex + 2,youKuoHaoIndex).trim();fromIndex = youKuoHaoIndex + 1;String getter = "get" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);Method getMethod = pojo.getClass().getDeclaredMethod(getter);Object propertyValue = getMethod.invoke(pojo);ps.setString(index,propertyValue.toString());index++;}count = ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}return count;}/*** 执行查询语句,返回一个对象* 只适合返回一条记录的 sql 语句* @param id* @param param* @return*/public Object selectOne(String id,Object param){Object obj = null;try {Connection connection = factory.getTransaction().getConnection();String giodBatisSql = factory.getMappedStatements().get(id).getSql();String sql = giodBatisSql.replaceAll("#\\{[0-9A-Za-z_$]*}","?");PreparedStatement ps = connection.prepareStatement(sql);// 这里只对一个占位符的情况做处理ps.setString(1,param.toString());ResultSet resultSet = ps.executeQuery();String resultType = factory.getMappedStatements().get(id).getResultType();// 从结果集取数据封装对象if (resultSet.next()) {// 获取 resultType 的 classClass<?> clazz = Class.forName(resultType);// 调用无参数构造方法创建对象obj = clazz.newInstance();// 给属性赋值// 关键:将查询结果的列名作为属性名ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();for (int i = 1; i < columnCount + 1; i++) {String propertyName = metaData.getColumnName(i);// 拼接方法名String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);// 获取 set 方法Method setMethod = clazz.getDeclaredMethod(setMethodName, String.class);// 调用 set 方法给对象 obj 赋值setMethod.invoke(obj,resultSet.getString(propertyName));}}} catch (Exception e) {e.printStackTrace();}return obj;}/*** 提交事务*/public void commint(){factory.getTransaction().commit();}/*** 回滚事务*/public void rollback(){factory.getTransaction().rollback();}/*** 关闭事务*/public void close(){factory.getTransaction().close();}
}

打包到本地仓库  

使用 GodBatis 框架  

<dependency><groupId>org.god.ibatis</groupId><artifactId>godBatis</artifactId><version>1.0</version>
</dependency>

三、思路 

Mybatis 核心流程:

(采用对 MyBatis 框架使用的过程逆推的方式)

首先需要创建一个 SqlSessionFactoryBuilder 对象

  • 通过 SqlSessionFactoryBuilder 对象的 build 方法解析核心配置文件,构建 SqlSessionFactory 对象(需要一个 InputStream 流对象指向核心配置文件)

  • 封装一个工具类 Resources 用于加载类路径中的资源

    • 其中的方法都是静态的,不需要对象就可以调用,所以工具类构造方法一般私有化,避免创建对象

  • 解析核心配置文件,创建具体的事务管理器对象、数据源对象以及存放 SQL 语句的 Map 集合。调用 SqlSessionFactory 的构造方法,传入事务管理器、Map 集合创建 SqlSessionFactory 对象

构建 SqlSessionFactory 对象

  • 一个数据库一般对应一个 SqlSessionFactory 对象

  • 通过 SqlSessionFactory 对象可以获取 SqlSession 对象(开启会话)

  • 一个 SqlSessionFactory 对象可以开启多个 SqlSession 会话

属性分析

在 SqlSessionFactoryBuilder 对象的 build 方法中需要构建一个 SqlSessionFactory 对象,并对其各个属性赋值,再将其作为返回值返回。

这个 SqlSessionFactory 对象需要封装的数据应该是 “事务管理器”、“存放 SQL 语句的 Map 集合”(使用 MappedStatement 类封装,具有 sql 及 resultType 两个属性)

由于事务管理器具有数据源属性,所以 SqlSessionFactory 对象可以通过其事务管理器属性获取数据源,故自身不需要数据源属性了,避免冗余

事务管理器

由于用户需要的事务管理器可能是 JDBC 事务管理器、MANAGED 事务管理器,所以这里采用面向接口编程的思想,抽取事务管理器接口,然后各种具体的事务管理器再对其方法做具体实现;使用时根据用户设置的属性值判断再创建具体的子类对象

子类需要实现的方法有:

  • 提交事务

  • 回滚事务

  • 关闭事务

  • 及其他方法(此处暂时未知,后续再做补充)

  • 开启数据库连接(后续添加的)

  • 获取数据库链接对象(后续添加的)

子类在具体实现接口方法时,要实现对事务的控制,则需要调用 Connectioin 对象的方法,而 Connection 对象需要通过数据源创建,所以 Transaction 对象需要有一个数据源的属性(此时因为事务管理器对象包含了数据源对象,所以 SqlSessionFactory 不需要有数据源属性)

为了保证执行事务的是同一个连接对象,所以给事务管理器对象添加一个 Connection 属性,并添加 openConnection、getConnection 方法,获取数据源中的连接对象

数据源

数据源与事务管理器类似,采用面向接口编程思想,实现 javax.sql.DataSource 接口,实现类的动态创建

在数据源对象中完成注册驱动、获取 Connection 连接对象,同时设置一个是否自动提交事务的标记

注册驱动只需要注册一次即可,所以在构造方法中完成

Connection 则在每一个调用 getConnection 方法时创建一个(UNPOOLED,不使用数据库连接池的实现)

封装 SqlSession 对象,完成配置文件中 SQL 语句的解析与执行

一  叶  知  秋,奥  妙  玄  心

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/696395.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国内使用 CloudFlare 避坑指南

最近明月收到了不少新手使用 CloudFlare 的求助,发现很多首次使用 CloudFlare 的甚至包括已经在使用 CloudFlare 的站长们对 CloudFlare 的使用有很多的误区,再加上国内简中互联网上有关 CloudFlare 的教程良莠不齐,更是加深了新手使用 CloudFlare 入坑的概率,让一些别有用…

基于51单片机的冰箱控制系统设计( proteus仿真+程序+设计报告+原理图+讲解视频)

基于51单片机冰箱控制系统设计( proteus仿真程序设计报告原理图讲解视频&#xff09; 基于51单片机冰箱控制系统设计 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真4. 程序代码5. 设计报告6. 原理图7. 设计资料内容清单&&下载链接资料下载链接&#xff1a; …

内容与图像一对多问题解决

场景复现 分析&#xff1a; 其实这是两给表&#xff0c;一个内容表&#xff0c;一个图片表&#xff0c;一对多的关系。 解决思路: 1. 先上传图片拿到图片的List集合ids&#xff0c;返回值是集合的ids&#xff0c;给到前端 2. 再添加内容表的数据生成了id&#xff0c;遍历查…

GPT搜索引擎原型曝光!

OpenAI发布会前一天&#xff0c;员工集体发疯中……上演大型套娃行为艺术。 A&#xff1a;我为B的兴奋感到兴奋&#xff1b;B&#xff1a;我为C的兴奋感到兴奋……Z&#xff1a;我为这些升级感到兴奋 与此同时还有小动作不断&#xff0c;比如现在GPT-4的文字描述已不再是“最先…

怎么ai拍照搜题?让你的学习之路更平坦!

怎么ai拍照搜题&#xff1f;在数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;的广泛应用已经渗透到我们生活的方方面面&#xff0c;其中AI拍照搜题功能更是受到了广大学子的热烈欢迎。这一功能通过拍照上传题目&#xff0c;利用AI技术快速识别并分析题目内容&#…

Mysql数据类型设计思考

一、Mysql数据类型设计规范 1.1 选择更小的数据类型 一般情况下&#xff0c;在满足存储要求的基础上&#xff0c;尽量选择小的存储类型。例如&#xff1a;存储0~200&#xff0c;tinyint和bigint都可以存储&#xff0c;那么选择tinyint。原因&#xff1a;越小的数据类型运算速…

Java后端的接口参数两个interger 类型情况解决

get请求 &#xff0c;对应请求头 RequestParm, post请求&#xff0c; 对应请求体 &#xff0c;RequestBody 两个Integer参数情况 GetMapping("/insertStaffClass")public R<Void> insertStaffClass(RequestParam Integer staffId,RequestParam List<Integ…

栈和队列的实现

目录 1.栈 1.1栈的概念以及结构&#xff1a; 1.2栈的实现 栈的初始化&#xff1a; 栈的销毁&#xff1a; 入栈&#xff1a; 获取栈顶数据&#xff1a; 判空&#xff1a; 获取元素的个数&#xff1a; 2.队列 2.1队列的概念及其结构 2.2队列的实现 队列的初始化 队…

DML之操作数据表

1. 插入数据 (1). 前言 前文我们实现了如果创建表&#xff0c;接下来我们将学习如何向数据表中插入数据.插入有两种方式. (2). 方式1 : 情况1 : 使用该语法一次只能向表中插入一条记录.为表中的任意字段按默认的顺序插入数据.值列表中需要为表的每一个字段指定值.并且值…

Android Studio开发之路(九)创建android library以及生成aar文件

一、需求 我做了一个camerax相机opencv图像处理图片上传服务器功能的android应用&#xff0c;应客户需求要将其改成一个SDK&#xff0c;由客户加到他们自己的app里边。 于是&#xff0c;我需要制作一个library&#xff0c;打包成aar文件&#xff08;jar:只有代码&#xff0c;没…

如何在CentOS7本地搭建ONLYOFFICE办公套件结合内网穿透实现公网访问

文章目录 1. 安装Docker2. 本地安装部署ONLYOFFICE3. 安装cpolar内网穿透4. 固定OnlyOffice公网地址 本篇文章讲解如何使用Docker在本地服务器上安装ONLYOFFICE&#xff0c;并结合cpolar内网穿透实现公网访问。 Community Edition允许您在本地服务器上安装ONLYOFFICE文档&…

C++青少年简明教程:C++数据类型

C青少年简明教程&#xff1a;C数据类型 数据类型定义了变量可以存储哪些类型的数据&#xff0c;以及对这些数据可以进行哪些操作。C提供了丰富的数据类型供开发者使用。 下面是 C 中常见的数据类型&#xff1a; ★整型&#xff08;int&#xff09;&#xff1a;整数类型的数据…