• (四)⼿写简单版MyBatis框架


    Mybatis学习目录

    上一篇:(三)Mybatis中的传值
    下一篇:(五)在WEB中应用MyBatis(使用MVC架构模式)

    环境搭建

    首先我们用mybatis的时候有很多XML文件,有核心配置文件和映射文件等等,我们需要解析这些XML文件,可以使用dom4j解析XML⽂件

    IDEA中创建Maven模块:我的模块名叫My_mybatis
    请添加图片描述

    dom4j可以参考:https://blog.csdn.net/weixin_45832694/article/details/127536145?spm=1001.2014.3001.5502
    引入dom4j依赖和mysql驱动依赖,为了测试方便还需要引入junit依赖

        <!--dem4j依赖-->
        <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>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.13.2</version>
          <scope>test</scope>
        </dependency>
        <!--mysql依赖-->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.30</version>
        </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    ⼿写框架之前,我们不知道从哪入手,可以参考之前的mybatis程序

    public class MybatisIntroductionTest {
        public static void main(String[] args) throws IOException {
            // 获取SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    
            // 获取SqlSessionFactory对象
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); // Resources.getResourceAsStream默认就是从类的根路径下开始查找资源。
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); // 一般情况下都是一个数据库对应一个SqlSessionFactory对象。
    
            // 获取SqlSession对象
            SqlSession sqlSession = sqlSessionFactory.openSession(); // 如果使用的事务管理器是JDBC的话,底层实际上会执行:conn.setAutoCommit(false);
    
            // 执行SQL语句,这里insert里的值是XXXMapper.xml
            int count = sqlSession.insert("insertCar"); // 返回值是影响数据库表当中的记录条数。
    
            System.out.println("插入了几条记录:" + count);
    
            // 手动提交
            sqlSession.commit(); // 如果使用的事务管理器是JDBC的话,底层实际上还是会执行conn.commit();
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    第一步:资源⼯具类

    可以看出Resources.getResourceAsStream,Resources是一个工具类,调用静态方法getResourceAsStream,自定义一个工具类MyResouces

    /**
     * 提供一个工具类,完成类路径的加载
     */
    public class MyResouces {
        private MyResouces(){//工具类构造方法私有化
        }
    
        /**
         * 从类路径加载资源
         * @param resource 类路径当中的文件名
         * @return 指向资源文件的InputStream输入流
         */
        public static InputStream getMyResourceAsStream(String resource){
            return ClassLoader.getSystemClassLoader().getResourceAsStream(resource);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    第二步:定义SqlSessionFactoryBuilder类

    参考测试程序,知道要获得SqlSession对象需要创建SqlSessionFactory,而SqlSessionFactory又是通过SqlSessionFactoryBuilder的build方法返回,所以需要定义一个MySqlSessionFactoryBuilder类,提供一个build方法,返回MySqlSessionFactory对象

    /**
     * MySqlSessionFactory构建器对象,通过bulid方法解析Mybatis的XML配置文件,创建SqlSessionFactory
     */
    public class MySqlSessionFactoryBuilder {
    
        public MySqlSessionFactoryBuilder() {
        }
        /**
         * 解析mybatis的XML配置文件,来构建MySqlSessionFactory对象
         * @param is 指向配置文件的一个输入流
         * @return MySqlSessionFactory
         */
        public MySqlSessionFactory bulid(InputStream is){
      
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    第三步:定义SqlSessionFactory类

    在bulid这个方法中我们需要解析XML文件,而mybatis中的核心配置文件的基本信息有数据源、事务管理器、SQL映射。
    事务管理需要用到Connection,而Connection是从配置文件dataSource标签来的,所以在JDBC事务管理器需要数据源属性
    所以SqlSessionFactory这个类只需要两个属性一个是事务管理器属性和SQL映射属性
    SQL映射在配置文件中可能有多个,所以需要一个集合进行存储。

    /**
     * MySqlSessionFactory对象:
     *          一般一个数据库对应一个MySqlSessionFactory
     *          通过MySqlSessionFactory获取SqlSession,开启会话
     *          一个MySqlSessionFactory对象能够开启多个SqlSession会话
     */
    public class MySqlSessionFactory {
        /**
         * 事务管理器属性,对应XML配置文件的transactionManager标签
         */
        private MyTransaction transactionManager;
        /**
         * 存放Sql语句的Map集合。
         * key存SQL-id
         * value存对应的SQL标签对象
         * MyMappedStatement是SQL映射的实体类
         */
        private Map<String ,MyMappedStatement> mappedStatementMap;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    第四步:定义JDBC事务管理器

    mybatis的事务管理有两种:JDBC事务管理器和MANAGED事务管理器
    需要可以随意切换,需要定义一个事务管理器接口,让JDBC事务管理器和MANAGED事务管理器去实现这个接口,实现里面的方法就行了,这里我们只实现JDBC事务管理器
    定义⼀个接⼝,然后每⼀个具体的事务管理器都实现这个接⼝。

    /**
     * 事务管理器接口。提供管理事务的方法
     * 所有事务管理器都应该实现这个接口
     */
    public interface MyTransaction {
        /**
         * 提交事务
         */
        void commit();
    
        /**
         * 回滚事务
         */
        void rollback();
    
        /**
         * 关闭事务
         */
        void close();
    
        /**
         * 真正开启数据库连接
         */
        void openConnention();
        /**
         * 获取Connention
         */
        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
    /**
     * JDBC事务管理器
     */
    public class JdbcTransaction implements MyTransaction{
        /**
         * 事务管理需要用到Connection,而Connection是从配置文件dataSource标签来的,所以需要数据源属性
         * 数据源属性,对应XML配置文件的dataSource标签
         */
        private DataSource dataSource;
        /**
         * 自动提交标志
         * true表示自动提交
         * false表示不采用自动提交
         */
        private boolean autoCommit;
        private Connection connection;
        @Override
        public Connection getConnection() {
            return connection;
        }
    
        public JdbcTransaction() {
        }
    
        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 openConnention() {
            if (connection == null) {
                try {
                    connection = dataSource.getConnection();
                    connection.setAutoCommit(autoCommit);
                } catch (SQLException 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

    第五步:定义数据源类

    mybatis的数据源有三种:UNPOOLED、POOLED以及JNDI
    这里只需实现UNPOOLED
    实现数据源需要去实现DataSource接口,并实现里面的方法

    /**
     * 数据源实现类:UNPOOLED
     * 不使用连接池,每一次都新建Connection对象。
     */
    public class UnPooledDataSource implements DataSource {
        private String url;
        private String username;
        private String password;
    
        public UnPooledDataSource() {
        }
    
        /**
         * 创建一个数据源对象,通过构造方法注册驱动
         * @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) {
                e.printStackTrace();
            }
            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

    第六步:定义MyMappedStatement类

    /**
     * 只是一个普通的java类,封装了SQL标签
     * 一个MyMappedStatement对象对应一个SQL标签
     */
    public class MyMappedStatement {
        private String sql;//对应SQL配置文件的SQL语句
        /**
         * 映射结果集类型,只有select标签有这个属性,其他标签没有是null,对应SQL配置文件的resultType属性
         */
        private String resultType;
    
        public MyMappedStatement() {
        }
    
        public MyMappedStatement(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 "MyMappedStatement{" +
                    "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

    第七步:完善SqlSessionFactory类

    /**
     * MySqlSessionFactory对象:
     *          一般一个数据库对应一个MySqlSessionFactory
     *          通过MySqlSessionFactory获取SqlSession,开启会话
     *          一个MySqlSessionFactory对象能够开启多个SqlSession会话
     */
    public class MySqlSessionFactory {
        /**
         * 事务管理器属性,对应XML配置文件的transactionManager标签
         */
        private MyTransaction transactionManager;
        /**
         * 存放Sql语句的Map集合。
         * key存SQL-id
         * value存对应的SQL标签对象
         */
        private Map<String ,MyMappedStatement> mappedStatementMap;
    
        public MySqlSessionFactory() {
        }
    
        public MySqlSessionFactory(MyTransaction transactionManager, Map<String, MyMappedStatement> mappedStatementMap) {
            this.transactionManager = transactionManager;
            this.mappedStatementMap = mappedStatementMap;
        }
    
        public MyTransaction getTransactionManager() {
            return transactionManager;
        }
    
        public void setTransactionManager(MyTransaction transactionManager) {
            this.transactionManager = transactionManager;
        }
    
        public Map<String, MyMappedStatement> getMappedStatementMap() {
            return mappedStatementMap;
        }
    
        public void setMappedStatementMap(Map<String, MyMappedStatement> mappedStatementMap) {
            this.mappedStatementMap = mappedStatementMap;
        }
    
    • 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

    第八步:完善SqlSessionFactoryBuilder中的build⽅法

    开始解析配置文件
    1.使用Dom4j创建reader对象
    2.通过类加载器获取Myabtis的配置文件输入流,这个已经在工具类封装了,而且是通过参数传过来了。
    3.读XML文件,返回一个文档对象document
    4.对标签进行处理
    4.1获取默认的环境,因为将来有可能配置多个环境
    (1)获取environments的default的值
    (2)通过defalt获取默认environment
    4.2处理默认环境的配置
    (1)获取事务管理器标签
    (2)获取数据源标签
    (3)对数据源和事务管理器进行处理,创建数据源对象和事务管理器对象
    5.获取所有的Mapper标签,进行处理,构建map集合
    6.最后创建出一个MySqlSessionFactory对象

    /**
     * MySqlSessionFactory构建器对象,通过bulid方法解析Mybatis的XML配置文件,创建SqlSessionFactory
     */
    public class MySqlSessionFactoryBuilder {
    
        public MySqlSessionFactoryBuilder() {
        }
        /**
         * 解析mybatis的XML配置文件,来构建MySqlSessionFactory对象
         * @param is 指向配置文件的一个输入流
         * @return MySqlSessionFactory
         */
        public MySqlSessionFactory bulid(InputStream is){
            MySqlSessionFactory factory = null;
            MyTransaction transactionManager = null;
            Map<String ,MyMappedStatement> mappedStatementMap = null;
            DataSource dataSource = null;
            try {
                //1.使用Dom4j创建reader对象
                SAXReader reader = new SAXReader();
                //2.通过类加载器获取Myabtis的配置文件输入流,这个已经在工具类封装了
                //3.读XML文件,返回一个文档对象document
                Document document = reader.read(is);
                //4.对标签进行处理
                //4.1获取默认的环境,因为将来有可能配置多个环境
                //(1)获取environments的default的值
                //通过标签匹配路径获取节点,这样就获取到了environments这个节点,返回是Node,Element是Node的子类,方法更多,使用更方便
                Element environments = ((Element) document.selectSingleNode("/configuration/environments"));
                //通过attributeValue获取default的值
                String defalt = environments.attributeValue("default");
                //(2)通过defalt获取默认environment
                Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='"+defalt+"']");
    
                //4.2处理默认环境的配置
                //(1)获取事务管理器标签
                //获取environment下的transactionManager标签,element用来获取孩子节点
                Element transactionManagerElt = environment.element("transactionManager");
                //获取事务管理器的类型
                //String transactionType = transactionManagerElt.attributeValue("type");
    
                //(2)获取数据源标签
                Element dataSourceElt = (Element) environment.element("dataSource");
                //String dataSourceType = dataSourceElt.attributeValue("type");
                //(3)对数据源和事务管理器进行处理
                //通过数据源标签获取数据源
                dataSource = getDataSource(dataSourceElt);
                //通过数据源和事务管理器标签,获取事务管理器
                transactionManager = getTransactionManager(transactionManagerElt,dataSource);
    
    
                //5.获取所有的Mapper标签,进行处理
                List<Node> mappers = document.selectNodes("//mapper");
    
                //通过mapper标签,处理射信息
                mappedStatementMap = getMappedStatement(mappers);
    
    
    
                factory = new MySqlSessionFactory(transactionManager,mappedStatementMap);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return factory;
        }
    
        /**
         * 解析所有的SQLMapper.xml文件,然后构建map集合
         * @param mappers 所有mapper的List集合
         * @return
         */
        private Map<String, MyMappedStatement> getMappedStatement(List<Node> mappers) {
            Map<String, MyMappedStatement> mappedStatementMap = new HashMap<>();
            mappers.forEach(mapper -> {
                Element mapperElt = (Element) mapper;
                //获取resource映射路径
                String resource = mapperElt.attributeValue("resource");
                //通过映射路径解析SQLMapper.xml文件
                SAXReader reader = new SAXReader();
                try {
                    Document document = reader.read(MyResouces.getMyResourceAsStream(resource));
                    Element SQLmapper = ((Element) document.selectSingleNode("mapper"));
                    String namespace = SQLmapper.attributeValue("namespace");
                    List<Element> elements = SQLmapper.elements();
                    elements.forEach(element -> {
                        String id = element.attributeValue("id");
                        String sqlid = namespace + "." + id;
                        String resultType = element.attributeValue("resultType");
                        //获取文本,去掉前后空格
                        String sql = element.getTextTrim();
    
                        MyMappedStatement myMappedStatement = new MyMappedStatement(sql,resultType);
                        mappedStatementMap.put(sqlid,myMappedStatement);
                    });
                } catch (DocumentException e) {
                    e.printStackTrace();
                }
            });
            return mappedStatementMap;
        }
    
        /**
         * 获取数据源对象
         * @param dataSourceElt 数据源标签
         * @return
         */
        private DataSource getDataSource(Element dataSourceElt) {
            DataSource dataSource = null;
            Map<String,String> map = new HashMap<>();
            List<Element> propertys = dataSourceElt.elements();
            propertys.forEach(propertysElt -> {
                String name = propertysElt.attributeValue("name");
                String value = propertysElt.attributeValue("value");
                map.put(name,value);
            });
            //获取数据源的类型,有可能是小写,所以要把它统一变成大写,trim()去掉前后空格
            String dataSourceType = dataSourceElt.attributeValue("type").trim().toUpperCase();
            if (Const.UN_POOLED_DATASOURCE.equals(dataSourceType)) {//UNPOOLED
                dataSource = new UnPooledDataSource(map.get("driver"),map.get("url"),map.get("username"),map.get("password"));
            }else if (Const.POOLED_DATASOURCE.equals(dataSourceType)){//POOLED
                dataSource = new PooledDataSource();
            }else if (Const.JNDI_DATASOURCE.equals(dataSourceType)){//JNDI
                dataSource = new JNDIDataSource();
            }
            return dataSource;
        }
    
        /**
         * 获取事务管理器对象
         * @param transactionManagerElt 事务管理器标签
         * @param dataSource 数据源对象
         * @return
         */
        private MyTransaction getTransactionManager(Element transactionManagerElt, DataSource dataSource) {
            MyTransaction transaction = null;
            String transactionType = transactionManagerElt.attributeValue("type");
            if (Const.JDBC_TRANSACTION.equals(transactionType)) {//JDBC,默认开启事务
                transaction = new JdbcTransaction(dataSource,false);
            }else if (Const.MANAGED_TRANSACTION.equals(transactionType)){//MANAGED
                transaction = new ManagedTransaction();
            }
            return transaction;
        }
    }
    
    
    • 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

    第九步:添加openSession⽅法获取会话对象

    /**
         * 获取SQL会话对象
         * @return
         */
        public MySqlSession openSession(){
            //打开连接
            transactionManager.openConnention();
            MySqlSession sqlSession = new MySqlSession(this);
            return sqlSession;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第十步:创建会话类MySqlSession

    这里只提供两个方法insert和selectOne

    /**
     * 专门负责执行SQL语句的会话对象
     */
    public class MySqlSession {
        MySqlSessionFactory factory;
    
        public MySqlSession() {
        }
    
        public MySqlSession(MySqlSessionFactory factory) {
            this.factory = factory;
        }
        //以下就是JDBC代码了,这里只写insert和selectOne
    
        /**
         * 执行insert语句,向数据库表当中插入记录
         * @param sqlid xml的要执行sql的id
         * @param pojo 插入的数据
         * @return
         */
        public int insert(String sqlid,Object pojo){
            int count = 0;
            try {
                Connection connection = factory.getTransactionManager().getConnection();
                String sql = factory.getMappedStatementMap().get(sqlid).getSql();
                //把#{}变成占位符?
                String newsql = sql.replaceAll("#\\{[0-9A-Za-z_$]*}", "?");
                PreparedStatement ps = connection.prepareStatement(newsql);
                int fromIndex = 0;
                int index = 1;
                while (true) {
                    int jingIndex = sql.indexOf("#", fromIndex);
                    if (jingIndex < 0) {
                        break;
                    }
                    int youkuohaoIndex = sql.indexOf("}", fromIndex);
                    String properName = sql.substring(jingIndex + 2, youkuohaoIndex).trim();
                    fromIndex = youkuohaoIndex + 1;
                    //调用对应的get的方法
                    String getMethodName = "get" + properName.toUpperCase().charAt(0) + properName.substring(1);
                    Method getMethod = pojo.getClass().getDeclaredMethod(getMethodName);
                    Object properValue = getMethod.invoke(pojo);
                    ps.setString(index,properValue.toString());
                    index++;
                }
                count = ps.executeUpdate();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return count;
        }
    
    
        /**
         * 执行查询语句,返回一个对象。该方法只适合返回一条记录的sql语句。
         * @param sqlId
         * @param param
         * @return
         */
        public Object selectOne(String sqlId, Object param){
            Object obj = null;
            try {
                Connection connection = factory.getTransactionManager().getConnection();
                MyMappedStatement mappedStatement = factory.getMappedStatementMap().get(sqlId);
                String sql = mappedStatement.getSql();
                String newsql = sql.replaceAll("#\\{[a-zA-Z0-9_$]*}", "?");
                PreparedStatement ps = connection.prepareStatement(newsql);
                // 给占位符传值
                ps.setString(1, param.toString());
                // 查询返回结果集
                ResultSet rs = ps.executeQuery();
                // 要封装的结果类型。
                String resultType = mappedStatement.getResultType();
                // 从结果集中取数据,封装java对象
                if (rs.next()) {
                    // 获取resultType的Class
                    Class<?> resultTypeClass = Class.forName(resultType);
                    // 调用无参数构造方法创建对象
                    obj = resultTypeClass.newInstance(); // Object obj = new User();
                    /*
                    解决问题的关键:将查询结果的列名作为属性名。
                    列名是id,那么属性名就是:id
                    列名是name,那么属性名就是:name
                     */
                    ResultSetMetaData rsmd = rs.getMetaData();
                    int columnCount = rsmd.getColumnCount();
                    for (int i = 0; i < columnCount; i++) {
                        String propertyName = rsmd.getColumnName(i + 1);
                        // 拼接方法名
                        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;
        }
    
    
        public void commit(){
            factory.getTransactionManager().commit();
        }
        public void rollback(){
            factory.getTransactionManager().rollback();
        }
        public void close(){
            factory.getTransactionManager().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
    • 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

    测试

    数据库创建一个表,这个表只能是Varchar类型,因为手写的这个Mybatis框架没有进行自动类型的推断,对传过来的数据统一看作是字符串处理

    请添加图片描述
    配置文件

    <?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="development">
            <environment id="development">
            	<!--这里只能是JDBC,其他没有实现-->
                <transactionManager type="JDBC"/>
                <!--这里只能是UNPOOLED,其他没有实现-->
                <dataSource type="UNPOOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mydbtest"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <mapper resource="DeptMapper.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

    DeptMapper.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="dept">
        <!--id是这条语句的唯一标识。这个id就代表了这条SQL语句-->
        <insert id="insertDept">
            insert into t_mybatis
            value(#{deptno},#{dname},#{loc})
        </insert>
        <select id="selectById" resultType="com.pojo.Dept">
            select * from t_mybatis where deptno=#{deptno}
        </select>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    实体类

    public class Dept {
        //private  deptno;
        private String deptno;
        private String dname;
        private String loc;
    
        public Dept() {
        }
    
        public Dept(String deptno, String dname, String loc) {
            this.deptno = deptno;
            this.dname = dname;
            this.loc = loc;
        }
    /* public Integer getDeptno() {
            return this.deptno;
        }*/
    
        /*public void setDeptno(Integer deptno) {
            this.deptno = deptno;
        }*/
    
        @Override
        public String toString() {
            return "Dept{" +
                    "deptno='" + deptno + '\'' +
                    ", dname='" + dname + '\'' +
                    ", loc='" + loc + '\'' +
                    '}';
        }
    
        public String getDeptno() {
            return deptno;
        }
    
        public void setDeptno(String deptno) {
            this.deptno = deptno;
        }
    
        public String getDname() {
            return dname;
        }
    
        public void setDname(String dname) {
            this.dname = dname;
        }
    
        public String getLoc() {
            return loc;
        }
    
        public void setLoc(String loc) {
            this.loc = loc;
        }
    }
    
    
    • 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

    测试程序

        @Test
        public void testInsert(){
            MySqlSessionFactoryBuilder sqlSessionFactoryBuilder = new MySqlSessionFactoryBuilder();
            MySqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.bulid(MyResouces.getMyResourceAsStream("mybatis-config.xml"));
            MySqlSession sqlSession = sqlSessionFactory.openSession();
            Dept dept = new Dept("10","销售部","北京");
            sqlSession.insert("dept.insertDept",dept);
            sqlSession.commit();
            sqlSession.close();
        }
        @Test
        public void testSelectOne(){
            MySqlSessionFactoryBuilder sqlSessionFactoryBuilder = new MySqlSessionFactoryBuilder();
            MySqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.bulid(MyResouces.getMyResourceAsStream("mybatis-config.xml"));
            MySqlSession sqlSession = sqlSessionFactory.openSession();
            Object o = sqlSession.selectOne("dept.selectById", "10");
            System.out.println(o);
            sqlSession.commit();
            sqlSession.close();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    insert测试,测试成功通过
    请添加图片描述
    数据库数据插入成功
    请添加图片描述
    selectOne测试,测试成功,显示数据
    请添加图片描述

  • 相关阅读:
    ThreadLocal源码解析以及常见面试题
    leetCode 214.最短回文串 + KMP
    SpringBoot入门
    Spark基础【RDD行动算子、序列化】
    多模态大模型Claude 3正式接入集简云与语聚!对标GPT-4且支持中文
    R语言ggplot2可视化相关系数图(correlation analysis):通过数据点的大小以及颜色(双色渐变填充)表征相关性的强度
    MOSFET器件手册关键参数解读
    让历史文化“活”起来,北京河图“万象中轴”助力打造北京城市金名片
    Qt程序打包成安装包exe
    文件管理命令
  • 原文地址:https://blog.csdn.net/weixin_45832694/article/details/127588766