手搓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能力也是一种提升,不得不说哥们现在找错找的还挺快的,好了我要去吃饭了,这文章写了我一下午,也算是某种复习吧。

 

对你有帮助的话点点赞吧😀

 

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

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

相关文章

Python语法基础(变量 注释 数据类型 输入与输出 运算符 缩进)

目录 变量变量命名规则变量的类型变量的创建变量的作用域 注释的方法数据类型对象和引用的概念Number(数字)数据转换 输入与输出输入函数输出函数输出函数的end参数输出格式多行语句 运算符算术运算符赋值运算符三目运算符运算符的优先级 缩进缩进格式注意事项层级嵌套 变量 标…

(论文阅读24/100)Visual Tracking with Fully Convolutional Networks

文献阅读笔记&#xff08;sel - CNN&#xff09; 简介 题目 Visual Tracking with Fully Convolutional Networks 作者 Lijun Wang, Wanli Ouyang, Xiaogang Wang, and Huchuan Lu 原文链接 http://202.118.75.4/lu/Paper/ICCV2015/iccv15_lijun.pdf 【DeepLearning】…

Python--- lstrip()--删除字符串两边的空白字符、rstrip()--删除字符串左边的空白字符、strip()--删除字符串右边的空白字符

strip() 方法主要作用&#xff1a;删除字符串两边的空白字符&#xff08;如空格&#xff09; lstrip() 方法 left strip&#xff0c;作用&#xff1a;只删除字符串左边的空白字符 rstrip() 方法&#xff0c;作用&#xff1a;只删除字符串右边的空白字符 strip 英 /strɪp…

数据结构--栈

栈 一、顺序栈 #include<iostream>using namespace std;//使用std命名空间&#xff0c;省略后续代码中得到std::前缀#define OK 1//定义一个宏&#xff0c;使用OK表示操作成功 #define ERROR 0//定义一个宏。使用ERROR表示操作失败 #define OVERFLOW -2//定义宏&#x…

STM32H743XX/STM32H563XX芯片烧录一次后,再次上电无法烧录

近期在使用STM32H563ZIT6这款芯片在开发板上使用正常&#xff0c;烧录到自己打的板子就遇到了芯片烧录一次后&#xff0c;再次上电无法烧录的问题。 遇到问题需要从以下5点进行分析。 首先看下开发板的原理图 1.BOOT0需要拉高。 2.NRST脚在开发板上是悬空的。这里我建议大家…

html页面如果是rst文件编译产生的,应该如何嵌入js代码

如果您的 HTML 页面是从 ReStructuredText (RST) 文件编译生成的&#xff0c;您可以在 RST 文件中使用适当的标记和指令来嵌入 JavaScript 代码&#xff0c;以实现在页面上显示消息或执行其他交互式操作。以下是一些基本的步骤&#xff1a; 在 RST 文件中嵌入 JavaScript&#…

基于AI智能分析网关的智慧视频监控系统一站式解决方案

1、功能概述 TSINGEE智能分析网关EasyCVR智慧视频监控系统基于云-边-端一体化协同架构&#xff0c;可兼容多协议、多类型的设备接入&#xff0c;实现视频数据采集、海量视频汇聚与处理、按需调阅、全网分发、 告警消息推送、数据级联共享、AI智能分析接入等视频能力服务&#…

合并两个有序链表OJ

合并两个有序链表OJ 文章目录 合并两个有序链表OJ一、题目及要求二、思路分析三、代码实现 一、题目及要求 二、思路分析 其次&#xff0c;题目里说了新链表是通过拼接原来的结点形成的&#xff0c;所以说我们不需要开辟新的空间。 三、代码实现 if (list1 NULL) {return li…

Zotero详细功能补充!熟练使用!【进阶版,持续更新】

Zotero安装请参见文章Zotero安装 1.改变条目文件夹 如果直接选择条目直接进行移动&#xff0c;能移动成功&#xff0c;但是原来文件夹和目标文件夹都会存在&#xff0c;实际是复制&#xff01; 如果只想保留在一个文件夹里面&#xff0c;可以选中条目&#xff0c;右击-从分…

ARMday03(寄存器读写、栈、程序状态寄存器、软中断和异常、混合编程)

单寄存器内存读写指令 将一个寄存器中的数值写入到内存&#xff0c;或者从内存中读取数据放在某一个指定寄存器中 指令码和功能 1.向内存中写&#xff1a; str{条件码} 目标寄存器,[目标地址]&#xff1a;将目标寄存器的4字节数值写入到目标地址为首地址的空间中 strh{条件码…

最新AI系统ChatGPT源码+AI绘画系统源码+支持GPT4.0+Midjourney绘画+搭建部署教程+附源码

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

CSRF(跨站请求伪造)攻击演示

目录 CSRF(跨站请求伪造)攻击演示CSRF 是什么CSRF 演示项目代码CSRF 演示过程服务启动演示 CSRF(跨站请求伪造)攻击演示 CSRF 是什么 CSRF&#xff08;Cross-Site Request Forgery&#xff09;跨站请求伪造&#xff0c;是一种网络安全攻击&#xff0c;其目标是利用被攻击者在…

Ubuntu安装.Net SDK

参考微软文档&#xff1a;在 Linux 上安装 .NET选择自己对应系统版本的链接辅助即可。 我这里使用的是 Ubuntu 20.04&#xff0c;因为在这个版本.Net 在Ubuntu包源存储库中不可用&#xff0c;需要手动添加Microsoft包存储库。 使用 APT 进行安装可通过几个命令来完成。 安装 …

软件安全测试怎么做?如何确保软件授权安全

在数字化不断演进的今天&#xff0c;软件安全测试变得至关重要。它验证了软件是否容易受到网络攻击&#xff0c;并检验恶意或意外输入对操作的影响。安全测试的目标是保障系统和信息的安全性和可靠性&#xff0c;确保它们不接受未授权的输入。 一、安全测试准备 开发者必须认识…

【广州华锐互动】太空探索VR模拟仿真教学系统

随着科技的不断发展&#xff0c;人类对宇宙的探索欲望愈发强烈。火星作为距离地球最近的行星之一&#xff0c;自然成为了人类关注的焦点。近年来&#xff0c;火星探测取得了一系列重要成果&#xff0c;为人类了解火星提供了宝贵的信息。然而&#xff0c;实地考察火星仍然面临着…

认识计算机-JavaEE初阶

文章目录 一、计算机的发展史二、冯诺依曼体系&#xff08;Von Neumann Architecture&#xff09;三、CPU基本工作流程3.1 算术逻辑单元&#xff08;ALU&#xff09;3.2 寄存器&#xff08;Register)和内存&#xff08;RAM&#xff09;3.3 控制单元&#xff08;CU&#xff09;3…

万宾科技智能井盖监测仪器助力建设数字化城市

市政公共设施建设在近几年来发展迅速&#xff0c;市政设备的更新换代&#xff0c;资产管理等也成为其中的重要一项。在市政设施建设过程中&#xff0c;井盖也是不可忽视的&#xff0c;一方面&#xff0c;根据传统的管理井盖模式来讲&#xff0c;缺乏有效的远程监控管理方法和手…

【hcie-cloud】【3】华为云Stack规划设计之华为云Stack交付综述【上】

文章目录 前言华为云Stack交付综述交付流程华为云Stack交付流程华为云Stack安装部署流程 交付工具链华为云Stack交付工具链eDesigner - 让解决方案销售更智能eDesigner配置页面 - 基本信息eDesigner配置页面 - 服务及组网配置eDesigner配置页面 - 弹性云服务器/ECSeDesigner配置…

2024年的后端和Web开发趋势

目录 1 2 3 4 5 1 不断变化的数字创新格局可能让人感觉像是一场无情的竞赛。作为开发人员&#xff0c;你的痛苦是真实的——交付尖端产品、保持竞争力、跟上不断变化的用户期望&#xff0c;综合起来你的压力可能是压倒性的。 但是&#xff0c;如果我们告诉你有一个指南针…

【工具推荐】一键多平台文章发布神器推荐(免费)

hello&#xff0c;大家好&#xff0c;我是你们老朋友洛林&#xff0c;上一篇文章说到自己深受多平台手动发布的折磨「传送门」&#xff0c;准备开发一款文章多平台工具&#xff0c;后来联系到 Wechatsync 原作者进行了简单的沟通&#xff0c;下面是关于以后的一些规划&#xff…