手搓MyBatis框架(原理讲解)
你在学完MyBatis框架后会不会觉得很神奇,为什么我改一个配置文件就可以让程序识别和执行不同的sql语句操作数据库?
SqlSessionFactoryBuilder,SqlSessionFactory和SqlSession对象到底是怎样执行的?
如果你有这些问题看就完事了
没错,现在要做的就是手搓mybatis框架底层,简易版还原mybatis框架的执行原理
一.分析
你是站在一个设计框架者的角度来和未来使用你框架的人来进行一个对接的
刚开始毫无头绪,我们需要找一段代码作为参考
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = factory.openSession();
这就是基本操作,通过SqlSessionFactoryBuilder对象的build方法,传进去配置文件的一个流来创建SqlSessionFactory对象。
然后通过SqlSessionFactory对象来创建SqlSession会话对象,最后通过SqlSession进行sql语句的调用和事务的管理
好吧,先估计一下大致有几个模块
1.SqlSessionFactoryBuilder模块,由于要传进去一个有关配置文件的流,所以这个模块里还要进行配置文件的解析 ,并把解析出来的东西
2.SqlSessionFactory模块,把SqlSessionFactoryBuilder来的模块进行封装和整合,毕竟这玩意一个环境一个,要经常使用的
3.SqlSession模块,直接对sql语句进行操作并进行事务的管理等
二.SqlSessionFactoryBuilder类的设计
首先你需要一个SqlSessionFactoryBuilder类,同时需要一个build方法来构建SqlSessionFactory对象。
考虑到未来使用框架的人会给你提供一个mybatis-config.xml文件,这里用一个最简单的mybatis-config.xml文件作为模板
<?xml version="1.0" encoding="UTF-8" ?><configuration><properties resource="jdbc.properties"/><environments default="dev"><environment id="dev"><transactionManager type="JDBC"/><dataSource type="UNPOOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/itcast"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><mapper resource="sqlMapper.xml"/></mappers></configuration>
1.Resources工具类的设计
dxbatis框架需要使用者提供一个配置文件路径,dxbatis框架把这个配置文件通过流返回,所以要提供一个方法类把该文件作为流返回,私有化构造方法,通过类加载器ClassLoader将mybatis-config.xml文件作为流返回
import java.io.InputStream;/*** dxbatis框架提供的一个工具类* 专门用来类路径中资源的加载** @author 丁小喜* @version 1.0*/public class Resources {private Resources() {}public static InputStream getResourceAsStream(String resource) {return ClassLoader.getSystemClassLoader().getResourceAsStream(resource);}
}
现在我们得到了配置文件流了,接下来就是在SqlSessionFactoryBuilder解析这个配置文件了
2.build方法的设计
解析xml文件我们需要两个依赖,dom4j依赖和jaxen依赖,在pom文件中引入这两个依赖
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j --><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version></dependency><!-- https://mvnrepository.com/artifact/jaxen/jaxen --><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version></dependency>
然后在build方法中进行解析
public SqlSessionFactory build(InputStream in) {SqlSessionFactory factory = null;try {//解析godbatis-config.xml文件SAXReader reader = new SAXReader();//把文件读进来Document document = reader.read(in);//获取environments标签节点Element environments = (Element) document.selectSingleNode("/configuration/environments");//获取default属性值String defaultId = environments.attributeValue("default");//获取默认使用的environment节点Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultId + "']");//获取transactionManager节点Element transactionElt = (Element) environment.element("transactionManager");//获取dataSource节点Element dataSourceElt = (Element) environment.element("dataSource");} catch (Exception e) {e.printStackTrace();}return factory;}
这里对解析xml文件进行一点简单的介绍:
dom4j解析
先创建一个SAXReader对象
再ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");获取流
然后调用SAXReader对象的read方法获取document文档对象
Element 节点对象
document.getRootElement()获取根节点
document.selectSingleNode(xpath)再强转成Element就是获取指定xpath的节点
element.attributeValue("default")获取属性
element.element("transactionManager")获取子节点
element.elements()获取所有子节点
element.getTextTrim()获取标签中的内容
注:配置文件可以找到servlet而注解找不到,看一下web.xml文件的metadata-complete是true就只允许配置文件,false配置文件和注解都可以
解析完配置文件我们发现我们得到了数据源dataSource节点,事务管理transactionManager节点 ,所以我们接下来需要做的是处理transactionManager节点和dataSource节点
3.sql语句集合的设计
咱不仅要解析mybatis-config.xml文件,咱想要执行sql语句,咱还必须得解析mapper映射文件才行,那什么容器来盛放这些sql语句才合适呢?
来想想mybatis框架是怎么做的,如何定位一个sql语句呢?
使用sqlId来定位一个sql标签的,sqlId又由namespace和标签id组成
所以我们自然而然的就想到Map集合,把sqlId作为key,把sql语句作为value
这是我们又想到sql语句标签不止有其中得sql语句,还有resultType属性,故我们还要设计一个存储sql标签的类MappedStatement。
这个集合就为
Map<String, MappedStatement> mappedStatements
4.MappedStatement类的设计
其实就是一个简单的pojo类,当标签时select时resultType的值为需要映射的pojo类型
而标签不为select时resultType属性为空
/*** 此类为一个pojo类* 用来封装mapper.xml映射文件中的一个sql标签* 如:select标签,insert标签等* 最后将其装入到一个mapper集合中作为解析mapper映射文件的结果** @author 丁小喜* @version 1.0*/public class MappedStatement {/*** 一个sql语句*/private String sql;/*** 如果此对象表示的标签为select* 表示他要封装的结果集* 其他标签此属性值为null*/private String resultType;public MappedStatement() {}public MappedStatement(String sql, String resultType) {this.sql = sql;this.resultType = resultType;}/*** 获取** @return sql*/public String getSql() {return sql;}/*** 设置** @param sql*/public void setSql(String sql) {this.sql = sql;}/*** 获取** @return resultType*/public String getResultType() {return resultType;}/*** 设置** @param resultType*/public void setResultType(String resultType) {this.resultType = resultType;}public String toString() {return "MappedStatement{sql = " + sql + ", resultType = " + resultType + "}";}
}
5.mapper映射文件的解析及sql语句集合的获取
直接获取mybatis-config.xml文件mapper标签
获取该文件的解析对象
获取namespace属性和标签的id属性放入集合的key
再通过getTextTrim方法获取到MappedStatement放入value并返回
private Map<String, MappedStatement> getMappedStatements(List<String> sqlMapperXMLPath) {Map<String, MappedStatement> mappedStatements = new HashMap<>();sqlMapperXMLPath.forEach(XMLPath -> {try {SAXReader reader = new SAXReader();Document document = reader.read(Resources.getResourceAsStream(XMLPath));System.out.println(XMLPath);Element mapperElt = (Element) document.selectSingleNode("mapper");String namespace = mapperElt.attributeValue("namespace");List<Element> taps = mapperElt.elements();taps.forEach(tap -> {String id = tap.attributeValue("id");String resultType = tap.attributeValue("resultType");String sql = tap.getTextTrim();MappedStatement mappedStatement = new MappedStatement(sql, resultType);String sqlId = namespace + '.' + id;mappedStatements.put(sqlId, mappedStatement);});} catch (Exception e) {e.printStackTrace();}});return mappedStatements;}
三.DateSource数据源实现类的设计
1.分析
因为dateSource节点有可能有UNPOOLED,POOLED,JUDI三个属性所以需要设计三个实现类实现javax.sql.DataSource接口,并实现其中的抽象方法。
Transaction进行事务管理时也需要获取连接对象故先进行此类的实现
2.UNPOOLED类的实现
因为驱动只用注册一次,所以就不用把driver作为属性了
只用在构造方法的时候注册以下驱动就行了
注:
@Override
public Connection getConnection() throws SQLException {
return this.getConnection(username,password);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
这两个方法最好都实现一下,第一次的时候我第一个getConnection()方法没有实现,最后测试的时候报了空指针异常,就是没有调用第二个getConnection()方法,直接返回了null
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*/public class UnPooledDateSource implements javax.sql.DataSource {/*** 数据库驱动属性*/private String url;private String username;private String password;public UnPooledDateSource() {}/*** 创建一个数据源对象** @param driver 数据库驱动* @param url 统一资源定位符* @param username 用户名* @param password 密码*/public UnPooledDateSource(String driver, String url, String username, String password) {try {Class.forName(driver);} catch (ClassNotFoundException e) {e.printStackTrace();}this.url = url;this.username = username;this.password = password;}@Overridepublic Connection getConnection() throws SQLException {return this.getConnection(username,password);}@Overridepublic Connection getConnection(String username, String password) throws SQLException {Connection connection = DriverManager.getConnection(url, username, password);return connection;}@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;}
}
四.Transaction接口及其实现类的设计
因为Transaction节点可能有JDBC和Managed两个值,我们接收dxbatis框架的使用者的值为两个,所以要设计一个 JDBCTransaction和一个ManagedTransaction的实现类来实现Transaction接口。最后要用多态的方法,向Transaction属性传值(是在SqlSessionFactory对象中)
1.Transaction接口的设计
你的事务管理器需要实现提交事务,回滚事务,关闭事务等功能,故设计了commit,rollback,close方法
import java.sql.Connection;/*** 事务管理器接口* 封装用来管理事务的抽象方法* 所有事务都应该遵守此规范* jdbc事务管理器和managed事务管理器都要实现此接口** @author 丁小喜* @version 1.0*/public interface Transaction {/*** 提交事务*/void commit();/*** 回滚事务*/void rollBack();/*** 关闭事务*/void close();}
2.JDBCTransaction实现类的实现
给JDBCTransaction添加一个属性connect用于直接调用对连接进行控制
这里面的connection没什么好说的直接调用
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;/*** JDBC事务管理器** @author 丁小喜* @version 1.0*/public class JDBCTransaction implements Transaction {/*** 数据源属性* 因为下面的方法中都需要数据源connection* 故在SqlSessionFactory类中不需要数据源了* 因为可以通过Transaction属性去获取数据源再写一个就冗余了*/private DataSource dataSource;/*** 自动提交属性* true为开启自动提交* false为关闭自动提交*/private boolean autoCommit;private Connection connection;/*** 创建事务管理器对象** @param dataSource 数据源* @param autoCommit 是否自动提交*/public JDBCTransaction(DataSource dataSource, boolean autoCommit) {this.dataSource = dataSource;this.autoCommit = autoCommit;}@Overridepublic void commit() {try {connection.setAutoCommit(this.autoCommit);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();}}}
3.build方法的完善
设计完了DateSource类和Transaction接口,现在可以把xml文件中的dateSource节点和transactionManager节点转换成对应的对象了
(1).dataSource节点的转换
先获取dataSource节点下的所有子节点也就是property节点,遍历这些节点,取出这些节点的name,再根据name获取这些节点的value把他们塞到一个集合里面。
再获取数据源类型,是哪个就创建对应的数据源对象,最后再返回这个数据源对象
ps:这里只实现了UnPooledDateSource,写别的会错的。
/*** 获取数据源对象** @param dataSourceElt 数据源节点* @return 数据源对象*/private DataSource getDataSource(Element dataSourceElt) {Map<String, String> map = new HashMap<>();DataSource dataSource = null;List<Element> propertys = dataSourceElt.elements("property");propertys.forEach(s -> {String name = s.attributeValue("name");String value = s.attributeValue("value");map.put(name, value);});String type = dataSourceElt.attributeValue("type").trim().toUpperCase();if (type.equals(Const.DATASOURCE_UN_POOLED)) {dataSource = new UnPooledDateSource(map.get("driver"), map.get("url"), map.get("username"), map.get("password"));System.out.println(dataSource);} else if (type.equals(Const.DATASOURCE_POOLED)) {dataSource = new PooledDataSource();} else if (type.equals(Const.DATASOURCE_JNDI)) {dataSource = new JNDIDataSource();} else {System.err.println("SqlSessionFactoryBuilder的getDataSource戳了");}return dataSource;}
(2).transactionManager节点的转换
这个就简单了,根据type属性创建对应的对象再返回就行了
/*** 获取事务管理器对象** @param transactionElt 事务管理器节点* @param dataSource 数据源对象* @return 事务管理器对象*/private Transaction getTransaction(Element transactionElt, DataSource dataSource) {Transaction transaction = null;String type = transactionElt.attributeValue("type").trim().toUpperCase();if (Const.TRANSACTION_JDBC.equals(type)) {transaction = new JDBCTransaction(dataSource, false);//事务默认不会自动提交} else if (Const.TRANSACTION_MANAGED.equals(type)) {transaction = new ManagedTransaction();} else {System.out.println("SqlSessionFactoryBuilder的getTransaction戳了");}return transaction;}
(3).最终build方法
public SqlSessionFactory build(InputStream in) {SqlSessionFactory factory = null;try {//解析godbatis-config.xml文件SAXReader reader = new SAXReader();//把文件读进来Document document = reader.read(in);//获取environments标签节点Element environments = (Element) document.selectSingleNode("/configuration/environments");//获取default属性值String defaultId = environments.attributeValue("default");//获取默认使用的environment节点Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultId + "']");//获取transactionManager节点Element transactionElt = (Element) environment.element("transactionManager");//获取dataSource节点Element dataSourceElt = (Element) environment.element("dataSource");//通过dataSource节点获取数据源DataSource dataSource = getDataSource(dataSourceElt);//通过transactionElt节点和dataSource数据源获取事务管理器Transaction transaction = getTransaction(transactionElt, dataSource);List<String> sqlMapperXMLPath = new ArrayList<>();List<Node> mapperElt = document.selectNodes("//mapper");mapperElt.forEach(s -> {Element mapperNode = (Element) s;String resource = mapperNode.attributeValue("resource");sqlMapperXMLPath.add(resource);});sqlMapperXMLPath.forEach(s-> System.out.println(s));Map<String, MappedStatement> mappedStatements = null;mappedStatements = getMappedStatements(sqlMapperXMLPath);factory = new SqlSessionFactory(transaction, mappedStatements);} catch (Exception e) {e.printStackTrace();}return factory;}
五.常量类Const的设计
常量类在项目设计中也是一种常用手段 ,可以增加代码可读性,常量便于管理
ps:还可以让你的代码看起来更高级(笑)
/*** 常量类** @author 丁小喜* @version 1.0*/
public class Const {public static final String DATASOURCE_UN_POOLED = "UNPOOLED";public static final String DATASOURCE_POOLED = "POOLED";public static final String DATASOURCE_JNDI = "JNDI";public static final String TRANSACTION_JDBC = "JDBC";public static final String TRANSACTION_MANAGED = "MANAGED";
}
六.SqlSessionFactory类的设计
到这就很简单了 ,明确SqlSessionFactory要干嘛
SqlSession sqlSession = factory.openSession();
要openSession创建SqlSession对象
那直接在openSession方法里new一个Session对象返回就完事了
SqlSession对象需要什么:
sql语句,事务管理器 ,连接
既然这样 ,就直接把 SqlSessionFactory在openSession方法中传给SqlSession,不管你open多少个Session,SqlSessionFactory都是一个
import java.util.Map;/*** 一个数据库对象对应一个SqlSessionFactory对象* 而SqlSessionFactory对象可以构建SqlSession对象(开启会话)* 一个SqlSessionFactory对象可以开启多个SqlSession对象** @author 丁小喜* @version 1.0*/public class SqlSessionFactory {/*** 创建SqlSessionFactory对象* @param transaction 事务管理器对象* @param mappedStatements sql语句对象集合*/public SqlSessionFactory(Transaction transaction, Map<String, MappedStatement> mappedStatements) {this.transaction = transaction;MappedStatements = mappedStatements;}/*** Transaction是一个事务管理器接口* 实现类有jdbcTransaction和managedTransaction* 通过配置文件的切换使事务管理器可以灵活切换,就解耦合了*/private Transaction transaction;/*** 这个属性表示的是mapper映射文件的所有sql语句的集合* key是能唯一标识sql语句的sqlId,其实就是namespace+sql标签的id* value是sql标签信息对象* 通过sqlId(key)可以唯一找到一个sql语句对象(value)*/private Map<String, MappedStatement> MappedStatements;/*** 返回一个SqlSession会话对象* @return SqlSession对象*/public SqlSession openSession(){SqlSession sqlSession = null;this.transaction.openConnection();//创建一个SqlSession对象,将SqlSessionFactory对象传进去//因为SqlSessionFactory对象需要执行sql语句所以需要connection和MappedStatements//又因为connection对象在transaction中,故直接将此对象传入其中;sqlSession = new SqlSession(this);return sqlSession;}public Transaction getTransaction() {return transaction;}public Map<String, MappedStatement> getMappedStatements() {return MappedStatements;}
}
七.SqlSession类的设计
终于到最后一步了
我们可以直面sql语句和jdbc了
先分析一波:框架使用者会怎么使用 SqlSession对象
其他不重要,SqlSession调用了insert方法,commit方法,close方法
当然,构造方法不能忘
咱们再在前面opensession的时候new过SqlSession对象传的是 SqlSessionFactory对象
故设计对应属性factory及其构造方法
private SqlSessionFactory factory;public SqlSession(SqlSessionFactory factory) {this.factory = factory;}
1.insert方法的设计
首先咱得先把要执行的sql语句找出来
框架使用者在传入的时候传了个sqlId,直接通过这个获取一个对应的sql对象
然后通过正则把框架中的sql语句换成jdbc可识别的语句
通过反射机制分别传值
最后就是jdbc经典代码,不多赘述了
public int insert(String sqlId, Object pojo) {int count = 0;try {Map<String, MappedStatement> mappedStatements = this.factory.getMappedStatements();//获取sql语句对象MappedStatement mappedStatement = mappedStatements.get(sqlId);//获取文件中未处理的sql语句String godSql = mappedStatement.getSql();//获取数据库连接对象Connection connection = this.factory.getTransaction().getConnection();String resultType = mappedStatement.getResultType();//将sql处理成PreparedStatement可以识别的的sql语句String sql = godSql.replaceAll("#\\{[a-z0-9A-Z_$]*}", "?");//获取PreparedStatementPreparedStatement ps = connection.prepareStatement(sql);//给第几个问号传什么值int fromIndex = 0;int index = 0;while (true) {int jIndex = godSql.indexOf('#', fromIndex);if (jIndex < 0) {break;}index++;System.out.println(index);int rightIndex = godSql.indexOf('}', fromIndex);String inner = godSql.substring(jIndex + 2, rightIndex).trim();String getMethod = "get" + inner.toUpperCase().charAt(0) + inner.substring(1);Method method = pojo.getClass().getDeclaredMethod(getMethod);Object retValue = method.invoke(pojo);ps.setString(index, retValue.toString());fromIndex = rightIndex + 1;}count = ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}return count;}
2.selectOne方法的设计
public Object selectOne(String sqlId,Object param){Object obj = null;try {//获取数据库连接对象Connection connection = this.factory.getTransaction().getConnection();//获取sql语句集对象MappedStatement mappedStatement = this.factory.getMappedStatements().get(sqlId);//获取框架中的sql语句String godSql = mappedStatement.getSql();//获取返回值类型String resultType = mappedStatement.getResultType();//获取jdbc可识别的sqlString sql = godSql.replaceAll("#\\{[a-zA-Z0-9_$]*}","?");//得到PreparedStatement对象PreparedStatement ps = connection.prepareStatement(sql);
// System.out.println(godSql);System.out.println(sql);//给占位符传值ps.setString(1,param.toString());ResultSet rs = ps.executeQuery();if(rs.next()){Class<?> pojo = Class.forName(resultType);obj = pojo.newInstance();//获取结果集源数据ResultSetMetaData rsmd = rs.getMetaData();int columnCount = rsmd.getColumnCount();//rsmd.getColumnName这玩意可能版本不一样这里是从1开始算的for (int i = 1; i <= columnCount; i++) {String columnName = rsmd.getColumnName(i);String setMethod = "set"+columnName.toUpperCase().charAt(0)+columnName.substring(1);Method declaredMethod = pojo.getDeclaredMethod(setMethod, String.class);declaredMethod.invoke(obj,rs.getString(columnName));}}} catch (Exception e) {e.printStackTrace();}return obj;}
3.事务方法的设计
调用factory属性获取Transaction中的方法执行就行了
/*** 提交事务*/public void commit() {this.factory.getTransaction().commit();}/*** 回滚事务*/public void rollBack() {this.factory.getTransaction().rollBack();}/*** 关闭事务*/public void close() {this.factory.getTransaction().close();}
最后SqlSession类是这样的
import java.lang.reflect.Method;
import java.sql.*;
import java.util.Map;/*** 专门负责执行sql语句的会话对象** @author 丁小喜* @version 1.0*/
public class SqlSession {private SqlSessionFactory factory;public SqlSession(SqlSessionFactory factory) {this.factory = factory;}public Object selectOne(String sqlId,Object param){Object obj = null;try {//获取数据库连接对象Connection connection = this.factory.getTransaction().getConnection();//获取sql语句集对象MappedStatement mappedStatement = this.factory.getMappedStatements().get(sqlId);//获取框架中的sql语句String godSql = mappedStatement.getSql();//获取返回值类型String resultType = mappedStatement.getResultType();//获取jdbc可识别的sqlString sql = godSql.replaceAll("#\\{[a-zA-Z0-9_$]*}","?");//得到PreparedStatement对象PreparedStatement ps = connection.prepareStatement(sql);
// System.out.println(godSql);System.out.println(sql);//给占位符传值ps.setString(1,param.toString());ResultSet rs = ps.executeQuery();if(rs.next()){Class<?> pojo = Class.forName(resultType);obj = pojo.newInstance();//获取结果集源数据ResultSetMetaData rsmd = rs.getMetaData();int columnCount = rsmd.getColumnCount();//rsmd.getColumnName这玩意可能版本不一样这里是从1开始算的for (int i = 1; i <= columnCount; i++) {String columnName = rsmd.getColumnName(i);String setMethod = "set"+columnName.toUpperCase().charAt(0)+columnName.substring(1);Method declaredMethod = pojo.getDeclaredMethod(setMethod, String.class);declaredMethod.invoke(obj,rs.getString(columnName));}}} catch (Exception e) {e.printStackTrace();}return obj;}public int insert(String sqlId, Object pojo) {int count = 0;try {Map<String, MappedStatement> mappedStatements = this.factory.getMappedStatements();//获取sql语句对象MappedStatement mappedStatement = mappedStatements.get(sqlId);//获取文件中未处理的sql语句String godSql = mappedStatement.getSql();//获取数据库连接对象Connection connection = this.factory.getTransaction().getConnection();String resultType = mappedStatement.getResultType();//将sql处理成PreparedStatement可以识别的的sql语句String sql = godSql.replaceAll("#\\{[a-z0-9A-Z_$]*}", "?");//获取PreparedStatementPreparedStatement ps = connection.prepareStatement(sql);//给第几个问号传什么值int fromIndex = 0;int index = 0;while (true) {int jIndex = godSql.indexOf('#', fromIndex);if (jIndex < 0) {break;}index++;System.out.println(index);int rightIndex = godSql.indexOf('}', fromIndex);String inner = godSql.substring(jIndex + 2, rightIndex).trim();String getMethod = "get" + inner.toUpperCase().charAt(0) + inner.substring(1);Method method = pojo.getClass().getDeclaredMethod(getMethod);Object retValue = method.invoke(pojo);ps.setString(index, retValue.toString());fromIndex = rightIndex + 1;}count = ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}return count;}/*** 提交事务*/public void commit() {this.factory.getTransaction().commit();}/*** 回滚事务*/public void rollBack() {this.factory.getTransaction().rollBack();}/*** 关闭事务*/public void close() {this.factory.getTransaction().close();}}
好了,dxbatis搓完了
文章写得我欲仙欲死的(笑)
最后感谢一下老杜的视频,大家要是看不懂的的话可以去参考一下老杜的mybatis视频,讲的清晰明了。
这个搓完真的有种穿起来的感觉,好像基础夯实了不少。对我的debug能力也是一种提升,不得不说哥们现在找错找的还挺快的,好了我要去吃饭了,这文章写了我一下午,也算是某种复习吧。
对你有帮助的话点点赞吧😀
相关文章:
手搓MyBatis框架(原理讲解)
你在学完MyBatis框架后会不会觉得很神奇,为什么我改一个配置文件就可以让程序识别和执行不同的sql语句操作数据库? SqlSessionFactoryBuilder,SqlSessionFactory和SqlSession对象到底是怎样执行的? 如果你有这些问题看就完事了 …...

FRC-EP系列--你的汽车数据一站式管家
FRC-EP系列产品主要面向汽车动力总成测试的客户,主要应用方向为残余总线仿真及网关。本文将详细介绍FRC-EP的产品特性和应用场景。 应用场景: 汽车电子生成研发过程中,需要对汽车各个控制器进行仿真测试,典型的测试对象有&#…...

【ARM Trace32(劳特巴赫) 使用介绍 3 - trace32 访问运行时的内存】
请阅读【ARM Coresight SoC-400/SoC-600 专栏导读】 文章目录 1.1 trace32 访问运行时的内存1.1.1 侵入式 运行时内存访问1.1.2 非侵入式运行时访问1.1.3 缓存一致性的非侵入式运行时访问 1.2 Trace32 侵入式和非侵入式 运行时访问1.2.1 侵入式访问1.2.2 非侵入式运行时访问 1…...

VirtualBox网络地址转换(NAT),宿主机无法访问虚拟机的问题
问题:NAT模式下,默认只能从内访问外面,而不能从外部访问里面,所以只能单向ping通,虚拟机的ip只是内部ip。 PS:桥接则是与主机公用网卡,有独立的外部ip。 解决:NAT模式可以通过配置 …...

【操作系统】考研真题攻克与重点知识点剖析 - 第 2 篇:进程与线程
前言 本文基础知识部分来自于b站:分享笔记的好人儿的思维导图与王道考研课程,感谢大佬的开源精神,习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析,本人技术…...
总结:利用原生JDK封装工具类,解析properties配置文件以及MF清单文件
总结:利用原生JDK封装工具类,解析properties配置文件以及MF清单文件 一背景描述:1.在不同的项目中,项目使用的开发框架都不一样,甚至是JDK原生开发模式。此时解析配置文件以及jar包中的清单文件,就只能利用…...

openGauss学习笔记-119 openGauss 数据库管理-设置数据库审计-设置文件权限安全策略
文章目录 openGauss学习笔记-119 openGauss 数据库管理-设置数据库审计-设置文件权限安全策略119.1 背景信息119.2 数据库程序目录及文件权限119.3 建议 openGauss学习笔记-119 openGauss 数据库管理-设置数据库审计-设置文件权限安全策略 119.1 背景信息 数据库在安装过程中…...

不可否认程序员的护城河已经越来越浅了
文章目录 那些在冲击程序员护城河低代码/无代码开发平台自动化测试和部署工具AI辅助开发工具在线学习和教育平台 面临冲击,程序员应该怎么做深入专业知识:不断学习全栈技能开发解决问题的能力建立人际网络管理和领导技能 推荐阅读 技术和应用的不断发展对…...

黑客技术-小白自学
前言 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域,都有攻与防…...

ZYNQ_project:key_beep
通过按键控制蜂鸣器工作。 模块框图: 时序图: 代码: /*1位按键消抖 */ module key_filter (input wire sys_clk ,input wire sys_rst_n ,input wire key_in ,output …...

css3文字环绕旋转
目录 固定数量文字环绕旋转不固定数量文字环绕旋转效果图 固定数量文字环绕旋转 <!-- 文字旋转测试 --> <template><div class"page"><div><div v-for"(item, index) in [...Array(20).keys()]" :key"index" style&…...

Linux 进程优先级 | 环境变量
目录 进程优先级 基本概念 认识优先级 PRI and NI NI值的范围 查看进程优先级 用top命令更改已存在进程的nice: 如何修改优先级 其他概念 环境变量 基本概念 常见环境变量 和环境变量相关的命令 环境变量的组织方式 通过代码如何获取环境变量 环境变量通…...

Nginx(五)
负载均衡 官网文档 Using nginx as HTTP load balancer nginx中实现反向代理的方式 HTTP:通过nginx配置反向代理到后端服务器,nginx将接收到的HTTP请求转发给后端服务器。使用 proxy_pass 命令 HTTPS:通过nginx配置反向代理到后端服务器&…...

永达理简析:利用保险的“财务规划”功能维持退休后生活水平
现代社会环境背景下,“自养自老”已经是一种未来养老趋势,很多人会为自己准备一份长期、比较周全的保障,这样财务规划不仅会分担子女的压力,也让自己有一个长远的保障。在各种财务储蓄工具中,商业保险占据着不可取代的…...

拓展认知边界:如何给大语言模型添加额外的知识
Integrating Knowledge in Language Models P.s.这篇文章大部分内容来自Stanford CS224N这门课Integrating Knowledge in Language Models这一节😁 为什么需要给语言模型添加额外的知识 1.语言模型会输出看似make sense但实际上不符合事实的内容 语言模型在生成…...
C语言assert断言
断言是对某种假设条件进行检查。 C语言中,断言被定义成宏的形式,原型在<assert.h>中。 #include <assert.h> void assert( int expression );assert将通过检查表达式expression的值来决定是否需要终止执行程序。 如果expression的值为假&am…...

开发模型(瀑布、螺旋、scrum) 和 测试模型(V、W)、增量和迭代、敏捷(思想)及敏捷开发 scrum
🧸欢迎来到dream_ready的博客,📜相信您对这篇博客也感兴趣o (ˉ▽ˉ;) 震惊!测试人员对BUG的全方位解析,测试的执行和BUG管理! 原来测试人员遇到BUG是这样返回给开发的!什么是BUG&am…...

[蓝桥杯复盘] 第 3 场双周赛20231111
[蓝桥杯复盘] 第 3 场双周赛20231111 总结深秋的苹果1. 题目描述2. 思路分析3. 代码实现 鲜花之海1. 题目描述2. 思路分析3. 代码实现 斐波拉契跳跃2. 思路分析3. 代码实现 星石传送阵2. 思路分析3. 代码实现 六、参考链接 总结 做了后4题。https://www.lanqiao.cn/oj-contes…...

浅析移动端车牌识别技术的工作原理及其过程
随着社会经济的发展与汽车的日益普及带来巨大的城市交通压力,在此背景下,智能交通系统成为解决这一问题的关键。而在提出发展无线智能交通系统后,作为智能交通的核心,车牌识别系统需要开始面对车牌识别移动化的现实需求。基于实现车牌识别移动化这一目标,一种基于Android移动终…...

计算机网络期末复习-Part4
1、UDP和TCP的比较 TCP提供可靠传输;UDP提供不可靠传输。TCP有连接;UDP无连接(减小时延)。TCP提供流量控制;UDP不提供流量控制。TCP提供拥塞控制;UDP不提供拥塞控制(传输快)。TCP提…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...