zoukankan      html  css  js  c++  java
  • 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>    
         <!-- 属性配置,读取properties中的配置文件 -->
        <properties resource="db.propertis">
           <property name="XXX" value="XXX"/>
        </properties>
        
        <!-- 类型别名 -->
        <typeAliases>
           <!-- 在用到User类型的时候,可以直接使用别名,不需要输入User类的全部路径 -->
           <typeAlias type="com.luck.codehelp.entity.User" alias="user"/>
        </typeAliases>
    
        <!-- 类型处理器 -->
        <typeHandlers>
            <!-- 类型处理器的作用是完成JDBC类型和java类型的转换,mybatis默认已经由了很多类型处理器,正常无需自定义-->
        </typeHandlers>
        
        <!-- 对象工厂 -->
        <!-- mybatis创建结果对象的新实例时,会通过对象工厂来完成,mybatis有默认的对象工厂,正常无需配置 -->
        <objectFactory type=""></objectFactory>
        
        <!-- 插件 -->
        <plugins>
            <!-- 可以自定义拦截器通过plugin标签加入 -->
           <plugin interceptor="com.lucky.interceptor.MyPlugin"></plugin>
        </plugins>
        
        <!-- 全局配置参数 -->
        <settings>   
            <setting name="cacheEnabled" value="false" />   
            <setting name="useGeneratedKeys" value="true" /><!-- 是否自动生成主键 -->
            <setting name="defaultExecutorType" value="REUSE" />   
            <setting name="lazyLoadingEnabled" value="true"/><!-- 延迟加载标识 -->
            <setting name="aggressiveLazyLoading" value="true"/><!--有延迟加载属性的对象是否延迟加载 -->
            <setting name="multipleResultSetsEnabled" value="true"/><!-- 是否允许单个语句返回多个结果集 -->
            <setting name="useColumnLabel" value="true"/><!-- 使用列标签而不是列名 -->
            <setting name="autoMappingBehavior" value="PARTIAL"/><!-- 指定mybatis如何自动映射列到字段属性;NONE:自动映射;PARTIAL:只会映射结果没有嵌套的结果;FULL:可以映射任何复杂的结果 -->
            <setting name="defaultExecutorType" value="SIMPLE"/><!-- 默认执行器类型 -->
            <setting name="defaultFetchSize" value=""/>
            <setting name="defaultStatementTimeout" value="5"/><!-- 驱动等待数据库相应的超时时间 ,单位是秒-->
            <setting name="safeRowBoundsEnabled" value="false"/><!-- 是否允许使用嵌套语句RowBounds -->
            <setting name="safeResultHandlerEnabled" value="true"/>
            <setting name="mapUnderscoreToCamelCase" value="false"/><!-- 下划线列名是否自动映射到驼峰属性:如user_id映射到userId -->
            <setting name="localCacheScope" value="SESSION"/><!-- 本地缓存(session是会话级别) -->
            <setting name="jdbcTypeForNull" value="OTHER"/><!-- 数据为空值时,没有特定的JDBC类型的参数的JDBC类型 -->
            <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/><!-- 指定触发延迟加载的对象的方法 -->
            <setting name="callSettersOnNulls" value="false"/><!--如果setter方法或map的put方法,如果检索到的值为null时,数据是否有用  -->
            <setting name="logPrefix" value="XXXX"/><!-- mybatis日志文件前缀字符串 -->
            <setting name="logImpl" value="SLF4J"/><!-- mybatis日志的实现类 -->
            <setting name="proxyFactory" value="CGLIB"/><!-- mybatis代理工具 -->
        </settings>  
    
        <!-- 环境配置集合 -->
        <environments default="development">    
            <environment id="development">    
                <transactionManager type="JDBC"/><!-- 事务管理器 -->
                <dataSource type="POOLED"><!-- 数据库连接池 -->    
                    <property name="driver" value="com.mysql.jdbc.Driver" />    
                    <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=UTF-8" />    
                    <property name="username" value="root" />    
                    <property name="password" value="root" />    
                </dataSource>    
            </environment>    
        </environments>    
        
        <!-- mapper文件映射配置 -->
        <mappers>    
            <mapper resource="com/luck/codehelp/mapper/UserMapper.xml"/>    
        </mappers>    
    </configuration>
    

    InterceptorChain

    拦截器集合

    public class InterceptorChain {
        //一个拦截器集合
        private final List<Interceptor> interceptors = new ArrayList();
    }
    

    Configuration

    配置文件中所有的属性都会封装到这个类里

     public class Configuration {
            protected Environment environment;//运行环境
    
            protected boolean safeRowBoundsEnabled = false;
            protected boolean safeResultHandlerEnabled = true;
            protected boolean mapUnderscoreToCamelCase = false;
            protected boolean aggressiveLazyLoading = true; //true:有延迟加载属性的对象被调用时完全加载任意属性;false:每个属性按需要加载
            protected boolean multipleResultSetsEnabled = true;//是否允许多种结果集从一个单独的语句中返回
            protected boolean useGeneratedKeys = false;//是否支持自动生成主键
            protected boolean useColumnLabel = true;//是否使用列标签
            protected boolean cacheEnabled = true;//是否使用缓存标识
            protected boolean callSettersOnNulls = false;//
            protected boolean useActualParamName = true;
    
            protected String logPrefix;
            protected Class <? extends Log> logImpl;
            protected Class <? extends VFS> vfsImpl;
            protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
            protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
            protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
            protected Integer defaultStatementTimeout;
            protected Integer defaultFetchSize;
            protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
            protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;//指定mybatis如果自动映射列到字段和属性,PARTIAL会自动映射简单的没有嵌套的结果,FULL会自动映射任意复杂的结果
            protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
    
            protected Properties variables = new Properties();
            protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            protected ObjectFactory objectFactory = new DefaultObjectFactory();
            protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
    
            protected boolean lazyLoadingEnabled = false;//是否延时加载,false则表示所有关联对象即使加载,true表示延时加载
            protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
    
            protected String databaseId;
    
            protected Class<?> configurationFactory;
    
            protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
            protected final InterceptorChain interceptorChain = new InterceptorChain();
            protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
            protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
            protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    
            protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
            protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
            protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
            protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
            protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
    
            protected final Set<String> loadedResources = new HashSet<String>(); //已经加载过的resource(mapper)
            protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
    
            protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
            protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
            protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
            protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
    
            protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
    
        }
    

    Configuration构造器

    注意,这里创建了一个用来存放mapper别名的typeAliasRegistry ,并且预置了好多别名进去。

    public Configuration() {
            this.safeRowBoundsEnabled = false;
            this.safeResultHandlerEnabled = true;
            this.mapUnderscoreToCamelCase = false;
            this.aggressiveLazyLoading = true;
            this.multipleResultSetsEnabled = true;
            this.useGeneratedKeys = false;
            this.useColumnLabel = true;
            this.cacheEnabled = true;
            this.callSettersOnNulls = false;
            this.localCacheScope = LocalCacheScope.SESSION;
            this.jdbcTypeForNull = JdbcType.OTHER;
            this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
            this.defaultExecutorType = ExecutorType.SIMPLE;
            this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;
            this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
            this.variables = new Properties();
            this.reflectorFactory = new DefaultReflectorFactory();
            this.objectFactory = new DefaultObjectFactory();
            this.objectWrapperFactory = new DefaultObjectWrapperFactory();
            this.mapperRegistry = new MapperRegistry(this);
            this.lazyLoadingEnabled = false;
            this.proxyFactory = new JavassistProxyFactory();
            this.interceptorChain = new InterceptorChain();
            this.typeHandlerRegistry = new TypeHandlerRegistry();
            //这个属性就是用来存储mapper的
            this.typeAliasRegistry = new TypeAliasRegistry();
            this.languageRegistry = new LanguageDriverRegistry();
            this.mappedStatements = new Configuration.StrictMap("Mapped Statements collection");
            this.caches = new Configuration.StrictMap("Caches collection");
            this.resultMaps = new Configuration.StrictMap("Result Maps collection");
            this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection");
            this.keyGenerators = new Configuration.StrictMap("Key Generators collection");
            this.loadedResources = new HashSet();
            this.sqlFragments = new Configuration.StrictMap("XML fragments parsed from previous mappers");
            this.incompleteStatements = new LinkedList();
            this.incompleteCacheRefs = new LinkedList();
            this.incompleteResultMaps = new LinkedList();
            this.incompleteMethods = new LinkedList();
            this.cacheRefMap = new HashMap();
            //这里初始化了好多mapper
            this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
            this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
            this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
            this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
            this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
            this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
            this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
            this.typeAliasRegistry.registerAlias("LRU", LruCache.class);
            this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
            this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
            this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
            this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
            this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
            this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
            this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
            this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
            this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
            this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
            this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
            this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
            this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
            this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
            this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
            this.languageRegistry.register(RawLanguageDriver.class);
        }
    

    TypeAliasRegistry

    内部是一个map,用来存储mapper别名的map

    public class TypeAliasRegistry {
            //内部是一个map
            private final Map<String, Class<?>> TYPE_ALIASES = new HashMap();
    
            public TypeAliasRegistry() {
                this.registerAlias("string", String.class);
                this.registerAlias("byte", Byte.class);
                this.registerAlias("long", Long.class);
                this.registerAlias("short", Short.class);
                this.registerAlias("int", Integer.class);
                this.registerAlias("integer", Integer.class);
                this.registerAlias("double", Double.class);
                this.registerAlias("float", Float.class);
                this.registerAlias("boolean", Boolean.class);
                this.registerAlias("byte[]", Byte[].class);
                this.registerAlias("long[]", Long[].class);
                this.registerAlias("short[]", Short[].class);
                this.registerAlias("int[]", Integer[].class);
                this.registerAlias("integer[]", Integer[].class);
                this.registerAlias("double[]", Double[].class);
                this.registerAlias("float[]", Float[].class);
                this.registerAlias("boolean[]", Boolean[].class);
                this.registerAlias("_byte", Byte.TYPE);
                this.registerAlias("_long", Long.TYPE);
                this.registerAlias("_short", Short.TYPE);
                this.registerAlias("_int", Integer.TYPE);
                this.registerAlias("_integer", Integer.TYPE);
                this.registerAlias("_double", Double.TYPE);
                this.registerAlias("_float", Float.TYPE);
                this.registerAlias("_boolean", Boolean.TYPE);
                this.registerAlias("_byte[]", byte[].class);
                this.registerAlias("_long[]", long[].class);
                this.registerAlias("_short[]", short[].class);
                this.registerAlias("_int[]", int[].class);
                this.registerAlias("_integer[]", int[].class);
                this.registerAlias("_double[]", double[].class);
                this.registerAlias("_float[]", float[].class);
                this.registerAlias("_boolean[]", boolean[].class);
                this.registerAlias("date", Date.class);
                this.registerAlias("decimal", BigDecimal.class);
                this.registerAlias("bigdecimal", BigDecimal.class);
                this.registerAlias("biginteger", BigInteger.class);
                this.registerAlias("object", Object.class);
                this.registerAlias("date[]", Date[].class);
                this.registerAlias("decimal[]", BigDecimal[].class);
                this.registerAlias("bigdecimal[]", BigDecimal[].class);
                this.registerAlias("biginteger[]", BigInteger[].class);
                this.registerAlias("object[]", Object[].class);
                this.registerAlias("map", Map.class);
                this.registerAlias("hashmap", HashMap.class);
                this.registerAlias("list", List.class);
                this.registerAlias("arraylist", ArrayList.class);
                this.registerAlias("collection", Collection.class);
                this.registerAlias("iterator", Iterator.class);
                this.registerAlias("ResultSet", ResultSet.class);
            }
        }
    

    返回顶部

    Environment

    //配置environment环境
    <environments default="development">
        <environment id="development">
            /** 事务配置 type= JDBC、MANAGED 
             *  1.JDBC:这个配置直接简单使用了JDBC的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
             *  2.MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。
             */
            <transactionManager type="JDBC" />
            /** 数据源类型:type = UNPOOLED、POOLED、JNDI 
             *  1.UNPOOLED:这个数据源的实现是每次被请求时简单打开和关闭连接。
             *  2.POOLED:这是JDBC连接对象的数据源连接池的实现。 
             *  3.JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器
             */
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/xhm" />
                <property name="username" value="root" />
                <property name="password" value="root" />
                //默认连接事务隔离级别
                <property name="defaultTransactionIsolationLevel" value=""/> 
            </dataSource>
        </environment>
    </environments>
    
    1. environment中包含事务和数据源
    2. 事务配置支持两种type= JDBC、MANAGED
      • JDBC:这个配置直接简单使用了JDBC的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
      • MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。
        3、数据源类型包括三种type = UNPOOLED、POOLED、JNDI
      • UNPOOLED:这个数据源的实现是每次被请求时简单打开和关闭连接。
      • POOLED:这是JDBC连接对象的数据源连接池的实现。
      • JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器

    返回顶部

    JdbcTransaction

    public class JdbcTransaction implements Transaction {
        //数据库连接对象
        protected Connection connection;
        //数据库DataSource
        protected DataSource dataSource;
        //数据库隔离级别
        protected TransactionIsolationLevel level;
        //是否自动提交
        protected boolean autoCommmit;
    
        public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
            //设置dataSource和隔离级别,是否自动提交属性
            //这里隔离级别传过来的是null,表示使用数据库默认隔离级别,自动提交为false,表示不自动提交
            this.dataSource = ds;
            this.level = desiredLevel;
            this.autoCommmit = desiredAutoCommit;
        }
    
         public Connection getConnection() throws SQLException {
            if (this.connection == null) {
                this.openConnection();
            }
    
            return this.connection;
        }
    
        //提交功能是通过Connection去完成的
        public void commit() throws SQLException {
            if (this.connection != null && !this.connection.getAutoCommit()) {
                if (log.isDebugEnabled()) {
                    log.debug("Committing JDBC Connection [" + this.connection + "]");
                }
    
                this.connection.commit();
            }
    
        }
    
        //回滚功能是通过Connection去完成的
        public void rollback() throws SQLException {
            if (this.connection != null && !this.connection.getAutoCommit()) {
                if (log.isDebugEnabled()) {
                    log.debug("Rolling back JDBC Connection [" + this.connection + "]");
                }
    
                this.connection.rollback();
            }
    
        }
    
        //关闭功能是通过Connection去完成的
        public void close() throws SQLException {
            if (this.connection != null) {
                this.resetAutoCommit();
                if (log.isDebugEnabled()) {
                    log.debug("Closing JDBC Connection [" + this.connection + "]");
                }
    
                this.connection.close();
            }
    
        }
        
        //获取连接是通过dataSource来完成的
        protected void openConnection() throws SQLException {
            if (log.isDebugEnabled()) {
                log.debug("Opening JDBC Connection");
            }
    
            this.connection = this.dataSource.getConnection();
            if (this.level != null) {
                this.connection.setTransactionIsolation(this.level.getLevel());
            }
    
            this.setDesiredAutoCommit(this.autoCommmit);
        }
    }
    
    1. JdbcTransaction是mybatis默认的事务类型
    2. JdbcTransaction中有一个Connection属性和dataSource属性,使用connection来进行提交、回滚、关闭等操作,
      也就是说JdbcTransaction其实只是在jdbc的connection上面封装了一下,实际使用的其实还是jdbc的事务

    返回顶部

    DefaultSqlSession

    public class DefaultSqlSession implements SqlSession {
    
      /**
       * mybatis全局配置新
       */
      private final Configuration configuration;
      /**
       * SQL执行器
       */
      private final Executor executor;
    
      /**
       * 是否自动提交
       */
      private final boolean autoCommit;
    
      private List<Cursor<?>> cursorList;
      
      public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
            this.configuration = configuration;
            this.executor = executor;
            this.dirty = false;
            this.autoCommit = autoCommit;
      }
      
      @Override
      public <T> T selectOne(String statement) {
        return this.<T>selectOne(statement, null);
      }
    
      @Override
      public <T> T selectOne(String statement, Object parameter) {
        // Popular vote was to return null on 0 results and throw exception on too many.
        List<T> list = this.<T>selectList(statement, parameter);
        if (list.size() == 1) {
          return list.get(0);
        } else if (list.size() > 1) {
          throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
          return null;
        }
      }
      @Override
      public <E> List<E> selectList(String statement) {
        return this.selectList(statement, null);
      }
    
      @Override
      public <E> List<E> selectList(String statement, Object parameter) {
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
      }
    
      @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          MappedStatement ms = configuration.getMappedStatement(statement);
          return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    }
    

    返回顶部

    SqlCommand

    
    public static class SqlCommand {
        //name为MappedStatement的id,也就是namespace.methodName(mapper.EmployeeMapper.getAll)
        private final String name;
        //SQL的类型,如insert,delete,update
        private final SqlCommandType type;
    
        public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
            //拼接Mapper接口名和方法名,(mapper.EmployeeMapper.getAll)
            String statementName = mapperInterface.getName() + "." + method.getName();
            MappedStatement ms = null;
            //检测configuration是否有key为mapper.EmployeeMapper.getAll的MappedStatement
            if (configuration.hasStatement(statementName)) {
                //获取MappedStatement
                ms = configuration.getMappedStatement(statementName);
            } else if (!mapperInterface.equals(method.getDeclaringClass())) {
                String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
                if (configuration.hasStatement(parentStatementName)) {
                    ms = configuration.getMappedStatement(parentStatementName);
                }
            }
    
            // 检测当前方法是否有对应的 MappedStatement
            if (ms == null) {
                if (method.getAnnotation(Flush.class) != null) {
                    name = null;
                    type = SqlCommandType.FLUSH;
                } else {
                    throw new BindingException("Invalid bound statement (not found): "
                            + mapperInterface.getName() + "." + methodName);
                }
            } else {
                // 设置 name 和 type 变量
                name = ms.getId();
                type = ms.getSqlCommandType();
                if (type == SqlCommandType.UNKNOWN) {
                    throw new BindingException("Unknown execution method for: " + name);
                }
            }
        }
    }
        public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
            //检测configuration是否有key为statementName的MappedStatement
            return this.mappedStatements.containsKey(statementName);
        }
    

    返回顶部

    MethodSignature

    public static class MethodSignature {
    
        private final boolean returnsMany;
        private final boolean returnsMap;
        private final boolean returnsVoid;
        private final boolean returnsCursor;
        private final Class<?> returnType;
        private final String mapKey;
        private final Integer resultHandlerIndex;
        private final Integer rowBoundsIndex;
        private final ParamNameResolver paramNameResolver;
    
        public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
    
            // 通过反射解析方法返回类型
            Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
            if (resolvedReturnType instanceof Class<?>) {
                this.returnType = (Class<?>) resolvedReturnType;
            } else if (resolvedReturnType instanceof ParameterizedType) {
                this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
            } else {
                this.returnType = method.getReturnType();
            }
            
            // 检测返回值类型是否是 void、集合或数组、Cursor、Map 等
            this.returnsVoid = void.class.equals(this.returnType);
            this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
            this.returnsCursor = Cursor.class.equals(this.returnType);
            // 解析 @MapKey 注解,获取注解内容
            this.mapKey = getMapKey(method);
            this.returnsMap = this.mapKey != null;
            /*
             * 获取 RowBounds 参数在参数列表中的位置,如果参数列表中
             * 包含多个 RowBounds 参数,此方法会抛出异常
             */ 
            this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
            // 获取 ResultHandler 参数在参数列表中的位置
            this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
            // 解析参数列表
            this.paramNameResolver = new ParamNameResolver(configuration, method);
        }
    }
    

    返回顶部

    BoundSql

    //一个完整的 SQL 语句,可能会包含问号 ? 占位符
            private final String sql;
            //参数映射列表,SQL 中的每个 #{xxx} 占位符都会被解析成相应的 ParameterMapping 对象
            private final List<ParameterMapping> parameterMappings;
            //运行时参数,即用户传入的参数,比如 Article 对象,或是其他的参数
            private final Object parameterObject;
            //附加参数集合,用于存储一些额外的信息,比如 datebaseId 等
            private final Map<String, Object> additionalParameters;
            //additionalParameters 的元信息对象
            private final MetaObject metaParameters;
    
    1. BoundSql语句的解析主要是通过对#{}字符的解析,将其替换成?。最后均包装成预表达式供PrepareStatement调用执行
    2. {}中的key属性以及相应的参数映射,比如javaType、jdbcType等信息均保存至BoundSql的parameterMappings属性中供最后的预表达式对象PrepareStatement赋值使用

    返回顶部

    DynamicContext

    public class DynamicContext {
    
        public static final String PARAMETER_OBJECT_KEY = "_parameter";
        public static final String DATABASE_ID_KEY = "_databaseId";
    
        //bindings 则用于存储一些额外的信息,比如运行时参数
        private final ContextMap bindings;
        //sqlBuilder 变量用于存放 SQL 片段的解析结果
        private final StringBuilder sqlBuilder = new StringBuilder();
    
        public DynamicContext(Configuration configuration, Object parameterObject) {
            // 创建 ContextMap,并将运行时参数放入ContextMap中
            if (parameterObject != null && !(parameterObject instanceof Map)) {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                bindings = new ContextMap(metaObject);
            } else {
                bindings = new ContextMap(null);
            }
    
            // 存放运行时参数 parameterObject 以及 databaseId
            bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
            bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
        }
    
        
        public void bind(String name, Object value) {
            this.bindings.put(name, value);
        }
    
        //拼接Sql片段
        public void appendSql(String sql) {
            this.sqlBuilder.append(sql);
            this.sqlBuilder.append(" ");
        }
        
        //得到sql字符串
        public String getSql() {
            return this.sqlBuilder.toString().trim();
        }
    
        //继承HashMap
        static class ContextMap extends HashMap<String, Object> {
    
            private MetaObject parameterMetaObject;
    
            public ContextMap(MetaObject parameterMetaObject) {
                this.parameterMetaObject = parameterMetaObject;
            }
    
            @Override
            public Object get(Object key) {
                String strKey = (String) key;
                // 检查是否包含 strKey,若包含则直接返回
                if (super.containsKey(strKey)) {
                    return super.get(strKey);
                }
    
                if (parameterMetaObject != null) {
                    // 从运行时参数中查找结果,这里会在${name}解析时,通过name获取运行时参数值,替换掉${name}字符串
                    return parameterMetaObject.getValue(strKey);
                }
    
                return null;
            }
        }
        // 省略部分代码
    }
    
    1. DynamicContext是SQL语句构建的上下文,每个SQL片段解析完成后,都会将解析结果存入DynamicContext中。待所有的SQL片段解析完毕后,一条完整的SQL语句就会出现在 DynamicContext 对象中。

    返回顶部

    SqlNode

    1. 对于一个包含了 ${} 占位符,或 等标签的 SQL,在解析的过程中,会被分解成多个片段。每个片段都有对应的类型,每种类型的片段都有不同的解析逻辑。在源码中,片段这个概念等价于 sql 节点,即 SqlNode。
    2. StaticTextSqlNode 用于存储静态文本,TextSqlNode 用于存储带有 ${} 占位符的文本,IfSqlNode 则用于存储 节点的内容。MixedSqlNode 内部维护了一个 SqlNode 集合,用于存储各种各样的 SqlNode。接下来,我将会对 MixedSqlNode 、StaticTextSqlNode、TextSqlNode、IfSqlNode、WhereSqlNode 以及 TrimSqlNode 等进行分析

    MixedSqlNode

    public class MixedSqlNode implements SqlNode {
        private final List<SqlNode> contents;
    
        public MixedSqlNode(List<SqlNode> contents) {
            this.contents = contents;
        }
    
        @Override
        public boolean apply(DynamicContext context) {
            // 遍历 SqlNode 集合
            for (SqlNode sqlNode : contents) {
                // 调用 salNode 对象本身的 apply 方法解析 sql
                sqlNode.apply(context);
            }
            return true;
        }
    }
    
    1. MixedSqlNode 可以看做是 SqlNode 实现类对象的容器,凡是实现了 SqlNode 接口的类都可以存储到 MixedSqlNode 中,包括它自己。MixedSqlNode 解析方法 apply 逻辑比较简单,即遍历 SqlNode 集合,并调用其他 SqlNode实现类对象的 apply 方法解析 sql。

    StaticTextSqlNode

    public class StaticTextSqlNode implements SqlNode {
    
        private final String text;
    
        public StaticTextSqlNode(String text) {
            this.text = text;
        }
    
        @Override
        public boolean apply(DynamicContext context) {
            //直接拼接当前sql片段的文本到DynamicContext的sqlBuilder中
            context.appendSql(text);
            return true;
        }
    }
    
    1. StaticTextSqlNode用于存储静态文本,直接将其存储的 SQL 的文本值拼接到 DynamicContext 的sqlBuilder中即可。下面分析一下 TextSqlNode。

    TextSqlNode

    public class TextSqlNode implements SqlNode {
    
        private final String text;
        private final Pattern injectionFilter;
    
        @Override
        public boolean apply(DynamicContext context) {
            // 创建 ${} 占位符解析器
            GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
            // 解析 ${} 占位符,通过ONGL 从用户传入的参数中获取结果,替换text中的${} 占位符
            // 并将解析结果的文本拼接到DynamicContext的sqlBuilder中
            context.appendSql(parser.parse(text));
            return true;
        }
    
        private GenericTokenParser createParser(TokenHandler handler) {
            // 创建占位符解析器
            return new GenericTokenParser("${", "}", handler);
        }
    
        private static class BindingTokenParser implements TokenHandler {
    
            private DynamicContext context;
            private Pattern injectionFilter;
    
            public BindingTokenParser(DynamicContext context, Pattern injectionFilter) {
                this.context = context;
                this.injectionFilter = injectionFilter;
            }
    
            @Override
            public String handleToken(String content) {
                Object parameter = context.getBindings().get("_parameter");
                if (parameter == null) {
                    context.getBindings().put("value", null);
                } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
                    context.getBindings().put("value", parameter);
                }
                // 通过 ONGL 从用户传入的参数中获取结果
                Object value = OgnlCache.getValue(content, context.getBindings());
                String srtValue = (value == null ? "" : String.valueOf(value));
                // 通过正则表达式检测 srtValue 有效性
                checkInjection(srtValue);
                return srtValue;
            }
        }
    }
    
    1. GenericTokenParser 是一个通用的标记解析器,用于解析形如 ${name},#{id} 等标记。此时是解析 ${name}的形式,从运行时参数的Map中获取到key为name的值,直接用运行时参数替换掉 ${name}字符串,将替换后的text字符串拼接到DynamicContext的sqlBuilder中

    举个例子吧,比喻我们有如下SQL

    SELECT * FROM user WHERE name = '${name}' and id= ${id}
    

    假如我们传的参数 Map中name值为 chenhao,id为1,那么该 SQL 最终会被解析成如下的结果:

    SELECT * FROM user WHERE name = 'chenhao' and id= 1
    

    很明显这种直接拼接值很容易造成SQL注入,假如我们传入的参数为name值为 chenhao'; DROP TABLE user;# ,解析得到的结果为

    SELECT * FROM user WHERE name = 'chenhao'; DROP TABLE user;#'
    

    由于传入的参数没有经过转义,最终导致了一条 SQL 被恶意参数拼接成了两条 SQL。这就是为什么我们不应该在 SQL 语句中是用 ${} 占位符,风险太大。

    IfSqlNode

    public class IfSqlNode implements SqlNode {
    
        private final ExpressionEvaluator evaluator;
        private final String test;
        private final SqlNode contents;
    
        public IfSqlNode(SqlNode contents, String test) {
            this.test = test;
            this.contents = contents;
            this.evaluator = new ExpressionEvaluator();
        }
    
        @Override
        public boolean apply(DynamicContext context) {
            // 通过 ONGL 评估 test 表达式的结果
            if (evaluator.evaluateBoolean(test, context.getBindings())) {
                // 若 test 表达式中的条件成立,则调用其子节点节点的 apply 方法进行解析
                // 如果是静态SQL节点,则会直接拼接到DynamicContext中
                contents.apply(context);
                return true;
            }
            return false;
        }
    }
    
    1. IfSqlNode 对应的是 节点,首先是通过 ONGL 检测 test 表达式是否为 true,如果为 true,则调用其子节点的 apply 方法继续进行解析。如果子节点是静态SQL节点,则子节点的文本值会直接拼接到DynamicContext中

    返回顶部

    StaticSqlSource

    public class StaticSqlSource implements SqlSource {
    
        private final String sql;
        private final List<ParameterMapping> parameterMappings;
        private final Configuration configuration;
    
        public StaticSqlSource(Configuration configuration, String sql) {
            this(configuration, sql, null);
        }
    
        public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
            this.sql = sql;
            this.parameterMappings = parameterMappings;
            this.configuration = configuration;
        }
    
        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            // 创建 BoundSql 对象
            return new BoundSql(configuration, sql, parameterMappings, parameterObject);
        }
    }
    

    返回顶部

    StatementHandler

    public class RoutingStatementHandler implements StatementHandler {
    
        private final StatementHandler delegate;
    
        public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
            ResultHandler resultHandler, BoundSql boundSql) {
    
            // 根据 StatementType 创建不同的 StatementHandler 
            switch (ms.getStatementType()) {
                case STATEMENT:
                    delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    break;
                case PREPARED:
                    delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    break;
                case CALLABLE:
                    delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    break;
                default:
                    throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
            }
        }
        
    }
    
    
    1. 通过StatementHandler可以获取Statement对象
    2. RoutingStatementHandler的构造方法会根据MappedStatement中的statementType变量创建不同的StatementHandler实现类。
      我们看到statementType的默认类型为PREPARED,将会创建PreparedStatementHandler。

    返回顶部

    jdbc是怎么操作数据库的

    //
    public class Login {
        /**
         *    第一步,加载驱动,创建数据库的连接
         *    第二步,编写sql
         *    第三步,需要对sql进行预编译
         *    第四步,向sql里面设置参数
         *    第五步,执行sql
         *    第六步,释放资源 
         * @throws Exception 
         */
         
        public static final String URL = "jdbc:mysql://localhost:3306/chenhao";
        public static final String USER = "liulx";
        public static final String PASSWORD = "123456";
        public static void main(String[] args) throws Exception {
            login("lucy","123");
        }
        
        public static void login(String username , String password) throws Exception{
            Connection conn = null; 
            PreparedStatement psmt = null;
            ResultSet rs = null;
            try {
                //加载驱动程序
                Class.forName("com.mysql.jdbc.Driver");
                //获得数据库连接
                conn = DriverManager.getConnection(URL, USER, PASSWORD);
                //编写sql
                String sql = "select * from user where name =? and password = ?";//问号相当于一个占位符
                //对sql进行预编译
                psmt = conn.prepareStatement(sql);
                //设置参数
                psmt.setString(1, username);
                psmt.setString(2, password);
                //执行sql ,返回一个结果集
                rs = psmt.executeQuery();
                //输出结果
                while(rs.next()){
                    System.out.println(rs.getString("user_name")+" 年龄:"+rs.getInt("age"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                //释放资源
                conn.close();
                psmt.close();
                rs.close();
            }
        }
    }
    

    返回顶部

    DataSource

    //
    public interface DataSource  extends CommonDataSource,Wrapper {
      //获取数据库连接
      Connection getConnection() throws SQLException;
    
      Connection getConnection(String username, String password)
        throws SQLException;
    
    }
    
    1. DataSource分两种,一种是使用连接池,一种是普通的DataSource
    2. DataSource用来获取数据库连接

    UnpooledDataSource

    //
    public class UnpooledDataSource implements DataSource {
        private ClassLoader driverClassLoader;
        private Properties driverProperties;
        private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap();
        private String driver;
        private String url;
        private String username;
        private String password;
        private Boolean autoCommit;
        private Integer defaultTransactionIsolationLevel;
    
        public UnpooledDataSource() {
        }
    
        public UnpooledDataSource(String driver, String url, String username, String password) {
            this.driver = driver;
            this.url = url;
            this.username = username;
            this.password = password;
        }
    
    1. 不具有池化特性,每次会返回一个新的数据库连接,而非复用旧的连接
    2. 提供了三个方法:
      • initializeDriver - 初始化数据库驱动
      • doGetConnection - 获取数据连接
      • configureConnection - 配置数据库连接

    初始化数据库驱动

    private synchronized void initializeDriver() throws SQLException {
            // 检测当前 driver 对应的驱动实例是否已经注册
            if (!registeredDrivers.containsKey(driver)) {
                Class<?> driverType;
                try {
                    // 加载驱动类型
                    if (driverClassLoader != null) {
                        // 使用 driverClassLoader 加载驱动
                        driverType = Class.forName(driver, true, driverClassLoader);
                    } else {
                        // 通过其他 ClassLoader 加载驱动
                        driverType = Resources.classForName(driver);
                    }
    
                    // 通过反射创建驱动实例
                    Driver driverInstance = (Driver) driverType.newInstance();
                    /*
                     * 注册驱动,注意这里是将 Driver 代理类 DriverProxy 对象注册到 DriverManager 中的,而非 Driver 对象本身。
                     */
                    DriverManager.registerDriver(new DriverProxy(driverInstance));
                    // 缓存驱动类名和实例,防止多次注册
                    registeredDrivers.put(driver, driverInstance);//入口
                } catch (Exception e) {
                    throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
                }
            }
        }
    
    
    //DriverManager
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
    public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {
    
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }
    }
    
    1. 通过反射机制加载驱动Driver,并将其注册到DriverManager中的一个常量集合中,供后面获取连接时使用,实际开发中有可能使用到了多种数据库类型,如Mysql、Oracle等,其驱动都是不同的,不同的数据源获取连接时使用的是不同的驱动。

    获取数据库连接

    //UnpooledDataSource
    public Connection getConnection() throws SQLException {
        return doGetConnection(username, password);
    }
        
    private Connection doGetConnection(String username, String password) throws SQLException {
        Properties props = new Properties();
        if (driverProperties != null) {
            props.putAll(driverProperties);
        }
        if (username != null) {
            // 存储 user 配置
            props.setProperty("user", username);
        }
        if (password != null) {
            // 存储 password 配置
            props.setProperty("password", password);
        }
        // 调用重载方法
        return doGetConnection(props);
    }
    
    private Connection doGetConnection(Properties properties) throws SQLException {
        initializeDriver();
        // 获取连接
        Connection connection = DriverManager.getConnection(url, properties);//入口
        configureConnection(connection);
        return connection;
    }
    
    //DriverManager
    private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
        // 获取类加载器
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
          if (callerCL == null) {
            callerCL = Thread.currentThread().getContextClassLoader();
          }
        }
        // 此处省略部分代码 
        // 这里遍历的是在registerDriver(Driver driver)方法中注册的驱动对象
        // 每个DriverInfo包含了驱动对象和其信息
        for(DriverInfo aDriver : registeredDrivers) {
    
          // 判断是否为当前线程类加载器加载的驱动类
          if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
              println("trying " + aDriver.driver.getClass().getName());
    
              // 获取连接对象,这里调用了Driver的父类的方法
              // 如果这里有多个DriverInfo,比喻Mysql和Oracle的Driver都注册registeredDrivers了
              // 这里所有的Driver都会尝试使用url和info去连接,哪个连接上了就返回
              // 会不会所有的都会连接上呢?不会,因为url的写法不同,不同的Driver会判断url是否适合当前驱动
              Connection con = aDriver.driver.connect(url, info);
              if (con != null) {
                // 打印连接成功信息
                println("getConnection returning " + aDriver.driver.getClass().getName());
                // 返回连接对像
                return (con);
              }
            } catch (SQLException ex) {
              if (reason == null) {
                reason = ex;
              }
            }
          } else {
            println("    skipping: " + aDriver.getClass().getName());
          }
        }  
    }
    
    1. 将一些配置信息放入到 Properties 对象中
    2. 循环所有注册的驱动,然后通过驱动进行连接,所有的驱动都会尝试连接,连接成功返回连接

    配置数据库连接

    private void configureConnection(Connection conn) throws SQLException {
        if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
            // 设置自动提交
            conn.setAutoCommit(autoCommit);
        }
        if (defaultTransactionIsolationLevel != null) {
            // 设置事务隔离级别
            conn.setTransactionIsolation(defaultTransactionIsolationLevel);
        }
    }
    
    1. 设置是否自动提交、事务隔离级别属性

    PooledDataSource

    //PooledConnection
    class PooledConnection implements InvocationHandler {
    
        private static final String CLOSE = "close";
        private static final Class<?>[] IFACES = new Class<?>[]{Connection.class};
    
        private final int hashCode;
        private final PooledDataSource dataSource;
        // 真实的数据库连接
        private final Connection realConnection;
        // 数据库连接代理
        private final Connection proxyConnection;
        
        // 从连接池中取出连接时的时间戳
        private long checkoutTimestamp;
        // 数据库连接创建时间
        private long createdTimestamp;
        // 数据库连接最后使用时间
        private long lastUsedTimestamp;
        // connectionTypeCode = (url + username + password).hashCode()
        private int connectionTypeCode;
        // 表示连接是否有效
        private boolean valid;
    
        public PooledConnection(Connection connection, PooledDataSource dataSource) {
            this.hashCode = connection.hashCode();
            this.realConnection = connection;
            this.dataSource = dataSource;
            this.createdTimestamp = System.currentTimeMillis();
            this.lastUsedTimestamp = System.currentTimeMillis();
            this.valid = true;
            // 创建 Connection 的代理类对象
            this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {...}
        
    }
    
    
    
    
    
    //
    public class PoolState {
    
        protected PooledDataSource dataSource;
    
        // 空闲连接列表
        protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();
        // 活跃连接列表
        protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();
        // 从连接池中获取连接的次数
        protected long requestCount = 0;
        // 请求连接总耗时(单位:毫秒)
        protected long accumulatedRequestTime = 0;
        // 连接执行时间总耗时
        protected long accumulatedCheckoutTime = 0;
        // 执行时间超时的连接数
        protected long claimedOverdueConnectionCount = 0;
        // 超时时间累加值
        protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
        // 等待时间累加值
        protected long accumulatedWaitTime = 0;
        // 等待次数
        protected long hadToWaitCount = 0;
        // 无效连接数
        protected long badConnectionCount = 0;
    }
    
    1. PooledDataSource 内部实现了连接池功能,用于复用数据库连接。因此,从效率上来说,PooledDataSource 要高于 UnpooledDataSource。但是最终获取Connection还是通过UnpooledDataSource,只不过PooledDataSource 提供一个存储Connection的功能。
    2. PooledDataSource 需要借助两个辅助类帮其完成功能,这两个辅助类分别是 PoolState 和 PooledConnection。
      PoolState 用于记录连接池运行时的状态,比如连接获取次数,无效连接数量等。
      同时 PoolState 内部定义了两个 PooledConnection 集合,用于存储空闲连接和活跃连接。
      PooledConnection 内部定义了一个 Connection 类型的变量,用于指向真实的数据库连接。以及一个 Connection 的代理类,用于对部分方法调用进行拦截。
      PooledConnection 内部也定义了一些字段,用于记录数据库连接的一些运行时状态。

    获取连接

    public class PooledDataSource implements DataSource {
        private static final Log log = LogFactory.getLog(PooledDataSource.class);
        //这里有辅助类PoolState
        private final PoolState state = new PoolState(this);
        //还有一个UnpooledDataSource属性,其实真正获取Connection是由UnpooledDataSource来完成的
        private final UnpooledDataSource dataSource;
        protected int poolMaximumActiveConnections = 10;
        protected int poolMaximumIdleConnections = 5;
        protected int poolMaximumCheckoutTime = 20000;
        protected int poolTimeToWait = 20000;
        protected String poolPingQuery = "NO PING QUERY SET";
        protected boolean poolPingEnabled = false;
        protected int poolPingConnectionsNotUsedFor = 0;  
        private int expectedConnectionTypeCode;
        
        public PooledDataSource() {
            this.dataSource = new UnpooledDataSource();
        }
        
        public PooledDataSource(String driver, String url, String username, String password) {
            //构造器中创建UnpooledDataSource对象
            this.dataSource = new UnpooledDataSource(driver, url, username, password);
        }
        
        public Connection getConnection() throws SQLException {
            return this.popConnection(this.dataSource.getUsername(), this.dataSource.getPassword()).getProxyConnection();
        }
        
        private PooledConnection popConnection(String username, String password) throws SQLException {
            boolean countedWait = false;
            PooledConnection conn = null;
            long t = System.currentTimeMillis();
            int localBadConnectionCount = 0;
    
            while (conn == null) {
                synchronized (state) {
                    // 检测空闲连接集合(idleConnections)是否为空
                    if (!state.idleConnections.isEmpty()) {
                        // idleConnections 不为空,表示有空闲连接可以使用,直接从空闲连接集合中取出一个连接
                        conn = state.idleConnections.remove(0);
                    } else {
                        /*
                         * 暂无空闲连接可用,但如果活跃连接数还未超出限制
                         *(poolMaximumActiveConnections),则可创建新的连接
                         */
                        if (state.activeConnections.size() < poolMaximumActiveConnections) {
                            // 创建新连接,看到没,还是通过dataSource获取连接,也就是UnpooledDataSource获取连接
                            conn = new PooledConnection(dataSource.getConnection(), this);
                        } else {    // 连接池已满,不能创建新连接
                            // 取出运行时间最长的连接
                            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
                            // 获取运行时长
                            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
                            // 检测运行时长是否超出限制,即超时
                            if (longestCheckoutTime > poolMaximumCheckoutTime) {
                                // 累加超时相关的统计字段
                                state.claimedOverdueConnectionCount++;
                                state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
                                state.accumulatedCheckoutTime += longestCheckoutTime;
    
                                // 从活跃连接集合中移除超时连接
                                state.activeConnections.remove(oldestActiveConnection);
                                // 若连接未设置自动提交,此处进行回滚操作
                                if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                                    try {
                                        oldestActiveConnection.getRealConnection().rollback();
                                    } catch (SQLException e) {...}
                                }
                                /*
                                 * 创建一个新的 PooledConnection,注意,
                                 * 此处复用 oldestActiveConnection 的 realConnection 变量
                                 */
                                conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
                                /*
                                 * 复用 oldestActiveConnection 的一些信息,注意 PooledConnection 中的 
                                 * createdTimestamp 用于记录 Connection 的创建时间,而非 PooledConnection 
                                 * 的创建时间。所以这里要复用原连接的时间信息。
                                 */
                                conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
                                conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
    
                                // 设置连接为无效状态
                                oldestActiveConnection.invalidate();
                                
                            } else {// 运行时间最长的连接并未超时
                                try {
                                    if (!countedWait) {
                                        state.hadToWaitCount++;
                                        countedWait = true;
                                    }
                                    long wt = System.currentTimeMillis();
                                    // 当前线程进入等待状态
                                    state.wait(poolTimeToWait);
                                    state.accumulatedWaitTime += System.currentTimeMillis() - wt;
                                } catch (InterruptedException e) {
                                    break;
                                }
                            }
                        }
                    }
                    if (conn != null) {
                        if (conn.isValid()) {
                            if (!conn.getRealConnection().getAutoCommit()) {
                                // 进行回滚操作
                                conn.getRealConnection().rollback();
                            }
                            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
                            // 设置统计字段
                            conn.setCheckoutTimestamp(System.currentTimeMillis());
                            conn.setLastUsedTimestamp(System.currentTimeMillis());
                            state.activeConnections.add(conn);
                            state.requestCount++;
                            state.accumulatedRequestTime += System.currentTimeMillis() - t;
                        } else {
                            // 连接无效,此时累加无效连接相关的统计字段
                            state.badConnectionCount++;
                            localBadConnectionCount++;
                            conn = null;
                            if (localBadConnectionCount > (poolMaximumIdleConnections
                                + poolMaximumLocalBadConnectionTolerance)) {
                                throw new SQLException(...);
                            }
                        }
                    }
                }
    
            }
            if (conn == null) {
                throw new SQLException(...);
            }
    
            return conn;
        }
    }
    
    1. 如果空闲连接不为空,直接从空闲连接集合中取出一个连接
    2. 无空闲连接,判断连接活跃数是否超过最大限制,如果没超过,直接创建一个连接(也是通过UnpooledDataSource创建)
    3. 如果活跃数达到最大限制
      取出运行时间最长的连接、获取运行时长,检测运行时长是否超出限制,
      如果运行时长超出限制我们直接将超时连接强行中断,并进行回滚,然后复用部分字段重新创建 PooledConnection
      如果运行时长未超出限制,阻塞请求线程等待。

    返回顶部

    MappedStatement

            public final class MappedStatement {
                private String resource;
                private Configuration configuration;
                private String id;
                private Integer fetchSize;
                private Integer timeout;
                private StatementType statementType;
                private ResultSetType resultSetType;
                private SqlSource sqlSource;
                private Cache cache;
                private ParameterMap parameterMap;
                private List<ResultMap> resultMaps;
                private boolean flushCacheRequired;
                private boolean useCache;
                private boolean resultOrdered;
                private SqlCommandType sqlCommandType;
                private KeyGenerator keyGenerator;
                private String[] keyProperties;
                private String[] keyColumns;
                private boolean hasNestedResultMaps;
                private String databaseId;
                private Log statementLog;
                private LanguageDriver lang;
                private String[] resultSets;
            }
    
    1. 在初始化阶段解析的sql语句成品最终存放在这里,执行查询的时候从这里获取,一条数据对应一条sql语句
      返回顶部
  • 相关阅读:
    字符串类型
    数据类型之整型
    数据类型
    两个版本的区别
    变量
    DHCP
    MySQL数据库编译及入门
    NFS网络文件系统
    Rsync 数据同步
    互联网数据分享平台
  • 原文地址:https://www.cnblogs.com/yanhui007/p/12669612.html
Copyright © 2011-2022 走看看