• 【零基础入门MyBatis系列】第五篇——手写MyBatis框架


    一、dom4j解析XML文件

    • 为了方便我们实践各种操作,我们创建了一个新的模块:parse-xml-by-dom4j
    • 为新模块设置打包方式和配置各种依赖
    • 将之前模块中的 mybatis-congfig.xmlCarMapper.xml 文件直接拷贝到了 resource 文件夹下

    🌔 1、解析核心配置文件 与 映射文件 【完整代码】

    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 Bonbons
     * @version 1.0
     */
    public class ParseXMLByDom4jTest {
        //解析SQL映射文件
        @Test
        public void testParseSqlMapperXML() throws Exception{
            //利用Reader获取数据流,也就是找到我们要解析的XML文件
            SAXReader reader = new SAXReader();
            InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("CarMapper.xml");
            Document document = reader.read(is);
            //获取namespace
            String xpath = "/mapper";
            Element mapper = (Element) document.selectSingleNode(xpath);
            String namespace = mapper.attributeValue("namespace");
            System.out.println(namespace);
            //获取当前节点下的所有子节点
            List<Element> mapperElements = mapper.elements();
            //遍历
            mapperElements.forEach(element -> {
                String id = element.attributeValue("id");
                System.out.println(id);
                //如果获取标签没有的属性,并不会报错而是返回mull
                String resultType = element.attributeValue("resultType");
                System.out.println(resultType);
                //获取标签中的sql语句[Trim的作用是去除前后空白,也有个getText方法]
                String sql = element.getTextTrim();
                System.out.println(sql);
                //利用replaceAll方法结合正则表达式,将占位符替换为 ?
                String newSql = sql.replaceAll("#\\{[0-9A-Za-z_$]*}", "?");
                System.out.println(newSql);
            });
        }
        @Test
        public void testParseMyBatisConfigXML() throws DocumentException {
            //通过SAXReader对象解析xml
            SAXReader reader = new SAXReader();
            //获取输入流
            InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
            //读取xml文件,返回整个xml文件的文档对象
            Document document =  reader.read(is);
            //我们利用xpath定位xml中的元素
            String xpath = "/configuration/environments";
            //文档类提供一个selectSingleNode方法去查找元素
            Element environments = (Element) document.selectSingleNode(xpath);
            //获取属性的值
            String defaultId = environments.attributeValue("default");
            System.out.println("环境id:" + defaultId);
            //获取具体的环境,我们仍然需要xpath去定位[如果此处不添加单引号就获取不到具体的属性值]
            //如果想获取环境的属性,需要采用 environment[@属性名]的方式
            xpath += "/environment[@id='"+defaultId+"']";
            Element environment = (Element) document.selectSingleNode(xpath);
    //        System.out.println(environment);
            //利用element方法获取指定元素下的子节点
            Element transcationManager = environment.element("transactionManager");
            //查看事务管理器的类型
            String transactionType = transcationManager.attributeValue("type");
            System.out.println("事务管理器:" + transactionType);
            //获取dataSource结点
            Element dataSource = environment.element("dataSource");
            String dataSourceType = dataSource.attributeValue("type");
            System.out.println("数据源:" + dataSourceType);
            //获取dataSource下的所有子节点
            List<Element> propertyElements = dataSource.elements();
            //遍历
            propertyElements.forEach(e -> {
                String name = e.attributeValue("name");
                String value = e.attributeValue("value");
                System.out.println(name + ":" + value);
            });
    
            //解析mapper
            //利用xpath获取路径
            xpath = "//mapper";
            List<Node> mappers = document.selectNodes(xpath);
            //遍历mappers
            mappers.forEach(e -> {
                //将原型强转为Element类型
                Element mapperElement = (Element) e;
                //获取resource属性
                String resource = mapperElement.attributeValue("resource");
                System.out.println(resource);
            });
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    🌔 2、解析核心配置文件的详细步骤

    (1)展示一下我的 mybatis-config.xml 文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <environments default="powernodeDB">
            <environment id="powernodeDB">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
                    <property name="username" value="root"/>
                    <property name="password" value="111111"/>
                </dataSource>
            </environment>
    
            <!--添加一个新的环境-->
            <environment id="mybatisDB">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.myql.cj.hdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/shop"/>
                    <property name="username" value="root"/>
                    <property name="password" value="111111"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <!--sql映射文件创建好之后,需要将该文件路径配置到这里-->
            <mapper resource="CarMapper.xml"/>
        </mappers>
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    (2)我们在 test/java 目录下创建一个测试类:ParseXMLByDom4jTest

    • 如果我们想解析一个文件,首先就要定位到这个文件的位置:

      • 我们通过 SAXReader 的实例去读取文件的数据流
      SAXReader reader = new SAXReader();
      InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml);
      Document document = reader(is);
      
      • 1
      • 2
      • 3
      • 最后得到的是整个xml文件的文档实例
    • 我们需要定位到具体的表现,所以我们实现要定位到环境标签的位置:

      • 我们使用 xpath 记录标签的位置
      • 通过 Document t的 selectSingleNode 去查找标签
      • 通过标签的 attributeValue(属性) 方法,可以获取标签的属性值
      String xpath = "/configuration/environments";
      Element environments = (Element) document.selectSingleNode(xpath);
      String default = environments.attributeValue("default");
      System.out.println("环境id:" + defaultId);
      
      • 1
      • 2
      • 3
      • 4
    • 之后与此类似,就是一层一层的解析xml文件中的所有标签及其属性

    • 接下来我们去查看具体的环境

      • 还是需要利用 xpath 去定位标签【因为可能存在多个环境,所以我们可以通过id去指定查看哪个环境】
      • 还是需要 selectSingleNode 根据 xpath 获取标签
      xpath += "/environment[@id='"+defaultId+"']";
      Element environment = (Element) document.selectSingleNode(xpath);
      System.out.println(environment);
      
      • 1
      • 2
      • 3
    • 因为之后与上述相同,且配置信息大部分都属于当前环境的子节点,所以给出说明和代码【注释在代码里面】

      • 我们使用 element(“标签名”) 获取子节点
      • 获得事务管理器的标签,并查看管理器的类型
      //获取子节点  -- 事务管理器
      Element transcationManager = environment.element("transactionManager");
      //查看事务管理器的类型
      String transactionType = transcationManager.attributeValue("type");
      System.out.println("事务管理器:" + transactionType);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 获取 dataSource 标签
      Element dataSource = environment.element("dataSource");
      String dataSourceType = dataSource.attributeValue("type");
      System.out.println("数据源:" + dataSourceType);
      
      • 1
      • 2
      • 3
      • 上面我们使用 element(标签名) 获取当前结点的一个指定子节点,我们可以使用 elements() 获取当前结点的所有子节点
      //获取dataSource下的所有子节点
      List<Element> propertyElements = dataSource.elements();
      //遍历
      propertyElements.forEach(e -> {
      	String name = e.attributeValue("name");
      	String value = e.attributeValue("value");
      	System.out.println(name + ":" + value);
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 还有一个 mapper 标签,但是它不是环境的子节点,所以我们呢要重新定位,其余步骤与上述内容类似

      //利用xpath获取路径
      xpath = "//mapper";
      List<Node> mappers = document.selectNodes(xpath);
      //遍历mappers
      mappers.forEach(e -> {
      //将原型强转为Element类型
      Element mapperElement = (Element) e;
      	 //获取resource属性
      	String resource = mapperElement.attributeValue("resource");
      	System.out.println(resource);
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 运行结果如下:【之所以输出多次CarMapper是我测试,在mapper标签里添加了多个重复资源】
      在这里插入图片描述
      🌔 3、解析映射文件xml文件CarMapper.xml

    (1)老样子,先看看 CarMapper.xml 文件中都有什么 【一条查询、一条插入语句

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.powernode.CarMapperTest">
        <insert id="insertCar">
            insert into t_cat values (null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})
        </insert>
    
        <select id="selectCarByCarNum" resultType="com.powernode.mybatis.pojo.Car">
            select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car where car_num = #{carNum}
        </select>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (2)如果想解析这个xml文件,整体思路梳理:

    • 我们要通过数据流与SAXReader实例定位到这个文件,最后获得document文档
    • 我们要利用 xpathselectSingleNode 获取根标签,此处为 namespace 标签,我们可以通过 attributeValue 方法获取属性值
    • 我们通过 elements() 获取所有子节点,进而查看属性值
    • 我们还可以通过 getTextTrim() 查看具体的SQL语句
    • 我们还可以将SQL语句的占位符替换成JDBC的格式
    • 代码如下:详情见注释
    	@Test
        public void testParseSqlMapperXML() throws Exception{
            //利用Reader获取数据流,也就是找到我们要解析的XML文件
            SAXReader reader = new SAXReader();
            InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("CarMapper.xml");
            Document document = reader.read(is);
            //获取namespace
            String xpath = "/mapper";
            Element mapper = (Element) document.selectSingleNode(xpath);
            String namespace = mapper.attributeValue("namespace");
            System.out.println(namespace);
            //获取当前节点下的所有子节点
            List<Element> mapperElements = mapper.elements();
            //遍历
            mapperElements.forEach(element -> {
                String id = element.attributeValue("id");
                System.out.println(id);
                //如果获取标签没有的属性,并不会报错而是返回mull
                String resultType = element.attributeValue("resultType");
                System.out.println(resultType);
                //获取标签中的sql语句[Trim的作用是去除前后空白,也有个getText方法]
                String sql = element.getTextTrim();
                System.out.println(sql);
                //利用replaceAll方法结合正则表达式,将占位符替换为 ?
                String newSql = sql.replaceAll("#\\{[0-9A-Za-z_$]*}", "?");
                System.out.println(newSql);
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    二、手写Godbatis框架

    • 并不是和mybatis一样的完整框架,只是实现了一些基本功能的删减版框架
    • 接下来我们从整体上分析一下这个框架:

    1、创建一个新的模块 – godbatis
    (1)确定打包方式和导入相关依赖【修改pom.xml文件】

    <packaging>jar</packaging>
        <dependencies>
            <!--dom4j依赖-->
            <dependency>
                <groupId>org.dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>2.1.3</version>
            </dependency>
            <!--jaxen依赖-->
            <dependency>
                <groupId>jaxen</groupId>
                <artifactId>jaxen</artifactId>
                <version>1.2.0</version>
            </dependency>
            <!--junit依赖-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    (2)为了方便从类路径加载资源,我们写一个工具类 – Resources.java

    package org.god.ibatis.utiils;
    
    import java.io.InputStream;
    
    /**
     * godbatis 工具类
     * 从类路径下加载资源
     * @author Bonbons
     * @version 1.0
     */
    public class Resources {
        /**
         * 工具类的构造器都是私有的 >> 不需要创建对象,提供的都是静态方法
         */
        public Resources(){}
        /**
         * 从类路径加载资源
         * @param resource 放在类路径当中的资源文件
         * @return 指向资源文件的一个输入流
         */
        public static InputStream getResourceAsStream(String resource){
            return ClassLoader.getSystemClassLoader().getResourceAsStream(resource);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2、创建SqlSessionFactoryBuilder类,为其设计一个build方法,可以返回一个SqlSessionFactory的对象

    package org.god.core;
    
    import java.io.InputStream;
    
    /**
     * SqlSessionFactory对象构建器
     * @author 老杜
     * @version 1.0
     * @since 1.0
     */
    public class SqlSessionFactoryBuilder {
    
        /**
         * 创建构建器对象
         */
        public SqlSessionFactoryBuilder() {
        }
    
    
        /**
         * 获取SqlSessionFactory对象
         * 该方法主要功能是:读取godbatis核心配置文件,并构建SqlSessionFactory对象
         * @param inputStream 指向核心配置文件的输入流
         * @return SqlSessionFactory对象
         */
        public SqlSessionFactory build(InputStream inputStream){
            // 解析配置文件,创建数据源对象
            // 解析配置文件,创建事务管理器对象
            // 解析配置文件,获取所有的SQL映射对象
            // 将以上信息封装到SqlSessionFactory对象中
            // 返回
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3、既然我们要通过build方法返回SqlSessionFactory的实例,那么我们要知道SqlSessionFactory包含哪些属性呢?

    • 使用了哪种事务管理器:此处我们只实现JDBC的事务管理器
    • SQL映射对象集合:为了方便,我们将SQL语句抽象出来,通过一个简单的java类,封装了SQL语句的resultType和语句内容

    (1)我们思考一个问题,对于事务管理器和数据源类型都有多种,难道我们他们彼此之间没有联系吗?

    实则不然,对于两种事务管理器、三种数据源类型他们各自都要遵守自己的规范,也就是我们要通过实现接口的方式来完成。【面向接口编程】
    (2)我们定义一个事务管理器的接口

    package org.god.ibatis.core;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    
    /**
     * 事务管理器接口,所有的事务管理器都应该实现该接口
     * 实际上接口也只有两个:JDBC、MANAGED事务管理器
     * 就是提供事务管理的方法
     *  @author Bonbons
     * @version 1.0
     */
    public interface Transaction {
        /**
         * 提交事务
         */
        void commit();
        /**
         * 回滚事务
         */
        void rollback();
        /**
         * 关闭事务
         */
        void close();
    
        //开启数据库连接
        void openConnection();
    
        //返回连接对象
        Connection getConnection();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    (2)再定义两个类JdbcTransactionManagedTransaction分别代表两种事务管理器的实现类,因为我们以JDBC为例,所以此处只给出如何实现Jdbc的事务管理器。

    package org.god.ibatis.core;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    /**
     * JDBC 事务管理器
     * @author Bonbons
     * @version 1.0
     */
    public class JdbcTransaction implements Transaction{
        /**
         * 数据源属性,面向接口编程
         */
        private DataSource dataSource;
    
        /**
         * 自动提交标志
         * true 表示自动提交
         * false 表示不采用自动提交
         */
        private boolean autoCommit;
        private Connection connection;
        //因为执行SQL语句的时候也需要连接对象,所以为外部提供获取connection的方法
        @Override
        public Connection getConnection(){
            return connection;
        }
        /**
         * 创建事务管理器对象
         * @param dataSource
         * @param autoCommit
         */
        public JdbcTransaction(DataSource dataSource, boolean autoCommit) {
            this.dataSource = dataSource;
            this.autoCommit = autoCommit;
        }
    
        @Override
        public void commit() {
            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.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void openConnection(){
            try {
                if(connection == null){
                    connection = dataSource.getConnection();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    (2)因为事务管理器的实例化需要获取数据源,所以我们将数据源的定义在事务管理器中完成,对外部提供会话对象的获取方法

    • 与事务管理器不同,我们不需要去写一个数据源的接口,因为Javajdk为我们提供了
    • 我们自己写的数据源类只需要实现 javax.sql.dataSource 接口
    • 数据源的类型有三种: POOLED、UNPOOLED、JNDI三种类型,我么以UNPOLED为例

    我们创建一个 UnPooledDataSource.java 用来实现数据源的获取

    package org.god.ibatis.core;
    
    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 Bonbons
     * @version 1.0
     */
    public class UnPooledDataSource implements javax.sql.DataSource{
        // private String driver; 数据驱动创建一次就可以了
        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;
        }
    
    
    
        @Override
        public Connection getConnection() throws SQLException {
            //每次获取连接的时候,都创建一个连接对象
            Connection connection = DriverManager.getConnection(url, username, password);
            return connection;
    
        }
    
        @Override
        public Connection getConnection(String username, String password) throws SQLException {
            return null;
        }
    
        @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;
        }
    
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    (3)事务管理器和数据源都有了,接下来我们就要考虑获取具体的SQL语句了

    • 我们定义一个 MapperdStatement.java 用来封装SQl标签
    package org.god.ibatis.core;
    
    /**
     * 普通pojo类,作用就是封装一个SQL标签
     * 一个MapperdStatement对象对应一个SQL标签
     * @author Bonbons
     * @version 1.0
     */
    public class MapperdStatement {
        /**
         * sql 代表SQL语句
         * resultType 代表返回的结果类型,只有在select语句中不为空
         */
        private String sql;
        private String resultType;
    
        //有参和无参构造方法
        public MapperdStatement(){}
    
        public MapperdStatement(String sql, String resultType) {
            this.sql = sql;
            this.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;
        }
    
        @Override
        public String toString() {
            return "MapperdStatement{" +
                    "sql='" + sql + '\'' +
                    ", resultType='" + resultType + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    4、此时已经完成了基本结构的定义,接下来需要完善SqlSessionFactory类和SqlSessionFactoryBuilder
    (1)完善SqlSessionFactory类,包含具体的属性、构造方法、GetSet方法

    package org.god.ibatis.core;
    
    import java.util.Map;
    
    /**
     * 一个数据库对应一个SqlSessionFactory对象,通过该对象可以开启会话并返回
     * 一个SqlSession的对象(一对多)
     * @author Bonbons
     * @version 1.0
     */
    public class SqlSessionFactory {
        /**
         * 事务管理器
         */
        Transaction transaction;
        /**
         * 存放sql语句的Map集合
         * key是sqlId
         * value是对应SQL标签信息对象
         */
        private Map<String, MapperdStatement> mapperdStatements;
        public SqlSessionFactory(){}
    
        public SqlSessionFactory(Transaction transaction, Map<String, MapperdStatement> mapperdStatements) {
            this.transaction = transaction;
            this.mapperdStatements = mapperdStatements;
        }
    
        public Transaction getTransaction() {
            return transaction;
        }
    
        public void setTransaction(Transaction transaction) {
            this.transaction = transaction;
        }
    
        public Map<String, MapperdStatement> getMapperdStatements() {
            return mapperdStatements;
        }
    
        public void setMapperdStatements(Map<String, MapperdStatement> mapperdStatements) {
            this.mapperdStatements = mapperdStatements;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    (2)完成SqlSessionFactoryBuilder类中的build方法【重点内容】

    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.utiils.Resources;
    
    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    public class SqlSessionFactoryBuilder {
        public SqlSessionFactoryBuilder() {
        }
    
        /**
         * 解析godbatis-config.xml文件,来构造SqlSessionFactory独享
         *
         * @param in 指向godbatis-config.xml文件的一个输入流
         * @return SqlSessionFactory对象
         */
        //利用build方法创建SqlSessionFactory
        public SqlSessionFactory build(InputStream in) {
            SqlSessionFactory factory = null;
            try {
                //解析godbatis-config.xml文件
                //(1)获取文件 >> 转换为document文档
                SAXReader reader = new SAXReader();
                Document document = reader.read(in);
                //(2)获得环境集合节点
                Element environments = (Element) document.selectSingleNode("/configuration/environments");
                //(3)获得它的属性 >> 默认使用的环境id
                String defaultId = environments.attributeValue("default");
                //(4)获取具体的环境
                Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultId + "']");
                //(5)获取子节点 事务管理器和数据源
                Element transactionElt = (Element) environment.element("transactionManager");
                Element dataSourceElt = (Element) environment.element("dataSource");
                //(6)创建JDBC事务管理器的时候需要传入数据源
                DataSource dataSource = getDataSource(dataSourceElt);
                //获取事务管理器
                Transaction transaction = getTransaction(transactionElt, dataSource);
                //创建一个集合,保存所有的Mapper.xml的路径
                List<Node> nodes = document.selectNodes("//mapper"); // "//"代表找到所有的mapper标签,而不是定位到mappers的位置
                List<String> sqlMapperXMLPathList = new ArrayList<>();
                //遍历nodes,获取其resource属性
                nodes.forEach(node -> {
                    Element mapper = (Element) node;
                    String resource = mapper.attributeValue("resource");
                    sqlMapperXMLPathList.add(resource);
                });
    
                //获取mapperedStatement [封装的SQl语句]
                Map<String, MapperdStatement> mappedStatements = getMappedStatements(sqlMapperXMLPathList);
                //解析完成我们返回SqlSessionFactory
                factory = new SqlSessionFactory(transaction, mappedStatements);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return factory;
        }
    
        private Map<String, MapperdStatement> getMappedStatements(List<String> sqlMapperXMLPathList) {
            //结果封装 key 为SQL的id,value 为resultType和SQL语句
            Map<String, MapperdStatement> mapperdStatements = new HashMap<>();
            //根据我们获得的mapper标签,获得每个Mapper文件的内容
            sqlMapperXMLPathList.forEach(sqlMapperXMLPath ->{
                try{
                    //解析每个xml文件
                    SAXReader reader = new SAXReader();
                    Document document = reader.read(Resources.getResourceAsStream(sqlMapperXMLPath));
                    Element mapper = (Element) document.selectSingleNode("mapper");
                    String namespace = mapper.attributeValue("namespace");
                    //获得所有SQL语句
                    List<Element> elements = mapper.elements();
                    elements.forEach(element -> {
                        String id = element.attributeValue("id");
                        //拼接namespace+id
                        String sqlId = namespace + "." +  id;
                        String resultType = element.attributeValue("resultType");
                        String sql = element.getTextTrim();
                        MapperdStatement mapperdStatement = new MapperdStatement(sql, resultType);
                        mapperdStatements.put(sqlId, mapperdStatement);
    
                    });
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
            return mapperdStatements;
        }
    
    
        private Transaction getTransaction(Element transactionElt, DataSource dataSource) {
            //事务管理器的类型
            String type = transactionElt.attributeValue("type").trim().toUpperCase();
            //创建事务管理器的对象
            Transaction transaction = null;
            //选择使用哪种事务管理器
            if(Const.JDBC_TRANSACTION.equals(type)){
                transaction = new JdbcTransaction(dataSource, false);
            }else{
                transaction = new ManagedTransaction();
            }
    
            return transaction;
        }
    
        /**
         * 获取数据源对象
         *
         * @param dataSourceElt 数据源跟标签
         * @return
         */
        private DataSource getDataSource(Element dataSourceElt) {
            //利用集合存储DataSource子节点的name和value
            Map<String, String> map = new HashMap<>();
            //获取DataSource节点的所有子节点
            List<Element> propertyElts = dataSourceElt.elements();
            //遍历所有子节点,记录元素值
            propertyElts.forEach(e -> {
                String name = e.attributeValue("name");
                String value = e.attributeValue("value");
                map.put(name, value);
            });
            //定义数据源的对象
            DataSource dataSource = null;
            //获取数据源类型[使用哪种连接池]
            String type = dataSourceElt.attributeValue("type").trim().toUpperCase();
            //根据不同的类型选择不同的操作[需要传入参数]
            if (Const.UN_POOLED_DATASOURCE.equals(type)) {
                dataSource = new UnPooledDataSource(map.get("driver"), map.get("url"), map.get("username"), map.get("password"));
            }
            if (Const.POOLED_DATASOURCE.equals(type)) {
                dataSource = new PooledDataSource();
            }
            if (Const.JNDI_DATASOURCE.equals(type)) {
                dataSource = new JNDIDataSource();
            }
    
            return dataSource;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152

    (3)为SqlSessionFactory类添加openSession方法,用于开启会话

    public SqlSession openSession(){
            //利用事务管理器的openSession方法来开启连接,这样确保会话不为空
            transaction.openConnection();
            //创建SqlSession的对象 [此处进行了修改2]
            SqlSession sqlSession = new SqlSession(this);
            return sqlSession;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4、为了实现具体的功能,我们提供插入数据和根据id查询数据的功能【为了方便,我们将所有字段都定义为字符串类型】

    • 在SqlSession类中提供 insertselectOne 方法

    (1)insert 方法的代码如下:【大部分都标注了注释】

    public int insert(String sqlId, Object pojo){
            //操作影响数据库表记录的条数
            int count = 0;
            try {
                //获取连接对象
                Connection connection = factory.getTransaction().getConnection();
                //获取我们封装的SQL语句,此时是使用#{}作为占位符的
                String godbatisSql = factory.getMapperdStatements().get(sqlId).getSql();
                //将sql语句改为JDBC的格式
                String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$]*}", "?");
                PreparedStatement ps = connection.prepareStatement(sql);
                //重点在于如何动态的给占位符传值
                //为了简化操作,该框架应用的数据库表的字段都是varchar类型
                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;
                    //如何获取属性值?[分为三部分:get + 字段首字母大写 + 字段剩余部分]
                    String getMethodName = "get" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
                    //
                    Method getMethod = pojo.getClass().getDeclaredMethod(getMethodName);
                    Object propertyValue = getMethod.invoke(pojo);
                    ps.setString(index, propertyValue.toString());
                    //更新索引
                    index++;
    
                }
                //执行SQL语句
                count = ps.executeUpdate();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return count;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    (2)selectOne 的代码块如下:

    public Object selectOne(String sqlId, Object param){
            Object obj = null;
            try {
                //获取连接对象
                Connection connection = factory.getTransaction().getConnection();
                //获取我们封装的SQL集合,key--sqlId、value--resultType和SQL语句
                MapperdStatement mapperdStatement = factory.getMapperdStatements().get(sqlId);
                //获取sql语句和结果集的类型
                String godbatisSql = mapperdStatement.getSql();
                String resultType = mapperdStatement.getResultType();
                //占位符替换
                String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-_$]*}", "?");
                //参数传递
                PreparedStatement ps = connection.prepareStatement(sql);;
                //此时占位符只有一个
                ps.setString(1,param.toString());
                ResultSet rs = ps.executeQuery();
    
                //从结果集中取数据封装成Java对象
                if(rs.next()){
                    // 获取resultType的Class
                    Class<?> resultTypeClass = Class.forName(resultType);
                    //调用无参构造方法创建对象
                    obj = resultTypeClass.newInstance();
                    //给User类的属性赋值
                    ResultSetMetaData rsmd = rs.getMetaData();
                    //列数
                    int columnCount = rsmd.getColumnCount();
                    //依次赋值
                    for (int i = 0; i < columnCount; i++) {
                        //获取第i个列名
                        String propertyName = rsmd.getColumnName(i + 1);
                        //拼接Set方法名
                        String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
                        //获取set方法
                        Method setMethod = resultTypeClass.getDeclaredMethod(setMethodName, String.class);
                        //调用set方法给obj传值
                        setMethod.invoke(obj, rs.getString(propertyName));
    
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    
            return obj;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    (3)所有的代码都经过了测试,文末会提供框架的 jar


    三、项目打包和测试

    1、将手写的这个框架打包

    • 因为我用的是idea,所有可以一键打包
    • 右侧点击Maven >> 找到项目 >> Lifecycle >> install
      在这里插入图片描述
    • 打包后会存储到我们的本地仓库中
      在这里插入图片描述
      2、框架测试

    (1)创建了一个新的模块 godbatis-test 用于功能测试
    (2)修改 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>com.powernode</groupId>
        <artifactId>godbatis-test</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
        <dependencies>
            <dependency>
                <groupId>org.god.ibatis</groupId>
                <artifactId>godbatis</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <properties>
            <maven.compiler.source>17</maven.compiler.source>
            <maven.compiler.target>17</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
    </project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    没有导入mysql驱动等,因为在原框架中有依赖关系自动就下载了,所以我们此处只额外添加了 Junit 的依赖。
    在这里插入图片描述

    • 那么我们在框架中也导入了 junit 依赖,为什么此处还需要重新导入呢?
      在这里插入图片描述

    因为 junitscopetest,在打包的时候就不会将其打包进来【test 代表作用范围为测试】

    (3)添加我们的核心配置文件映射文件

    • godbatis-config.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <configuration>
        <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/powernode"/>
                    <property name="username" value="root"/>
                    <property name="password" value="111111"/>
                </dataSource>
            </environment>
    
            <!--添加一个新的环境-->
            <environment id="mybatisDB">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.myql.cj.hdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/shop"/>
                    <property name="username" value="root"/>
                    <property name="password" value="111111"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <!--sql映射文件创建好之后,需要将该文件路径配置到这里-->
            <mapper resource="UserMapper.xml"/>
        </mappers>
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • UserMapper.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="user">
        <insert id="insertUser">
            insert into t_user values (#{id}, #{name}, #{age})
        </insert>
        <select id="selectById" resultType="cn.User">
            select id, name, age from t_user where id = #{id}
        </select>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    (4)编写我们的pojo类,利用普通Java类作为数据库表查询结果的返回值类型

    package cn;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    public class User {
        private String id;
        private String name;
        private String age;
    
        public User(){}
        public User(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;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    (5)编写测试类 UserMapperTest

    package com.powernode.godbatis.test;
    
    import cn.User;
    import org.god.ibatis.core.SqlSession;
    import org.god.ibatis.core.SqlSessionFactoryBuilder;
    import org.god.ibatis.utiils.Resources;
    import org.junit.Test;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    public class UserMapperTest {
        @Test
        public void testInsertUser(){
            //创建会话
            SqlSession sqlSession = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("godbatis-config.xml")).openSession();
            //执行SQL
            User user = new User("111", "孙悟空", "20");
            sqlSession.insert("user.insertUser", user);
            //提交事务、关闭连接
            sqlSession.commit();
            sqlSession.close();
    
        }
        @Test
        public void testSelectOne(){
            //创建会话
            SqlSession sqlSession = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("godbatis-config.xml")).openSession();
            //执行SQL
            Object user = sqlSession.selectOne("user.selectById", 1);
            System.out.println(user);
            //关闭连接
            sqlSession.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 对于这两个方法我们分开进行测试【使用了junit进行单元测试】
    • 测试插入方法,原数据库中只有一条记录

    在这里插入图片描述
    运行结果,孙悟空的信息成功添加进去
    在这里插入图片描述

    • 测试根据id查询数据,我们就查id为1的用户,查询结果如下

    在这里插入图片描述
    点击下载godbatis的jar包(阿里云盘): godbatis-1.0-SNAPSHOT.jar

  • 相关阅读:
    UACANet: Uncertainty Augmented Context Attention for Polyp Segmentation
    Echarts柱状图格式化Label加单位
    7、DVWA——SQL盲注
    编程每日一练(多语言实现)基础篇:百元买百鸡
    Clickhouse表引擎—日志系列引擎
    ArcGIS Pro和ArcGIS有什么区别和联系,优势有哪些?
    笨方法自学python(一)
    对Transformer的一些理解
    面试题--基础篇
    界面控件DevExpress WPF地图组件,助力轻松搭建自己的地图数据服务器!
  • 原文地址:https://blog.csdn.net/qq_61323055/article/details/127607059