MyBatis 源代码阅读笔记 2 基于"注解&quo

作者: 光剑书架上的书 | 来源:发表于2019-07-24 02:15 被阅读27次

    MyBatis 源代码阅读笔记 2 基于"注解"方式的代码编写

    源代码工程

    https://github.com/Jason-Chen-2017/source-code-reading

    代码详解:

    package com.light.sword;
    
    import org.apache.ibatis.datasource.DataSourceFactory;
    import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
    import org.apache.ibatis.mapping.Environment;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.apache.ibatis.transaction.TransactionFactory;
    import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
    
    import javax.sql.DataSource;
    import java.util.List;
    import java.util.Properties;
    
    public class MyBatisDemoAnno {
    
        public static void main(String[] args) {
            // 获取数据源
            DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();
            Properties props = new Properties();
            props.setProperty("driver", "com.mysql.cj.jdbc.Driver");
            props.setProperty("url", "jdbc:mysql://127.0.0.1:3306/test");
            props.setProperty("username", "root");
            props.setProperty("password", "root");
            dataSourceFactory.setProperties(props);
            DataSource dataSource = dataSourceFactory.getDataSource();
            // 事务管理
            TransactionFactory transactionFactory = new JdbcTransactionFactory();
            Environment environment = new Environment("dev", transactionFactory, dataSource);
            // 配置对象
            Configuration configuration = new Configuration(environment);
            // add Mapper 接口类
            configuration.addMapper(ProductMapper.class);
            // open SqlSession
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            // 从SQL会话中获取 ProductMapper
            ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
            List<Product> productList = productMapper.findAll();
            System.out.println(productList);
        }
    }
    
    

    其中, ProductMapper 如下:

    package com.light.sword;
    
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    public interface ProductMapper {
    
        @Select("select * from product")
        List<Product> findAll();
    
    }
    
    

    运行输出:

    [Product{id=1, name='Product1'}, Product{id=2, name='Product2'}]

    对应到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>
        <typeAliases>
            <typeAlias alias="Product" type="com.light.sword.Product"></typeAlias>
        </typeAliases>
        <environments default="dev">
            <environment id="dev">
                <transactionManager type="JDBC"></transactionManager>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <mapper resource="mapper/ProductMapper.xml"></mapper>
        </mappers>
    
    </configuration>
    

    mapper/ProductMapper.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">
    <!-- namespace命名空间,做SQL隔离 -->
    <mapper namespace="productMapper">
        <select id="listAll" resultType="Product">
            select *
            from product
        </select>
    </mapper>
    

    源码运行解析

    DataSource

    
    ===CONFINGURATION==============================================
     jdbcDriver                     com.mysql.cj.jdbc.Driver
     jdbcUrl                        jdbc:mysql://127.0.0.1:3306/test
     jdbcUsername                   root
     jdbcPassword                   ************
     poolMaxActiveConnections       10
     poolMaxIdleConnections         5
     poolMaxCheckoutTime            20000
     poolTimeToWait                 20000
     poolPingEnabled                false
     poolPingQuery                  NO PING QUERY SET
     poolPingConnectionsNotUsedFor  0
     ---STATUS-----------------------------------------------------
     activeConnections              0
     idleConnections                0
     requestCount                   0
     averageRequestTime             0
     averageCheckoutTime            0
     claimedOverdue                 0
     averageOverdueCheckoutTime     0
     hadToWait                      0
     averageWaitTime                0
     badConnectionCount             0
    ===============================================================
    

    我们使用的是

    DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();

      public PooledDataSource() {
        dataSource = new UnpooledDataSource();
      }
    
            // 获取数据源
            DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();
            Properties props = new Properties();
            props.setProperty("driver", "com.mysql.cj.jdbc.Driver");
            props.setProperty("url", "jdbc:mysql://127.0.0.1:3306/test");
            props.setProperty("username", "root");
            props.setProperty("password", "root");
            dataSourceFactory.setProperties(props);
            DataSource dataSource = dataSourceFactory.getDataSource();
    

    然后 事务管理器:

    public class JdbcTransaction implements Transaction {
    
      private static final Log log = LogFactory.getLog(JdbcTransaction.class);
    
      protected Connection connection;
      protected DataSource dataSource;
      protected TransactionIsolationLevel level;
      protected boolean autoCommit;
    
      public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
        dataSource = ds;
        level = desiredLevel;
        autoCommit = desiredAutoCommit;
      }
    
      public JdbcTransaction(Connection connection) {
        this.connection = connection;
      }
    
      @Override
      public Connection getConnection() throws SQLException {
        if (connection == null) {
          openConnection();
        }
        return connection;
      }
    
      @Override
      public void commit() throws SQLException {
        if (connection != null && !connection.getAutoCommit()) {
          if (log.isDebugEnabled()) {
            log.debug("Committing JDBC Connection [" + connection + "]");
          }
          connection.commit();
        }
      }
    
      @Override
      public void rollback() throws SQLException {
        if (connection != null && !connection.getAutoCommit()) {
          if (log.isDebugEnabled()) {
            log.debug("Rolling back JDBC Connection [" + connection + "]");
          }
          connection.rollback();
        }
      }
    
      @Override
      public void close() throws SQLException {
        if (connection != null) {
          resetAutoCommit();
          if (log.isDebugEnabled()) {
            log.debug("Closing JDBC Connection [" + connection + "]");
          }
          connection.close();
        }
      }
    
      protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
        try {
          if (connection.getAutoCommit() != desiredAutoCommit) {
            if (log.isDebugEnabled()) {
              log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
            }
            connection.setAutoCommit(desiredAutoCommit);
          }
        } catch (SQLException e) {
          // Only a very poorly implemented driver would fail here,
          // and there's not much we can do about that.
          throw new TransactionException("Error configuring AutoCommit.  "
              + "Your driver may not support getAutoCommit() or setAutoCommit(). "
              + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
        }
      }
    
      protected void resetAutoCommit() {
        try {
          if (!connection.getAutoCommit()) {
            // MyBatis does not call commit/rollback on a connection if just selects were performed.
            // Some databases start transactions with select statements
            // and they mandate a commit/rollback before closing the connection.
            // A workaround is setting the autocommit to true before closing the connection.
            // Sybase throws an exception here.
            if (log.isDebugEnabled()) {
              log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
            }
            connection.setAutoCommit(true);
          }
        } catch (SQLException e) {
          if (log.isDebugEnabled()) {
            log.debug("Error resetting autocommit to true "
                + "before closing the connection.  Cause: " + e);
          }
        }
      }
    
      protected void openConnection() throws SQLException {
        if (log.isDebugEnabled()) {
          log.debug("Opening JDBC Connection");
        }
        connection = dataSource.getConnection();
        if (level != null) {
          connection.setTransactionIsolation(level.getLevel());
        }
        setDesiredAutoCommit(autoCommit);
      }
    
      @Override
      public Integer getTimeout() throws SQLException {
        return null;
      }
    
    }
    

    openSession

    根据 configuration 构建 SqlSessionFactory, 然后使用 SqlSessionFactory 打开session :

            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            // 从SQL会话中获取 ProductMapper
            ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
            List<Product> productList = productMapper.findAll();
    

    通过 接口类型的 ProductMapper 创建出实例对象的代码:

      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
          return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
      }
    

    MapperProxyFactory

    package org.apache.ibatis.binding;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import org.apache.ibatis.session.SqlSession;
    
    /**
     * @author Lasse Voss
     */
    public class MapperProxyFactory<T> {
    
      private final Class<T> mapperInterface;
      private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
    
      public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      public Class<T> getMapperInterface() {
        return mapperInterface;
      }
    
      public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
      }
    
      @SuppressWarnings("unchecked")
      protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }
    
      public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
      }
    
    }
    

    相关文章

      网友评论

        本文标题:MyBatis 源代码阅读笔记 2 基于"注解&quo

        本文链接:https://www.haomeiwen.com/subject/dyttrctx.html