mybatis属性类
Posted yanhui007
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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&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>
- environment中包含事务和数据源
- 事务配置支持两种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);
}
}
- JdbcTransaction是mybatis默认的事务类型
- 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;
- BoundSql语句的解析主要是通过对#{}字符的解析,将其替换成?。最后均包装成预表达式供PrepareStatement调用执行
-
{}中的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;
}
}
// 省略部分代码
}
- DynamicContext是SQL语句构建的上下文,每个SQL片段解析完成后,都会将解析结果存入DynamicContext中。待所有的SQL片段解析完毕后,一条完整的SQL语句就会出现在 DynamicContext 对象中。
SqlNode
- 对于一个包含了 ${} 占位符,或
、 等标签的 SQL,在解析的过程中,会被分解成多个片段。每个片段都有对应的类型,每种类型的片段都有不同的解析逻辑。在源码中,片段这个概念等价于 sql 节点,即 SqlNode。 - 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;
}
}
- 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;
}
}
- 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;
}
}
}
- 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;
}
}
- 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());
}
}
}
- 通过StatementHandler可以获取Statement对象
- 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;
}
- DataSource分两种,一种是使用连接池,一种是普通的DataSource
- 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;
}
- 不具有池化特性,每次会返回一个新的数据库连接,而非复用旧的连接
- 提供了三个方法:
- 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();
}
}
- 通过反射机制加载驱动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());
}
}
}
- 将一些配置信息放入到 Properties 对象中
- 循环所有注册的驱动,然后通过驱动进行连接,所有的驱动都会尝试连接,连接成功返回连接
配置数据库连接
private void configureConnection(Connection conn) throws SQLException {
if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
// 设置自动提交
conn.setAutoCommit(autoCommit);
}
if (defaultTransactionIsolationLevel != null) {
// 设置事务隔离级别
conn.setTransactionIsolation(defaultTransactionIsolationLevel);
}
}
- 设置是否自动提交、事务隔离级别属性
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;
}
- PooledDataSource 内部实现了连接池功能,用于复用数据库连接。因此,从效率上来说,PooledDataSource 要高于 UnpooledDataSource。但是最终获取Connection还是通过UnpooledDataSource,只不过PooledDataSource 提供一个存储Connection的功能。
- 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;
}
}
- 如果空闲连接不为空,直接从空闲连接集合中取出一个连接
- 无空闲连接,判断连接活跃数是否超过最大限制,如果没超过,直接创建一个连接(也是通过UnpooledDataSource创建)
- 如果活跃数达到最大限制
取出运行时间最长的连接、获取运行时长,检测运行时长是否超出限制,
如果运行时长超出限制我们直接将超时连接强行中断,并进行回滚,然后复用部分字段重新创建 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;
}
- 在初始化阶段解析的sql语句成品最终存放在这里,执行查询的时候从这里获取,一条数据对应一条sql语句
返回顶部
以上是关于mybatis属性类的主要内容,如果未能解决你的问题,请参考以下文章
[mybatis]动态sql_sql_抽取可重用的sql片段
Mybatis -- 动态Sql概述动态Sql之<if>(包含<where>)动态Sql之<foreach>sql片段抽取