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://xiahunao.cn/news/3031109.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

org.hsqldb.jdbcDriver 类,导致 ClassNotFoundException 异常如何解决?

确保JDBC驱动包存在&#xff1a;检查系统是否已经安装了HSQLDB JDBC驱动。如果没有安装或驱动没有正确放置在类路径中&#xff0c;需要下载并添加它。你可以从 HSQLDB官网 下载JDBC驱动包。 添加JDBC驱动到类路径&#xff1a;将下载的HSQLDB JDBC驱动&#xff08;通常是一个JA…

基于STM32F401RET6智能锁项目(使用库函数点灯、按键)

点灯硬件原理图 1、首先&#xff0c;我们查看一下原理图&#xff0c;找到相对应的GPIO口 LED_R低电平导通&#xff0c;LED4亮&#xff0c;所以LED_R的GPIO口需要配置一个低电平才能亮&#xff1b; LED_G低电平导通&#xff0c;LED3亮&#xff0c;所以LED_R的GPIO口需要配置一…

解决常见的Android问题

常见问题&#xff1a; 1、查杀&#xff1a; 查杀一般分为两个方向一种是内存不足的查杀&#xff0c;一种的是因为温度限频查杀&#xff0c;统称为内存查杀&#xff0c;两个问题的分析思路不同 1、内存不足查杀&#xff1a; 主要是因为当用户出现后台运行多个APP或者是相机等…

AE 电源 DC 80 kW PulsarAccessory User ManualMay 20055703041-C说明数

AE 电源DC 80 kW Pulsar Accessory User ManualMay20055703041C说明

Android 12.0 TvSettings系统设置wifi连接密码框点击Enter键失去焦点

1.前言 在12.0的系统box产品开发中,在TvSettings中,在wifi连接的时候,在用遥控器输入wifi密码框的时候,会发现在按遥控器Enter键的时候, 发现EditText焦点失去了,导致输入法消失了,为了解决这个问题就需要拦截Enter键保证正常输入wifi密码,接下来就来实现这个功能 如图…

Ansible的安装与配置

Ansible的安装与配置 1. 安装ansible # ansible是epel源提供的&#xff0c;所以必须安装epel&#xff1a; [rootRocky9 ~]# yum -y install epel-release Last metadata expiration check: 0:01:53 ago on Tue 26 Dec 2023 10:05:34 PM CST. Dependencies resolved. Package …

iPhone苹果手机下载安装Skype苹果applestore怎么切换账号?

iPhone苹果手机下载安装Skype需要切换到可下载Skype应用的地区苹果账号&#xff0c;才能搜索找到并下载Skype到苹果手机。请按下面的教程切换苹果账号下载安装Skype到苹果手机。 *注&#xff1a;如果您没有可下载Skype应用的苹果账号&#xff0c;请打开「词令」关键词口令直达…

社交媒体数据恢复:飞书

飞书数据恢复过程包括以下几个步骤&#xff1a; 确认数据丢失&#xff1a;首先要确认数据是否真的丢失&#xff0c;有时候可能只是被隐藏或者误操作删除了。 检查回收站&#xff1a;飞书中删除的文件会默认保存在回收站中&#xff0c;用户可以通过进入回收站找到被删除的文件&…

简单的神经网络

一、softmax的基本概念 我们之前学过sigmoid、relu、tanh等等激活函数&#xff0c;今天我们来看一下softmax。 先简单回顾一些其他激活函数&#xff1a; Sigmoid激活函数&#xff1a;Sigmoid函数&#xff08;也称为Logistic函数&#xff09;是一种常见的激活函数&#xff0c…

【回溯算法】【Python实现】符号三角形问题

文章目录 [toc]问题描述回溯法时间复杂性Python实现 问题描述 下图是由 14 14 14个“ ”和 14 14 14个“ − - −”组成的符号三角形&#xff0c; 2 2 2个同号下面都是” “&#xff0c; 2 2 2个异号下面都是“ − - −” 在一般情况下&#xff0c;符号三角形的第一行有 n…

机器学习-L1正则/L2正则

机器学习-L1正则/L2正则 目录 1.L1正则 2.L2正则 3.结合 1.L1正则 L1正则是一种用来约束模型参数的技术&#xff0c;常用于机器学习和统计建模中&#xff0c;特别是在处理特征选择问题时非常有用。 想象一下&#xff0c;你在装备行囊准备去旅行&#xff0c;但你的行囊有一…

第五十八节 Java设计模式 - 适配器模式

Java设计模式 - 适配器模式 我们在现实生活中使用适配器很多。例如&#xff0c;我们使用存储卡适配器连接存储卡和计算机&#xff0c;因为计算机仅支持一种类型的存储卡&#xff0c;并且我们的卡与计算机不兼容。 适配器是两个不兼容实体之间的转换器。适配器模式是一种结构模…

Ubuntu搭建VsCode C++ 开发环境

Ubuntu搭建VsCode C 开发环境 安装VS Code 使用命令来安装VS Code&#xff1a;他会下载vscode的最新版本。 sudo snap install --classic code如果不使用命令 的方式 在官网下载vscode安装包&#xff08; 后缀为 .deb的包 &#xff09;之后&#xff08;可以选择版本 &#x…

YOLOv9独家原创改进: 特征融合创新 | 一种基于内容引导注意力(CGA)的混合融合 | IEEE TIP 2024 浙大

💡💡💡创新点:提出了一种基于内容引导注意力(CGA)的混合融合方案,将编码器部分的低级特征与相应的高级特征有效融合。 💡💡💡在多个数据集实现暴力涨点,适用于小目标,低对比度场景 💡💡💡如何跟YOLOv9结合:将backbone和neck的特征融合,改进结构图如下…

揭秘设计模式的魔法:打造高效、可维护的软件架构

设计模式是软件架构设计师的必修课&#xff0c;设计模式中蕴含的思想是架构设计师必须掌握的。毋庸置疑&#xff0c;良好的设计可以让系统更容易地被复用、被移植和维护&#xff0c;而如何快速进行良好的设计则离不开设计模式&#xff0c;尤其是面向对象设计和编程。 说到设计模…

用ps显示出淘宝裸眼3d立体画中的内容

淘宝前段时间在弄猜数字的游戏&#xff0c;其中有一题是3d立体画&#xff0c;如果我们把图片用ps处理一下&#xff0c;结果马上就出来了。打开原图&#xff0c;再复制进一个新图层&#xff0c;新图层混合模式选“差值”&#xff0c;左右移动新图层&#xff0c;就看到答案啦。 原…

Xilinx 千兆以太网TEMAC IP核用户接口信号

用户接口包括AX14-Stream发送接口和AX14-Stream接收接口&#xff0c;下文简称为用户发送接口和用户接收接口&#xff0c;数据案度可以是易位或16位&#xff0c;其中&#xff0c;8位接口主要针对标准的以太网应用&#xff0c;它利用一个125MHz的时钟产生1Gbps的数据率;当使用16位…

Redis20种使用场景

Redis20种使用场景 1缓存2抽奖3Set实现点赞/收藏功能4排行榜5PV统计&#xff08;incr自增计数&#xff09;6UV统计&#xff08;HeyperLogLog&#xff09;7去重&#xff08;BloomFiler&#xff09;8用户签到&#xff08;BitMap&#xff09;9GEO搜附近10简单限流11全局ID12简单分…

基于MWORKS 2024a的MIMO-OFDM 无线通信系统设计

一、引言 在终端设备和数据流量爆发式增长的今天&#xff0c;如何提升通信系统容量、能量效率和频谱利用率成为5G通信的关键问题之一。大规模天线阵列作为5G及B5G无线通信关键技术通过把原有发送端天线数量提升一个或多个数量级&#xff0c;实现波束聚集、控制波束转向&#x…

深入学习指针3

目录 前言 1.二级指针 2.指针数组 3.指针数组模拟二维数组 前言 Hello,小伙伴们我又来了&#xff0c;上期我们讲到了数组名的理解&#xff0c;指针与数组的关系等知识&#xff0c;那今天我们就继续深入到学习指针域数组的练联系&#xff0c;如果喜欢作者菌生产的内容还望不…