手写Mybatis

2023-11-07

Mybatis核心配置文件就是为了配置Configration

因此要首先会解析Mybatis核心配置文件

首先使用dom4J解析Mybatis核心配置文件

新建模块演示dom4j解析.xml

目录放错了  无所谓

引入依赖

从原来项目可以拷贝过来

就些简单配置就好

解析核心配置文件和解析xxxMapper.xml映射文件代码

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

/**
 * @author hrui
 * @date 2023/9/10 18:24
 */
public class ParseXMLByDom4JTest {


    //解析Mapper映射文件
    @Test
    public void testParseSqlMapperXML() throws DocumentException {
        //创建SAXReader对象
        SAXReader reader=new SAXReader();
        //获取输入流(将resources目录下的mybatis-config1.xml转输入流)
        InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("mapper/CarMapperABC.xml");
        //读XML文件
        Document document=reader.read(inputStream);
        String xpath="/mapper";
        Element mapper =(Element) document.selectSingleNode(xpath);
        String namespace = mapper.attributeValue("namespace");
        System.out.println(namespace);//aaa
        //获取mapper节点下所有子节点
        List<Element> elements = mapper.elements();
        elements.forEach(e->{
            //获取sqlId
            String id = e.attributeValue("id");
            //获取resultType  没有返回null
            System.out.println(id);
            //获取标签中的sql语句,取出前后空白
            String sql = e.getTextTrim();
            System.out.println(sql);//insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
            //现在是:insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
            //内部肯定使用JDBC:insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)
            //转换
            String newSql = sql.replaceAll("#\\{[0-9A-Za-z_$]*}", "?");//使用正则替换
            System.out.println(newSql);//insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)
        });
    }



    //解析核心配置文件
    @Test
    public void testParseMybatisConfigXML() throws DocumentException {
        //创建SAXReader对象
        SAXReader reader=new SAXReader();
        //获取输入流(将resources目录下的mybatis-config1.xml转输入流)
        InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config1.xml");
        //读XML文件
        Document document=reader.read(inputStream);
        //System.out.println(document);
        //获取根标签
        Element root = document.getRootElement();
        //System.out.println(root.getName());//configuration
        //获取跟标签后肯定是去获取环境 environments 获取里面的default数据库id
        //获取默认的环境id
        //xpath是做标签路径匹配的.能够让我们快速定位XML文件中的元素
        //以下的xpath:从跟下开始找configuration,然后找configuration标签下的子标签environments
        String xpath="/configuration/environments";
        Element element = (Element)document.selectSingleNode(xpath);
        //System.out.println(element);
        //获取属性的值 默认环境id
        String aDefaultEnvivironmentId = element.attributeValue("default");
        //System.out.println("默认环境id是:"+aDefaultEnvivironmentId);//development

        //去找environment的 id属性,然后和默认环境id比较,确定默认使用哪个数据库
        //下面是xpath的写法 意思找
        xpath="/configuration/environments/environment[@id='"+aDefaultEnvivironmentId+"']";
        Element enviroment =(Element) document.selectSingleNode(xpath);
        //System.out.println(enviroment.getName());environment
        //获取environment节点下的transactionManager
        Element transactionManager = enviroment.element("transactionManager");
        String transactionType = transactionManager.attributeValue("type");
        //System.out.println("事务管理器的类型:"+transactionType);//获取事务管理器类型----->JDBC
        //接着获取datasource节点
        Element datasource = enviroment.element("dataSource");
        String dataSourcetype = datasource.attributeValue("type");
        System.out.println("数据源的类型:"+dataSourcetype);//POOLED
        //获取dataSource下的所有子节点
        List<Element> propertyElements = datasource.elements();
        //遍历
        propertyElements.forEach(e->{
            String name = e.attributeValue("name");
            String value = e.attributeValue("value");
            System.out.println("name="+name+",value="+value);
        });

        //获取所有的mapper标签
        //如果你不想从跟下开始获取,而是想从任意位置开始,获取某个标签,xpath可以这样写
        xpath="//mapper";//  两个//开始
        List<Node> mappers = document.selectNodes(xpath);
        //遍历
        mappers.forEach(n->{
            Element e=(Element)n;
            String resource = e.attributeValue("resource");
            System.out.println(resource);
        });

    }
}

新建module 

引入依赖

可以回顾下标准写法

逆推下

Resources

SqlSessionFactoryBuilder

SqlSessionFactory

transaction  

SqlSession

SQL Mapper

思路:1.Resources用来加载核心配置文件,返回一个InputStream流,

2.SqlSessionFactoryBuilder里有个build方法,该方法用来返回一个SqlSessionFactory对象

这里要考虑,SqlSessionFactory里有些什么,用来做什么.

那么这样就明了了,build方法用来解析核心配置文件,用以给SqlSessionFactory里的属性赋值,而属性有哪些,就是上面这些呗(事务管理器,JDBC连接需要的driver,url,username,password)

另外

和映射文件

是否考虑用一个容器Map存放,key为sql的id   value是其他

将这个对象也封装到SqlSessionFactory中

这个对象中该有哪些属性:暂时放sql语句和resultType  当然实际放的不只这两个  获取不到就是null

SqlSessionFactory中还需要一个事务管理器,这个事务管理器可以是JDBC也可以是MANAGED,那么可以定义为接口,另外定义两个具体的实现  这里使用JDBC事务管理器

而我们定义了事务管理器之后,事务管理器需要搞定的就三个方法,commit,rollback,close

但是这三个方法需要连接对象,而要获取连接对象可以定义个数据源

在核心配置文件中,数据源有三个选项,分别是POOLED   UNPOOLED  JNDI

那好办,定义为接口呗

这里有个JDK规范,不管你是POOLED   UNPOOLED  JNDI所有的数据源都需要实现JDK里的DataSource接口  那么接口也不用定义了 直接写三个实现类  这里使用UNPOOLED 不使用连接池

那么在实现类里需要driver url username password属性

然后又个SqlSession对象

里面又insert方法   xxx 方法  需要用到什么,再解决

基本思路就是这样

整体结构

1.Resources

package com.gmybatis.utils;

import java.io.InputStream;

/**
 * 工具类
 * 用于"类路径"中资源的加载
 * @author hrui
 * @date 2023/9/10 20:25
 */
public class Resources {
    //工具类建议构造方法私有化,因为工具类一般方法都是静态的,是种编程习惯

    public Resources() {
    }

    /**
     * 用于"类路径"种加载资源
     * @param resource
     * @return
     */
    public static InputStream getResourceAsStream(String resource){
        return ClassLoader.getSystemClassLoader().getResourceAsStream(resource);
    }
}

2.SqlSessionFactoryBuilder   构建器对象

package com.gmybatis.core;

import com.gmybatis.utils.Resources;
import jdk.nashorn.internal.ir.ReturnNode;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;


import javax.sql.DataSource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * SqlSessionFactory的构建器对象
 * 通过SqlSessionFactoryBuilder的build方法来解析核心配置文件,
 * 然后创建SqlSessionFactory对象
 * @author hrui
 * @date 2023/9/10 20:31
 */
public class SqlSessionFactoryBuilder {

    public SqlSessionFactoryBuilder() {
    }


    /**
     * 解析核心配置文件,来构建SqlSessionFactory
     * @param in
     * @return
     */
    public SqlSessionFactory build(InputStream in){
        SqlSessionFactory sqlSessionFactory=null;
        try {
            //解析核心配置文件
            //创建SAXReader对象
            SAXReader reader=new SAXReader();

            //读XML文件
            Document document=reader.read(in);
            //获取environments
            Element environments =(Element) document.selectSingleNode("/configuration/environments");
            //获取default属性值
            String defaultId = environments.attributeValue("default");

            //拿匹配的环境节点
            Element environment = (Element)document.selectSingleNode("/configuration/environments/environment[@id='"+defaultId+"']");
            //获取transactionManager
            Element transactionManagerEle = environment.element("transactionManager");
            //获取dataSource
            Element dataSourceEle = environment.element("dataSource");
            //获取mapper
            List<Node> mapperList = document.selectNodes("//mapper");//获取整个配置文件中所有的mapper标签
            //用于封装所有mappers里面的mapper的路径
            List<String> sqlMapperXMLPathList=new ArrayList<>();
            mapperList.forEach(n->{
                Element e=(Element) n;
                String resource = e.attributeValue("resource");
                sqlMapperXMLPathList.add(resource);
            });


            //获取数据源对象
            DataSource dataSource=getDataSource(dataSourceEle);

            //定义事务管理器
            Transaction transaction=getTransaction(transactionManagerEle,dataSource);


            //key是namespase+sql的id
            Map<String,MappedStatement> MappedStatements=getMappedStatements(sqlMapperXMLPathList);

            //解析完成之后,构建出SqlSessionFactory对象
            sqlSessionFactory=new SqlSessionFactory(transaction,MappedStatements);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sqlSessionFactory;
    }

    private Map<String, MappedStatement> getMappedStatements(List<String> sqlMapperXMLPathList) {
        Map<String,MappedStatement> map=new HashMap<>();
        sqlMapperXMLPathList.forEach(path->{
            try {
                //创建SAXReader对象
                SAXReader reader=new SAXReader();
                //读XXXMapper.XML映射文件
                Document document=reader.read(Resources.getResourceAsStream(path));
                //解析映射文件
                Element mapper = (Element)document.selectSingleNode("mapper");//带不带/都可以
                String namespace = mapper.attributeValue("namespace");
                List<Element> elements = mapper.elements();
                elements.forEach(e->{
                    String id = e.attributeValue("id");
                    //namespase+id
                    String sqlId=namespace+"."+id;
                    String resultType = e.attributeValue("resultType");
                    String sqlContent = e.getTextTrim();
                    MappedStatement mappedStatement=new MappedStatement(sqlContent,resultType);

                    map.put(sqlId,mappedStatement);
                });
            } catch (DocumentException e) {
                e.printStackTrace();
            }
        });
        return map;
    }

    private DataSource getDataSource(Element dataSourceEle) {
        Map<String,String> map=new HashMap<>();
        //获取节点下所有property
        List<Element> propertys = dataSourceEle.elements("property");
        propertys.forEach(e->{
            String name = e.attributeValue("name");
            String value = e.attributeValue("value");
            map.put(name,value);
        });

        DataSource dataSource=null;
        //type 可能是  POOLED UNPOOLED JNDI
        String type = dataSourceEle.attributeValue("type");
//        if(type.equalsIgnoreCase("POOLED")){  //这里简易定义常量类
//
//        }
        if(type.equalsIgnoreCase(Const.POOLED_DATASOURCE)){
            dataSource=new POOLEDDataSource();
        }
        if(type.equalsIgnoreCase(Const.UN_POOLED_DATASOURCE)){//只对这个做了实现
            dataSource=new UNPOOLEDDataSource(map.get("driver"),map.get("url"),map.get("username"),map.get("password"));
        }
        if(type.equalsIgnoreCase(Const.JNDI_DATASOURCE)){
            dataSource=new JNDIDataSource();
        }
        return dataSource;
    }

    private Transaction getTransaction(Element transactionManager, DataSource dataSource) {

        Transaction transaction=null;
        String type = transactionManager.attributeValue("type");
        if(type.equalsIgnoreCase(Const.JDBC_TRANSACTION)){
            transaction=new JDBCTransaction(dataSource,false );//只对JDBCTransaction做了实现
        }
        if(type.equalsIgnoreCase(Const.MANAGED_TRANSACTION)){
            transaction=new MANAGEDTransaction();
        }
        return transaction;
    }


}

3.SqlSessionFactory

package com.gmybatis.core;

import java.util.Map;

/**
 *SqlSessionFactory对象,一个数据库对应一个SqlSessionFactory对象
 * 通过SqlSessionFactory对象可以获得SqlSession对象(开启会话)
 * 一个SqlSessionFactory对象可以开启多个SqlSession会话
 * @author hrui
 * @date 2023/9/10 20:34
 */
public class SqlSessionFactory {
    //事务管理器 可以是JDBC:原生JDBC事务 也可以是MANAGED:交容器管理,比如Spring   可以灵活切换建议定义为接口
    private Transaction transaction;

    //数据源属性 因为已经在事务管理器里定义了数据源,因此这里不需要定义  可以通过事务管理器来获取


    private Map<String,MappedStatement> mappedStatementMap;

    public SqlSessionFactory() {
    }

    public SqlSessionFactory(Transaction transaction, Map<String, MappedStatement> mappedStatementMap) {
        this.transaction = transaction;
        this.mappedStatementMap = mappedStatementMap;
    }

    public Transaction getTransaction() {
        return transaction;
    }

    public void setTransaction(Transaction transaction) {
        this.transaction = transaction;
    }

    public Map<String, MappedStatement> getMappedStatementMap() {
        return mappedStatementMap;
    }

    public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
        this.mappedStatementMap = mappedStatementMap;
    }

    //    public SqlSession openSession(boolean flag){
//        return null;
//    }
}

Transaction接口及实现类,这里只实现了JDBCTransaction

package com.gmybatis.core;

import java.sql.Connection;

/**
 * 事务管理器接口,所有的事务管理器都应该实现该接口
 * JDBC事务管理器,MANAGED事务管理器都应该实现这个接口
 * 提供控制事务的方法
 * @author hrui
 * @date 2023/9/10 21:14
 */
public interface Transaction {

    //提交事务
    void commit();

    //回滚事务
    void rollback();

    //关闭事务
    void close();

    /**
     * 是否需要其他方法后续再看
     * 真正开启数据库连接
     */
    void openConnection();

    Connection getConnection();
}

JDBCTransaction

package com.gmybatis.core;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * JDBC事务管理器
 * @author hrui
 * @date 2023/9/10 21:19
 */
public class JDBCTransaction implements Transaction{

    //数据源属性
    private  DataSource dataSource;

    /**
     * 自动提交标志
     * true为自动提交
     * false为不自动提交
     */
    private Boolean aotoCommit;

    private Connection connection;

    /**
     * 用于外界获取Connection
     * 外界用的Connection对象 必须和事务管理器的是同一个
     * 这样才可以在事务管理器里  commit  rollback  closed
     * @return
     */
    @Override
    public Connection getConnection() {
        return connection;
    }

    //用于给属性connection赋值 只要事务管理器不换 连接就是同一个连接、
    @Override
    public void openConnection(){
        if(connection==null){
            try {
                connection=dataSource.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public JDBCTransaction(DataSource dataSource, Boolean aotoCommit) {
        this.dataSource = dataSource;
        this.aotoCommit = aotoCommit;
    }

    @Override
    public void commit() {
        //控制事务的时候需要调用JDBC里的连接对象 需要数据源
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void rollback() {
        try {
            connection.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void close() {
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

MANAGEDTransaction

package com.gmybatis.core;

import java.sql.Connection;

/**
 * MANAGED事务管理器
 * @author hrui
 * @date 2023/9/10 21:19
 */
public class MANAGEDTransaction implements Transaction{
    @Override
    public void commit() {

    }

    @Override
    public void rollback() {

    }

    @Override
    public void close() {

    }

    @Override
    public void openConnection() {

    }

    @Override
    public Connection getConnection() {
        return null;
    }
}

实现DataSource的3个实现   只实现了UNPOOLEDDataSource

package com.gmybatis.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
 * 不使用数据库连接池
 * @author hrui
 * @date 2023/9/10 21:33
 */
public class UNPOOLEDDataSource implements DataSource {

    private String driver;

    private String url;

    private String username;

    private String password;


    public UNPOOLEDDataSource(String driver, String url, String username, String password) {
        try {
            //直接注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
    }



    @Override
    public Connection getConnection() throws SQLException {
        //需要driver url username password 定义为属性
        Connection connection = DriverManager.getConnection(url, username, password);
        return connection;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}
package com.gmybatis.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
 * 使用数据库连接池  这里不写连接池
 * @author hrui
 * @date 2023/9/10 21:32
 */
public class POOLEDDataSource implements DataSource {
    @Override
    public Connection getConnection() throws SQLException {
        //从数据库连接池获取Connection对象 这里不写连接池
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

JNDIDataSource

package com.gmybatis.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 hrui
 * @date 2023/9/10 21:33
 */
public class JNDIDataSource implements DataSource {
    @Override
    public Connection getConnection() throws SQLException {
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {

        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

定义了一个常量类

package com.gmybatis.core;

/**
 * 整个框架的常量类
 * @author hrui
 * @date 2023/9/10 22:26
 */
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";
}

注意测试时候事务管理要选用JDBC   数据源类型要选用UNPOOLED

mapper路径要写对

测试基本没问题

下面把SqlSession加进去

package com.gmybatis.core;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Locale;

/**
 * 专门负责执行SQL语句的会话对象
 * @author hrui
 * @date 2023/9/10 23:40
 */
public class SqlSession {

    private SqlSessionFactory sqlSessionFactory;

    public SqlSession(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }


    //测试
    public static void main(String[] args) {
        String sql="insert into t_car values(#{id},#{asd},#{name})";
        int fromIndex=0;
        int index=1;
        while(true) {
            int jingIndex = sql.indexOf("#",fromIndex);

            if(jingIndex<0){
                break;
            }
            System.out.println(index);
            index++;
            int youkuohaoIndex = sql.indexOf("}",fromIndex);
            String propertyName = sql.substring(jingIndex + 2, youkuohaoIndex).trim();
            System.out.println(propertyName);
            fromIndex = youkuohaoIndex + 1;
        }

    }


    public int insert(String sqlId,Object obj){
        //JDBC代码
        Connection connection=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        int count = 0;
        try {
            connection=sqlSessionFactory.getTransaction().getConnection();
            String sql=sqlSessionFactory.getMappedStatementMap().get(sqlId).getSql();
            String sql1 = sql.replaceAll("#\\{[0-9A-Za-z_$]*}", "?");//使用正则替换
            ps = connection.prepareStatement(sql1);
            //有几个?号 不知道将pojo对象中的哪个属性给哪个 暂时全当String

            int fromIndex=0;
            int index=1;
            while(true) {
                int jingIndex = sql.indexOf("#",fromIndex);

                if(jingIndex<0){
                    break;
                }
                //System.out.println(index);

                int youkuohaoIndex = sql.indexOf("}",fromIndex);
                String propertyName = sql.substring(jingIndex + 2, youkuohaoIndex).trim();
                //System.out.println(propertyName);
                fromIndex = youkuohaoIndex + 1;
                String getMethodName="get"+propertyName.toUpperCase().charAt(0)+propertyName.substring(1);
                Method getMethod=obj.getClass().getDeclaredMethod(getMethodName);
                Object invoke = getMethod.invoke(obj);
                ps.setString(index,invoke.toString());
                index++;
            }

            count = ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //这里不要加 不然直接关闭了
//        finally {
//            if(rs!=null){
//                try {
//                    rs.close();
//                } catch (SQLException e) {
//                    e.printStackTrace();
//                }
//            }
//            if(ps!=null){
//                try {
//                    ps.close();
//                } catch (SQLException e) {
//                    e.printStackTrace();
//                }
//            }
//            if(connection!=null){
//                try {
//                    connection.close();
//                } catch (SQLException e) {
//                    e.printStackTrace();
//                }
//            }
//        }
        return count;
    }


    public Object selectOne(){
        return null;
    }

    public void commit(){
        sqlSessionFactory.getTransaction().commit();
    }

    public void rollback(){
        sqlSessionFactory.getTransaction().rollback();
    }

    public void close(){
        sqlSessionFactory.getTransaction().close();
    }
}

修改sqlSessionFactory

package com.gmybatis.core;

import java.util.Map;

/**
 *SqlSessionFactory对象,一个数据库对应一个SqlSessionFactory对象
 * 通过SqlSessionFactory对象可以获得SqlSession对象(开启会话)
 * 一个SqlSessionFactory对象可以开启多个SqlSession会话
 * @author hrui
 * @date 2023/9/10 20:34
 */
public class SqlSessionFactory {
    //事务管理器 可以是JDBC:原生JDBC事务 也可以是MANAGED:交容器管理,比如Spring   可以灵活切换建议定义为接口
    private Transaction transaction;

    //数据源属性 因为已经在事务管理器里定义了数据源,因此这里不需要定义  可以通过事务管理器来获取


    private Map<String,MappedStatement> mappedStatementMap;

    public SqlSessionFactory() {
    }

    public SqlSessionFactory(Transaction transaction, Map<String, MappedStatement> mappedStatementMap) {
        this.transaction = transaction;
        this.mappedStatementMap = mappedStatementMap;
    }

    public Transaction getTransaction() {
        return transaction;
    }

    public void setTransaction(Transaction transaction) {
        this.transaction = transaction;
    }

    public Map<String, MappedStatement> getMappedStatementMap() {
        return mappedStatementMap;
    }

    public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
        this.mappedStatementMap = mappedStatementMap;
    }

    /**
     * 获取sql会话对象
     * @param flag
     * @return
     */
    public SqlSession openSession(){
        //开启会话的前提是开启连接
        transaction.openConnection();
        //创建SqlSession对象
        SqlSession sqlSession=new SqlSession(this);
        return sqlSession;
    }
}

JDBCTransaction的openConnection的代码加上开启事务

package com.gmybatis.core;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * JDBC事务管理器
 * @author hrui
 * @date 2023/9/10 21:19
 */
public class JDBCTransaction implements Transaction{

    //数据源属性
    private  DataSource dataSource;

    /**
     * 自动提交标志
     * true为自动提交
     * false为不自动提交
     */
    private boolean aotoCommit;

    private Connection connection;

    /**
     * 用于外界获取Connection
     * 外界用的Connection对象 必须和事务管理器的是同一个
     * 这样才可以在事务管理器里  commit  rollback  closed
     * @return
     */
    @Override
    public Connection getConnection() {
        return connection;
    }

    //用于给属性connection赋值 只要事务管理器不换 连接就是同一个连接、
    @Override
    public void openConnection(){
        if(connection==null){
            try {
                connection=dataSource.getConnection();
                //开启事务
                connection.setAutoCommit(aotoCommit);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public JDBCTransaction(DataSource dataSource, Boolean aotoCommit) {
        this.dataSource = dataSource;
        this.aotoCommit = aotoCommit;
    }

    @Override
    public void commit() {
        //控制事务的时候需要调用JDBC里的连接对象 需要数据源
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void rollback() {
        try {
            connection.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void close() {
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

引入mysql依赖测试  新建表

新建实体类

package com.gmybatis.core;

/**
 * @author hrui
 * @date 2023/9/11 0:31
 */
public class Car {
    private String id;
    private String name;
    private String age;

    public Car() {
    }

    public Car(String id, String name, String age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

映射文件

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

手写Mybatis 的相关文章

  • Jmeter 性能压测-常遇问题与解决技巧

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 2k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • INT201 形式语言与自动机笔记(下)

    L6 Context Free Languages 上下文无关语言 Context Free Grammar CFG 是一组用于生成字符串模式的递归规则 上下文无关的语法可以描述所有的常规语言 但它们不能描述所有可能的语言 e g 遵循这些
  • 1.常用单词学习

    1 1 听力练习 第一课 Av264771740 P1 Av736460000 P1 哔哩哔哩 bilibili 有推荐的吗 这个和这个都很推荐 这个多少钱 请给我这个 全部 这些一共多少钱 卫生间在哪呢 一度 願 麻烦再来一次 英語話 会
  • 实时获取建材网商品数据:API实现详解与代码示例

    一 引言 随着电子商务的快速发展 实时获取商品数据对于企业决策 市场分析以及数据驱动的营销策略至关重要 建材网作为国内知名的建材信息平台 提供了API接口 使得第三方开发者可以方便地获取商品数据 本文将详细介绍如何使用 建材网的API接口
  • 网络安全从入门到精通(超详细)学习路线

    首先看一下学网络安全有什么好处 1 可以学习计算机方面的知识 在正式学习网络安全之前是一定要学习计算机基础知识的 只要把网络安全认真的学透了 那么计算机基础知识是没有任何问题的 操作系统 网络架构 网站容器 数据库 前端后端等等 可以说不想
  • 【一份老网工珍藏多年的网络配置笔记,很重要!】

    01 交换机 路由器的几种配置模式及模式转换 1 用户模式 登录到交换机 路由器 时会自动进入用户模式 提示符为 switchname gt 在该模式下只能够查看相关信息 对 IOS的运行不产生任何影响 2 特权模式 用户模式下 键入 en
  • 2020年认证杯SPSSPRO杯数学建模C题(第二阶段)抗击疫情,我们能做什么全过程文档及程序

    2020年认证杯SPSSPRO杯数学建模 C题 抗击疫情 我们能做什么 原题再现 2020 年 3 月 12 日 世界卫生组织 WHO 宣布 席卷全球的冠状病毒引发的病毒性肺炎 COVID 19 是一种大流行病 世卫组织上一次宣布大流行是在
  • The Planets:Venus

    靶场下载 The Planets Venus VulnHub 信息收集 arp scan l Interface eth0 type EN10MB MAC 00 0c 29 43 7c b1 IPv4 192 168 1 60 Starti
  • 小白也能学会的创建Git仓库实操

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 2k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • SRC漏洞挖掘经验+技巧篇

    一 漏洞挖掘的前期 信息收集 虽然是前期 但是却是我认为最重要的一部分 很多人挖洞的时候说不知道如何入手 其实挖洞就是信息收集 常规owasp top 10 逻辑漏洞 重要的可能就是思路猥琐一点 这些漏洞的测试方法本身不是特别复杂 一般混迹
  • 白帽子如何快速挖到人生的第一个漏洞 | 购物站点挖掘商城漏洞

    本文针对人群 很多朋友们接触安全都是通过书籍 网上流传的PDF 亦或是通过论坛里的文章 但可能经过了这样一段时间的学习 了解了一些常见漏洞的原理之后 对于漏洞挖掘还不是很清楚 甚至不明白如何下手 可能你通过 sql labs 初步掌握了sq
  • 【VUE毕业设计】基于SSM的在线课堂学习设计与实现(含源码+论文)

    文章目录 1 项目简介 2 实现效果 2 1 界面展示 3 设计方案 3 1 概述 3 2 系统流程 3 3 系统结构设计 4 项目获取
  • 为什么我强烈推荐大学生打CTF!

    前言 写这个文章是因为我很多粉丝都是学生 经常有人问 感觉大一第一个学期忙忙碌碌的过去了 啥都会一点 但是自己很难系统的学习到整个知识体系 很迷茫 想知道要如何高效学习 这篇文章我主要就围绕两点 减少那些罗里吧嗦的废话 直接上干货 CTF如
  • 动态数据源路由 - 数据源路由器未初始化

    我指的是这个article https spring io blog 2007 01 23 dynamic datasource routing 其中我们可以使用 Spring Framework 中的 AbstractRoutingDat
  • 使用 mybatis 进行 spring boot 的 spring 模拟 mvc 测试需要属性“sqlSessionFactory”或“sqlSessionTemplate”

    演示项目位置 https github com soliders mockmvctest ofspringboot withmybatismapper https github com soliders mockmvctest ofspri
  • MyBatis 不返回查询的所有结果

    问题 我有一个返回 17 条记录的查询 当我将 MyBatis 与具有以下内容的地图一起使用时
  • Intellij 无法理解 SQL 字符串

    大家 我正在制作一个玩具网络应用程序 它使用 Spring Boot 和 Mybatis Mybatis映射器配置Java接口 我希望 Intellij 能够理解 SQL 字符串 但事实并非如此 我期待像下面这样的 如果它理解 Intell
  • “class java.lang.String”中名为“tablename”的属性没有 getter

    我收到错误There is no getter for property named tablename in class java lang String 当我使用mybatis时 我的映射器 xml 是这样的
  • Spring Service @Transactional 不回滚事务 Mybatis SqlSession

    目标是在失败时回滚所有 任何事务 但这并没有按预期工作 我们使用Spring MVC JMS Service Mybatis 在日志中 JMS 设置为回滚 但行已插入且未回滚 想知道我错过了什么或做错了什么吗 最近添加了 Transacti
  • MyBatis 枚举的使用

    我知道以前有人问过这个问题 但我无法根据迄今为止找到的信息实施解决方案 所以也许有人可以向我解释一下 我有一个表 状态 它有两列 id 和 name id是PK 我不想使用 POJO Status 而是使用枚举 我创建了这样一个枚举 如下所

随机推荐

  • 自动计算30天内的股价最高价源代码

    我可以回答这个问题 您可以使用以下代码来计算30天内股价的最高价 复制 import pandas as pd import yfinance as yf 设置股票代码和日期范围 symbol AAPL start date 2021 01
  • Python绝技:运用Python成为顶级黑客

    Python 是一门常用的编程语言 它不仅上手容易 而且还拥有丰富的支持库 对经常需要针对自己所 处的特定场景 以极少的代码量实现所需的功能 Python绝技 运用Python成为顶级黑客结合具体的场景和真 实的案例 详述了 Python
  • 《软件测试的艺术》第三章 代码检查、走查和评审

    软件测试的艺术 第三章 代码检查 走查和评审 3 1 代码检查与走查 3 2 代码检查 3 2 1 代码检查小组 3 2 2 检查议程与注意事项 3 2 3 对事不对人 和人有关的注意事项 3 2 4 代码检查的衍生功效 3 3 用于代码检
  • 100个python算法超详细讲解:农夫过河

    100个python算法超详细讲解 谷哥技术 1 问题描述 一个农夫在河边带了一匹狼 一只羊和一棵白菜 他需要把这三样东西用 船带到河的对岸 然而 这艘船只能容下农夫本人和另外一样东西 如果农夫 不在场的话 狼会吃掉羊 羊也会吃掉白菜 请编
  • 鸿蒙内核阅读笔记-定时器

    鸿蒙内核阅读笔记 定时器 简介 核心模块 定时器 los swtmr c 介绍 阅读代码 函数部分 简介 近期在阅读鸿蒙liteOS a 由于是初次探索内核的奥秘 将一些阅读的心得进行分享 希望能在作为笔记的同时 也能帮助更多人学习 感谢图
  • springboot + eureka集群,实现注册中心,实现负载均衡

    搭建eureka集群 新建一个boot项目 File new project 如图选择 next 起名字如下 gt next finish 新建3个注册中心 以三个注册中心为例 想多的自己加 项目名字上 new module next 起名
  • Basic Level 1055 集体照 (25分)

    题目 拍集体照时队形很重要 这里对给定的 N 个人 K 排的队形设计排队规则如下 每排人数为 N K 向下取整 多出来的人全部站在最后一排 后排所有人的个子都不比前排任何人矮 每排中最高者站中间 中间位置为 m 2 1 其中 m 为该排人数
  • PowerDesigner安装步骤和打印错误

    1 解压 2 双击运行安装包 等待初始化 3 初始化完成后点击next 4 选择地区 同意协议 完成后点击next 5 选择安装位置 完成后点击next 6 选择要安装的组件 eclipse我不需要 需要可以勾选并配置 完成后点击next
  • pandas apply使用多列计算生成新的列

    在python数据分析中 有时需要根据多列数据生成中间结果 pandas给我们带来了很多方便 通常简短的代码可以实现一些高级功能 灵活掌握一些技巧可以事倍功半 pandas的apply方法用于对指定列的每个元素进行相同的操作 下面生成一个d
  • 静态代码分析工具清单:开源篇(各语言)

    本文是一个静态代码分析工具的清单 共有26个工具 包括4个 NET工具 2个Ada工具 7个C 工具 4个Java工具 2个JavaScript工具 1个Opa工具 2个Packaging工具 3个Perl工具 1个Python工具 1 N
  • Python——列表排序和赋值

    1 列表排序 列表排序方法 ls sort 对列表ls 中的数据在原地进行排序 ls 13 5 73 4 9 ls sort ls sort reverse False 默认升序 reverse True 降序 ls 13 5 73 4 9
  • Mysql备份工具xtraback全量和增量测试

    Mysql备份工具xtraback全量和增量测试 xtrabackup 是 percona 的一个开源项目 可以热备份innodb XtraDB 和MyISAM 会锁表 官方网址http www percona com docs wiki
  • 系统运维日常工作有哪些,应该具备哪些技能

    一 日常工作内容 看监控 网站流量 CDN流量 看邮件有没有普通业务监控报警 看有家中有没有其他需要做的工作 处理报警 查看报警的原因 和开发一起解决 并且尽量找出避免再次发生的方法 例如添加一些定时清理脚本 处理发布 基本都是自动化 但是
  • 使用tab键分割的文章能快速转换成表格。( )_Word教程:最常用的 7 个 Tab 键用法,瞅一眼就会了...

    Tab键一直是键盘上 不起眼 的一个按键 平时很多人可能不会去碰触这个键 实际上 它有很多实用的功能 如果用好了 可以大大提高我们的操作效率 01 段首空两格 写文章时 通常段首需要空两格 许多人会直接敲空格 这种操作是很Low的 其实 你
  • git cherry-pick apply-merge 任意commit(s)

    文章目录 必杀技 困惑 cherry pick 必杀技 是什么让Git 从众多版本控制系统中脱颖而出 独领风骚 当然是其分支模型 正因如此 人称分支模型为Git的 必杀技 如果不能熟练的运用Git的分支技巧 那就远离了Git的精华 困惑 我
  • zabbix配置微信报警

    如有错误 敬请谅解 此文章仅为本人学习笔记 仅供参考 如有冒犯 请联系作者删除 6 1 注册企业微信 企业微信注册地址 https work weixin qq com 设置总部门名称添加成员 也可以成员扫码加入 点击 成员加入 过程略 6
  • C语言实现RGB888转BMP格式图片功能

    1 bmp格式介绍 bmp格式图片里实际存储的也是RGB原始数据 可以分为8bit 16bit 24bit 32bit的bmp格式 也就是指bmp图片中保存的RGB是用8bit 16bit 24bit 32bit来表示 简单理解就是在原始R
  • 2020年IT技术的发展趋势!

    在IT范畴 企业指导者在IT范畴做出的选择不只会对业务开展和客户关系产生影响 也会对整体经济产生影响 而整体经济越来越依赖于为关键业务提供牢靠效劳的企业网络 随着岁末年初的到来 如今是瞻望将来一年行业开展趋向的时分 以便企业为做出明智的决议
  • 1054 求平均值

    1054 求平均值 20 分 本题的基本要求非常简单 给定 N 个实数 计算它们的平均值 但复杂的是有些输入数据可能是非法的 一个 合法 的输入是 1000 1000 区间内的实数 并且最多精确到小数点后 2 位 当你计算平均值的时候 不能
  • 手写Mybatis

    Mybatis核心配置文件就是为了配置Configration 因此要首先会解析Mybatis核心配置文件 首先使用dom4J解析Mybatis核心配置文件 新建模块演示dom4j解析 xml 目录放错了 无所谓 引入依赖 从原来项目可以拷