【手写Mybatis】step04:数据源的解析、事务和sql执行

一、设计

建立数据源连接池Druid和JDBC事务操作,以xml为信息的接入口,在XMLConfigBuilder中解析,向配置类 configuration添加 JDBC 操作环境信息。DefaultSqlSession执行sql语句并返回输出结果。

在这里插入图片描述

parse解析xml中 DB配置信息,并且把配置信息里面的连接池和事务工厂到配置信息下Environment
上一节selectone打印sql语句信息,而现在是DB 执行sql语句,并把结果包装输出

二、实现

在这里插入图片描述
以事务Transaction,TransactionFactory事务工厂接口的实现,DataSourceFactory数据源接口实现为DruidDataSourceFactory。阿里Druid连接池做db的链接

SqlSessionFactoryBuilder 是作为mybatis的入口,通过xml文件的io解析,并且返回处理

通过解析把 XML 信息注册到 Configuration 配置类中,其中environment环境配置由事务工厂和数据源驱动组合而成,再把environment通过传递 Configuration 配置类中,最后 DefaultSqlSession 中的selectOne方法链接数据驱动,执行sql并返回输出包装结果。

2.1 代码结构


mybatis-step-03
└── src
    ├── main
    │   └── java
    │       └── com.qf
    │           ├── binding
    │           │   ├── MapperMethod.java
    │           │   ├── MapperProxy.java
    │           │   ├── MapperProxyFactory.java
    │           │   └── MapperRegistry.java
    │           ├── builder
    │           │   ├── xml
    │           │   │   └── XMLConfigBuilder.java
    │           │   └── BaseBuilder.java
    │           ├── datasource
    │           │   ├── druid
    │           │   │   └── DruidDataSourceFactory.java
    │           │   └── DataSourceFactory.java
    │           ├── io
    │           │   └── Resources.java
    │           ├── mapping
    │           │   ├── BoundSql.java
    │           │   ├── Environment.java
    │           │   ├── MappedStatement.java
    │           │   ├── ParameterMapping.java
    │           │   └── SqlCommandType.java
    │           └── session
    │               ├── defaults
    │               │   ├── DefaultSqlSession.java
    │               │   └── DefaultSqlSessionFactory.java
    │               ├── Configuration.java
    │               ├── SqlSession.java
    │               ├── SqlSessionFactory.java
    │               └── SqlSessionFactoryBuilder.java
    │           ├── transaction
    │           │   ├── jdbc
    │           │   │   ├── JdbcTransaction.java
    │           │   │   └── JdbcTransactionFactory.java
    │           │   ├── Transaction.java
    │           │   └── TransactionFactory.java
    │           └── type
    │               ├── JdbcType.java
    │               └── TypeAliasRegistry.java
    └── test
        ├── java
        │   └── com.qf
        │       ├── dao
        │       │   └── IUserDao.java
        │       ├── po
        │       │   └── User.java
        │       └── ApiTest.java
        └── resources
            ├── mapper
            │   └──User_Mapper.xml
            └── mybatis-config-datasource.xml


2.2 事务接口Transaction


package com.qf.mybatis.transaction;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 事务接口
 */
public interface Transaction {

    /**
     * 数据库连接
     * @return
     * @throws SQLException
     */
    Connection getConnection() throws SQLException;

    /**
     *事务提交
     * @throws SQLException
     */
    void commit() throws SQLException;

    /**
     *事务回滚
     */
    void rollback() throws SQLException;

    /**
     * 事务关闭
     * @throws SQLException
     */
    void close() throws SQLException;
}



Transaction 事务的接口,包括数据库连接,事务回滚、提交和关闭

package com.qf.mybatis.transaction.jdbc;

import com.qf.mybatis.session.TransactionIsolationLevel;
import com.qf.mybatis.transaction.Transaction;

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

public class JDBCTransaction implements Transaction {

    protected Connection connection;
    protected DataSource dataSource;
    protected TransactionIsolationLevel level=TransactionIsolationLevel.NONE;
    protected  boolean autoCommit;

    public JDBCTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit){
        this.dataSource=dataSource;
        this.level=level;
        this.autoCommit=autoCommit;
    }
    public JDBCTransaction(Connection connection){
        this.connection=connection;
    }

    @Override
    public Connection getConnection() throws SQLException {
        connection=dataSource.getConnection();
        connection.setAutoCommit(autoCommit);
        connection.setTransactionIsolation(level.getLevel());
        return connection;
    }

    @Override
    public void commit() throws SQLException {
        if (connection!=null&&!connection.getAutoCommit()){
                connection.commit();
        }
    }

    @Override
    public void rollback() throws SQLException {
        if (connection!=null&&!connection.getAutoCommit()){
            connection.rollback();
        }
    }

    @Override
    public void close() throws SQLException {
        if (connection!=null&&!connection.getAutoCommit()){
            connection.close();
        }
    }
}

JDBCTransaction :JDBC事务实现类,获取连接,提交事务,提供了JDBC的基本能力

2.3 事务工厂TransactionFactory

package com.qf.mybatis.transaction;

import com.qf.mybatis.session.TransactionIsolationLevel;

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

/**
 * 事务工厂
 */
public interface TransactionFactory {

    Transaction newTransaction(Connection conn);

    Transaction newTransaction(DataSource conn, TransactionIsolationLevel level, boolean autoCommit);
}



事务工厂:工厂模式设计,创建事务

package com.qf.mybatis.transaction.jdbc;

import com.qf.mybatis.session.TransactionIsolationLevel;
import com.qf.mybatis.transaction.Transaction;
import com.qf.mybatis.transaction.TransactionFactory;

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

public class JDBCTransactionFactory implements TransactionFactory {
    @Override
    public Transaction newTransaction(Connection conn) {
        return new JDBCTransaction(conn);
    }

    @Override
    public Transaction newTransaction(DataSource conn, TransactionIsolationLevel level, boolean autoCommit) {
        return new JDBCTransaction(conn,level,autoCommit);
    }
}

JDBC事务工厂实现,创建JDBC事务对象

2.4 环境配置Environment

package com.qf.mybatis.mapping;//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//


import com.qf.mybatis.transaction.TransactionFactory;

import javax.sql.DataSource;

public final class Environment {
    private final String id;
    private final TransactionFactory transactionFactory;
    private final DataSource dataSource;

    public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
        this.id=id;
        this.transactionFactory=transactionFactory;
        this.dataSource=dataSource;
    }

    public String getId() {
        return this.id;
    }

    public TransactionFactory getTransactionFactory() {
        return this.transactionFactory;
    }

    public DataSource getDataSource() {
        return this.dataSource;
    }

    public static class Builder {
        private final String id;
        private TransactionFactory transactionFactory;
        private DataSource dataSource;

        public Builder(String id) {
            this.id = id;
        }

        public Environment.Builder transactionFactory(TransactionFactory transactionFactory) {
            this.transactionFactory = transactionFactory;
            return this;
        }

        public Environment.Builder dataSource(DataSource dataSource) {
            this.dataSource = dataSource;
            return this;
        }

        public String id() {
            return this.id;
        }

        public Environment build() {
            return new Environment(this.id, this.transactionFactory, this.dataSource);
        }
    }
}


解析XML文件,配置信息下面的environmenttransactionManagerdataSource标签,environment对象组合transactionFactory事务工厂和dataSource数据源驱动

2.5 TypeAliasRegistry类型别名注册机

package com.qf.mybatis.type;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * 类型别名注册机
 */
public class TypeAliasRegistry {

    private final Map<String,Class<?>> TYPE_ALIAS=new HashMap<>();

    public TypeAliasRegistry(){
        //引用类型
        registerAlias("string",String.class);

        // 基本包装类型
        registerAlias("byte", Byte.class);
        registerAlias("long", Long.class);
        registerAlias("short", Short.class);
        registerAlias("int", Integer.class);
        registerAlias("integer", Integer.class);
        registerAlias("double", Double.class);
        registerAlias("float", Float.class);
        registerAlias("boolean", Boolean.class);
    }

    public void registerAlias(String alias, Class<?> aClass) {
        String key = alias.toLowerCase(Locale.ENGLISH);
        TYPE_ALIAS.put(key,aClass);
    }

    public <T> Class<?> resolveAlias(String string){
        return TYPE_ALIAS.get(string.toLowerCase(Locale.ENGLISH));
    }

}

TypeAliasRegistry 注册类型的别名,简化类的名字,以及提供 registerAlias 注册方法和 resolveAlias 获取方法

2.6 Configuration配置信息

public class Configuration {

    //环境
    protected Environment environment;

    // 映射注册机
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);

    // 映射的语句,存在Map里
    protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();

    // 类型别名注册机
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

    public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);
    }
    
    //...
}

2.7 xml解析配置环境信息

 public Configuration parse() {
        try {
            //环境
            environmentElement(root.element("environments"));
            //解析映射器
            mapperElement(root.element("mappers"));
        }catch (Exception e){
            throw new RuntimeException("Error parsing SQL Mapper configuration Cause:"+e,e);
        }
        return configuration;
    }

    private void environmentElement(Element context) throws IllegalAccessException, InstantiationException {
        String environment = context.attributeValue("default");
        List<Element> elementList = context.elements("environment");
        for (Element element : elementList) {
            String id = element.attributeValue("id");
            if (id.equals(environment)){
                //事务管理器
                TransactionFactory transactionFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(element.element("transactionManager").attributeValue("type")).newInstance();
                Element dataSourceElement = element.element("dataSource");
                //数据源
                DataSourceFactory dataSourceFactory= (DataSourceFactory) typeAliasRegistry.resolveAlias(dataSourceElement.attributeValue("type")).newInstance();
                List<Element> properList = dataSourceElement.elements("property");
                Properties props=new Properties();
                for (Element property : properList) {
                    String name = property.attributeValue("name");
                    String value = property.attributeValue("value");
                    props.setProperty(name,value);
                }
                dataSourceFactory.setProperties(props);
                DataSource dataSource = dataSourceFactory.getDataSource();

                //构建环境
                Environment.Builder environmentBuilder= new Environment.Builder(id)
                        .dataSource(dataSource)
                        .transactionFactory(transactionFactory);
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }

2.8 sqlSession结果包装输出

  public <T> T selectOne(String statement, Object param) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        MappedStatement mappedStatement = configuration.getMappedStatement(statement);
        Environment environment = configuration.getEnvironment();
        DataSource dataSource = environment.getDataSource();
        Connection connection = dataSource.getConnection();

        BoundSql boundSql = mappedStatement.getBoundSql();
        String sql = boundSql.getSql();

        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setLong(1,Long.parseLong(((Object[])param)[0].toString()));
        ResultSet resultSet = preparedStatement.executeQuery();

        List<T> list=resultSet2Obj(resultSet,Class.forName(boundSql.getResultType()));
        return list.get(0);
    }


private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) throws IllegalAccessException, InstantiationException, SQLException {
        List<T> list=new ArrayList<>();
        try {
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            while (resultSet.next()){
                T obj = (T)clazz.newInstance();
                for (int i = 1; i <= columnCount; i++) {
                    Object value = resultSet.getObject(i);
                    String columnName = metaData.getColumnName(i);
                    String setMethod="set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
                    Method method;
                    if (value instanceof Timestamp){
                        method=clazz.getMethod(setMethod, Date.class);
                    }else{
                        method=clazz.getMethod(setMethod, value.getClass());
                    }
                    method.invoke(obj,value);
                    list.add(obj);
                }
            }

        }catch (Exception e){
            e.printStackTrace();
        }

        return list;
    }

3、测试

定义的dao接口和对应的xml文件

package com.qf.mybatis.dao;


public interface IUserDao {
    String queryUserInfoById(String id);
}


<?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">
            <transactionManager type="JDBC"/>
            <dataSource type="DRUID">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/edu_user?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="111111"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/User_Mapper.xml"/>
    </mappers>

</configuration>

<?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.qf.mybatis.dao.IUserDao">

    <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.qf.mybatis.po.User">
        SELECT id, userId, userHead, createTime
        FROM user
        where id = #{id}
    </select>

</mapper>


ApiTest

package com.qf.mybatis.test;

import com.qf.mybatis.dao.IUserDao;
import com.qf.mybatis.io.Resources;
import com.qf.mybatis.po.User;
import com.qf.mybatis.session.SqlSession;
import com.qf.mybatis.session.SqlSessionFactory;
import com.qf.mybatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Reader;

public class ApiTest {

    private Logger logger= LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void test_SqlSessionFactory() throws IOException {
        //1、sqlsession工厂里面拿取sqlsession
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        Reader reader = Resources.getResourcesAsReader("mybatis-config-datasource.xml");
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //2、获取映射器
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);

        //3、测试结果
        User user = userDao.queryUserInfoById(1L);
        logger.info("结果:{}",user);
    }
}


结果:

"C:\Program Files\Java\jdk1.8.0_301\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\idea2020\IntelliJ IDEA 2020.2.4\lib\idea_rt.jar=18515:D:\idea2020\IntelliJ IDEA 2020.2.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\idea2020\IntelliJ IDEA 2020.2.4\lib\idea_rt.jar;D:\idea2020\IntelliJ IDEA 2020.2.4\plugins\junit\lib\junit5-rt.jar;D:\idea2020\IntelliJ IDEA 2020.2.4\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\rt.jar;D:\zhoutao\handwrite-mybatis\step-04-datasourceuse\target\test-classes;D:\zhoutao\handwrite-mybatis\step-04-datasourceuse\target\classes;C:\Users\Administrator\.m2\repository\com\alibaba\fastjson\1.2.75\fastjson-1.2.75.jar;C:\Users\Administrator\.m2\repository\org\dom4j\dom4j\2.1.3\dom4j-2.1.3.jar;C:\Users\Administrator\.m2\repository\junit\junit\4.7\junit-4.7.jar;C:\Users\Administrator\.m2\repository\cn\hutool\hutool-all\5.5.0\hutool-all-5.5.0.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.5\slf4j-api-1.7.5.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.5\jcl-over-slf4j-1.7.5.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.0.9\logback-classic-1.0.9.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.0.9\logback-core-1.0.9.jar;C:\Users\Administrator\.m2\repository\mysql\mysql-connector-java\5.1.48\mysql-connector-java-5.1.48.jar;C:\Users\Administrator\.m2\repository\com\alibaba\druid\1.2.9\druid-1.2.9.jar;C:\Users\Administrator\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.qf.mybatis.test.ApiTest,test_SqlSessionFactory
18:19:43.678 [main] INFO  c.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
Mon Oct 31 18:19:43 CST 2022 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
18:19:43.932 [main] INFO  com.qf.mybatis.test.ApiTest - 结果:User{id=1, userId='10001', userName='小傅哥', userHead='1_04', createTime=2022-04-13 00:00:00.0, updateTime=null}

Process finished with exit code 0

目前可以实现mybatis的基本功能了,执行sql语句,并输出了结果

4、总结

1、xml为入口XMLConfigBuilderparse方法解析文件,并把环境的事务transactionFactory、数据源dataSource放到Environment类中,映射注册机放到MapperRegister,共同组合成为Configuration配置。

2、DefaultSqlSessionselectOne方法数据源连接,执行MappedStatement的sql语句,并返回输出结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舒克日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值