/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.session;
/**
* 结果处理类
* @author Clinton Begin
*/
public interface ResultHandler {
/**
* 处理结果
* @param resultContext 结果上下文
*/
void handleResult(ResultContext extends T> resultContext);
}
DefaultResultHandler
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor.result;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
/**
* 默认ResultHandler,通过ObjectFactory去构建list。本质是一个list
* @author Clinton Begin
*/
public class DefaultResultHandler implements ResultHandler
DefaultMapResultHandler
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor.result;
import java.util.Map;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
/**
* 默认Map的ResultHandler
* @author Clinton Begin
*/
public class DefaultMapResultHandler implements ResultHandler {
/**
* 映射结果
*/
private final Map mappedResults;
/**
* 映射Key
*
*/
private final String mapKey;
/**
* 对象工厂
*/
private final ObjectFactory objectFactory;
/**
* 对象包装工厂
*/
private final ObjectWrapperFactory objectWrapperFactory;
/**
* 反射工厂
*/
private final ReflectorFactory reflectorFactory;
/**
*
* @param mapKey 指定一个字段作为返回Map中的key,这里一般也就是使用唯一键来做key.
* 参考博客:https://blog.csdn.net/u012734441/article/details/85861337
* @param objectFactory 对象工厂
* @param objectWrapperFactory 对象包装工厂
* @param reflectorFactory 反射工厂
*/
@SuppressWarnings("unchecked")
public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
this.mappedResults = objectFactory.create(Map.class);
this.mapKey = mapKey;
}
@Override
public void handleResult(ResultContext extends V> context) {
//获取结果对象
final V value = context.getResultObject();
//value元对象
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
// TODO is that assignment always true?
//获取mapKey的key
final K key = (K) mo.getValue(mapKey);
mappedResults.put(key, value);
}
public Map getMappedResults() {
return mappedResults;
}
}
Cursor
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cursor;
import java.io.Closeable;
/**
* Cursor contract to handle fetching items lazily using an Iterator.
* Cursors are a perfect fit to handle millions of items queries that would not normally fits in memory.
* If you use collections in resultMaps then cursor SQL queries must be ordered (resultOrdered="true")
* using the id columns of the resultMap.
*
* 这个游标通过迭代器来获取一条条数据
* 游标非常适合处理数百万查询数据,这个样百万数据并不适合内存中获取
* 如果在resultMap去收集结果,那么sql查询必须是有序的即设置(在id列进行设置resultOrdered="true")
*
* https://blog.csdn.net/m0_37355951/article/details/97552290
* @author Guillaume Darmont / [email protected]
*/
public interface Cursor extends Closeable, Iterable {
/**
* 游标开始从数据库获取数据这个就返回true
* @return true if the cursor has started to fetch items from database.
*/
boolean isOpen();
/**
* 判断所有元素是否已经获取完
* @return true if the cursor is fully consumed and has returned all elements matching the query.
*/
boolean isConsumed();
/**
* 获取数据的索引,从0开始,如果没有数据就返回-1
* Get the current item index. The first item has the index 0.
* @return -1 if the first cursor item has not been retrieved. The index of the current item retrieved.
*/
int getCurrentIndex();
}
DefaultCursor
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cursor.defaults;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetWrapper;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
/**
* This is the default implementation of a MyBatis Cursor.
* This implementation is not thread safe.
*
* 这个是默认的Mybatis Cursor接口实现类
* 该实例并不是线程安全的
*
*
* 默认的mybatis游标实例
*
*
* @author Guillaume Darmont / [email protected]
*/
public class DefaultCursor implements Cursor {
// ResultSetHandler stuff
/**
* 默认的结果集处理器
*/
private final DefaultResultSetHandler resultSetHandler;
/**
* Mapper.xml的resultMap标签信息封装类对象
*/
private final ResultMap resultMap;
/**
* 结果集包装类对象
*/
private final ResultSetWrapper rsw;
/**
* Mybatis的分页对象
*/
private final RowBounds rowBounds;
/**
* 对象包装结果处理类
*/
private final ObjectWrapperResultHandler objectWrapperResultHandler = new ObjectWrapperResultHandler<>();
/**
* 游标迭代器
*/
private final CursorIterator cursorIterator = new CursorIterator();
/**
* 迭代器打开了的标记,该标记使得迭代器不能多次打开游标,只能打开一次
*/
private boolean iteratorRetrieved;
/**
* 游标的默认状态是创建
*/
private CursorStatus status = CursorStatus.CREATED;
/**
* 分页对象的索引位置为-1
*/
private int indexWithRowBound = -1;
/**
* 游标状态枚举
*/
private enum CursorStatus {
/**
* A freshly created cursor, database ResultSet consuming has not started.
* 刚刚出炉的游标,数据库的结果集还没有被消费
*/
CREATED,
/**
* A cursor currently in use, database ResultSet consuming has started.
* 游标已经被使用,同时结果集数据已经开始被使用了
*/
OPEN,
/**
* A closed cursor, not fully consumed.
* 游标被关闭,可能并没有全部被消费掉
*/
CLOSED,
/**
* A fully consumed cursor, a consumed cursor is always closed.
* 表示游标已经遍历所有数据集,这个消费完游标一直将会被关闭掉
*/
CONSUMED
}
/**
*
* @param resultSetHandler 默认结果集处理器
* @param resultMap Mapper.xml的resultMap标签信息封装类对象
* @param rsw 结果集包装类对象
* @param rowBounds Mybatis的分页对象
*/
public DefaultCursor(DefaultResultSetHandler resultSetHandler, ResultMap resultMap, ResultSetWrapper rsw, RowBounds rowBounds) {
this.resultSetHandler = resultSetHandler;
this.resultMap = resultMap;
this.rsw = rsw;
this.rowBounds = rowBounds;
}
/**
* 游标是否打开了
*/
@Override
public boolean isOpen() {
//如果当前游标状态为OPEN
return status == CursorStatus.OPEN;
}
/**
* 判断游标是否被消费完了
*/
@Override
public boolean isConsumed() {
//如果当前游标状态为CONSUMED
return status == CursorStatus.CONSUMED;
}
/**
* 获取当前的索引
*/
@Override
public int getCurrentIndex() {
//分页偏移量+游标迭代器索引位置
return rowBounds.getOffset() + cursorIterator.iteratorIndex;
}
/**
* 获取迭代器
*/
@Override
public Iterator iterator() {
//如果是已经获取过一次迭代器了
if (iteratorRetrieved) {
//抛出只能迭代异常的异常
throw new IllegalStateException("Cannot open more than one iterator on a Cursor");
}
//如果游标已经关闭
if (isClosed()) {
//抛出游标已关闭异常
throw new IllegalStateException("A Cursor is already closed.");
}
//修改标记,防止重新调用该方法
iteratorRetrieved = true;
//返回游标迭代器
return cursorIterator;
}
/**
* 关闭游标
*/
@Override
public void close() {
//如果游标已经关闭直接返回
if (isClosed()) {
return;
}
//获取结果集
ResultSet rs = rsw.getResultSet();
try {
//如果结果集不为空
if (rs != null) {
//关闭结果集
rs.close();
}
} catch (SQLException e) {
// ignore 忽略关闭后异常
} finally {
//将游标状态设置为已关闭
status = CursorStatus.CLOSED;
}
}
/**
* 使用Mybatis分页对象抓取下一个结果
* @return 下一个结果
*/
protected T fetchNextUsingRowBound() {
//从数据库中获取下一个结果
T result = fetchNextObjectFromDatabase();
//通过下面的while循环,不断获取下一个结果直到达到分页对象设置的开始位置。
//只要结果不为null 且 当前分页索引位置小于分页对象设置的开始位置 就继续获取下一个结果。
while (result != null && indexWithRowBound < rowBounds.getOffset()) {
//从数据库中获取下一个结果
result = fetchNextObjectFromDatabase();
}
//返回结果
return result;
}
/**
* 从数据库中获取下一个结果
* @return 下一个结果
*/
protected T fetchNextObjectFromDatabase() {
// //判断游标是否已经关闭,已关闭返回null
if (isClosed()) {
return null;
}
try {
//设置当前状态是游标打开状态
status = CursorStatus.OPEN;
//如果结果集包装类不是已经关闭
if (!rsw.getResultSet().isClosed()) {
/**
* 构建出来的结果对象,如果父级结果属性映射不为null,会将结果对象赋值到父级结果属性对应的结果对象中,
* 否则将结果对象加入到reusltHandler中
*/
//把结果放入objectWrapperResultHandler对象的result中
resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
// 获取对象包装处理的结果
T next = objectWrapperResultHandler.result;
// 如果结果不为空结果, 索引++
if (next != null) {
indexWithRowBound++;
}
// No more object or limit reached
///next为null或者读取条数等于偏移量+限制条数,偏移量+限制条数=可读取的条数
if (next == null || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit()) {
//关闭游标
close();
//将游标状态修改为已消耗完的关闭状态
status = CursorStatus.CONSUMED;
}
//把结果设置为null
objectWrapperResultHandler.result = null;
//返回结果
return next;
}
/**
* 游标是否已经关闭
*/
private boolean isClosed() {
//如果当前游标状态为CLOSED或者为CONSUMED
return status == CursorStatus.CLOSED || status == CursorStatus.CONSUMED;
}
/**
* 下一个读取索引位置
*/
private int getReadItemsCount() {
//当前分页对象索引位置+1
return indexWithRowBound + 1;
}
/**
* 对象包装结果处理类
*/
private static class ObjectWrapperResultHandler implements ResultHandler {
/**
* 结果对象
*/
private T result;
@Override
public void handleResult(ResultContext extends T> context) {
//获取结果对象
this.result = context.getResultObject();
//标记结果上下文为停止
context.stop();
}
}
/**
* 游标迭代器
*/
private class CursorIterator implements Iterator {
/**
* Holder for the next object to be returned.
* 保存下一个将会被返回的对象
*/
T object;
/**
* 返回下一个对象的索引
* Index of objects returned using next(), and as such, visible to users.
*/
int iteratorIndex = -1;
/**
* 是否有下一个结果
*/
@Override
public boolean hasNext() {
if (object == null) {
object = fetchNextUsingRowBound();
}
return object != null;
}
/**
* 获取下一个结果
*/
@Override
public T next() {
// Fill next with object fetched from hasNext()
T next = object;
//执行过haNext()方法object的值才不会为null
if (next == null) {
////表示没有执行hasNext()方法,所以在获取一次数据
next = fetchNextUsingRowBound();
}
//如果下一个结果不为null
if (next != null) {
//保存下一个将会被返回的对象设置为null
object = null;
//下一个对象的索引++
iteratorIndex++;
//返回下一个结果
return next;
}
//下一个结果为null时,抛出异常
throw new NoSuchElementException();
}
/**
* 不支持删除对象
*/
@Override
public void remove() {
throw new UnsupportedOperationException("Cannot remove element from Cursor");
}
}
}
ResultLoaderMap
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor.loader;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BaseExecutor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
/**
* 用来表示需要懒加载的属性集,本质是一个HashMap
* https://blog.csdn.net/qq_39470742/article/details/88794755
* @author Clinton Begin
* @author Franta Mejta
*/
public class ResultLoaderMap {
/**
* 懒加载属性集
*/
private final Map loaderMap = new HashMap<>();
/**
* 添加加载器到映射
* @param property 属性名
* @param metaResultObject 结果元对象
* @param resultLoader 结果加载器
*/
public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
/**
* 将 {@code property} 转换成大写,如果 {@code property} 属于'xxx.xx.x'格式,
* 则将'.'作为分割符分成数组,去数组的第一个元素转化成大写并返回出去。
*/
String upperFirst = getUppercaseFirstProperty(property);
//如果upperFirst忽略大小写的相等判断property不对应 且 加载器映射队列中已经含有upperFist
if (!upperFirst.equalsIgnoreCase(property) && loaderMap.containsKey(upperFirst)) {
throw new ExecutorException("Nested lazy loaded result property '" + property
+ "' for query id '" + resultLoader.mappedStatement.getId()
+ " already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
}
loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
}
public final Map getProperties() {
return new HashMap<>(this.loaderMap);
}
public Set getPropertyNames() {
return loaderMap.keySet();
}
public int size() {
return loaderMap.size();
}
public boolean hasLoader(String property) {
return loaderMap.containsKey(property.toUpperCase(Locale.ENGLISH));
}
/**
* 执行对 {@code property} 的懒加载查询
* @param property 属性名
* @return 在加载器映射队列中存在该property对应的LoadPair对象时,返回true,否则返回false
*/
public boolean load(String property) throws SQLException {
//获取property对应的LoadPair对象,这里使用的是remove,所以只能获取一次
LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
if (pair != null) {
//执行懒加载查询
pair.load();
return true;
}
return false;
}
public void remove(String property) {
loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
}
/**
* 执行对 加载器映射队列的所有元素 的懒加载查询
*/
public void loadAll() throws SQLException {
final Set methodNameSet = loaderMap.keySet();
String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
for (String methodName : methodNames) {
load(methodName);
}
}
/**
* 将 {@code property} 转换成大写,如果 {@code property} 属于'xxx.xx.x'格式,则将'.'作为
* 分割符分成数组,去数组的第一个元素转化成大写并返回出去。
* @param property 属性名
* @return 大写的 {@code property}
*/
private static String getUppercaseFirstProperty(String property) {
String[] parts = property.split("\\.");
return parts[0].toUpperCase(Locale.ENGLISH);
}
/**
* 对于单个属性懒加载信息的封装,最重要的就是其load方法用来加载属性
*
* Property which was not loaded yet.属性仍然未加载
*/
public static class LoadPair implements Serializable {
private static final long serialVersionUID = 20130412;
/**
* Name of factory method which returns database connection.
*
* 返回数据库连接的工厂方法名
*
*/
private static final String FACTORY_METHOD = "getConfiguration";
/**
* Object to check whether we went through serialization..
*
* 对象用于来检查我们是否经过序列化。
*
*/
private final transient Object serializationCheck = new Object();
/**
* Meta object which sets loaded properties.
*
* 已加载属性的集合元对象
*
*/
private transient MetaObject metaResultObject;
/**
* Result loader which loads unread properties.
*
*/
private transient Log log;
/**
* Factory class through which we get database connection.
*
* 用于获取数据库连接的工厂类
*
*/
private Class> configurationFactory;
/**
* Name of the unread property.
*
* 未读取的属性名
*
*/
private String property;
/**
* ID of SQL statement which loads the property.
*
* 用于加载 {@link #property} 的ID的SQL脚本
*
*/
private String mappedStatement;
/**
* Parameter of the sql statement.
*
* {@link #mappedStatement} 的参数对象
*
*/
private Serializable mappedParameter;
/**
*
* @param property 属性名
* @param metaResultObject 已加载属性的集合元对象
* @param resultLoader 结果加载器
*/
private LoadPair(final String property, MetaObject metaResultObject, ResultLoader resultLoader) {
this.property = property;
this.metaResultObject = metaResultObject;
this.resultLoader = resultLoader;
//保存所需的信息只有原始对象可以序列化;
/* Save required information only if original object can be serialized. */
if (metaResultObject != null && metaResultObject.getOriginalObject() instanceof Serializable) {
//获取参数对象
final Object mappedStatementParameter = resultLoader.parameterObject;
/* @todo May the parameter be null? */
if (mappedStatementParameter instanceof Serializable) {
// Mapper.xml文件的select,delete,update,insert这些DML标签的封装类对象的Id
this.mappedStatement = resultLoader.mappedStatement.getId();
//参数对象序列化赋值
this.mappedParameter = (Serializable) mappedStatementParameter;
//获取数据库连接的工厂类
this.configurationFactory = resultLoader.configuration.getConfigurationFactory();
} else {
//日志信息打印
Log log = this.getLogger();
if (log.isDebugEnabled()) {
log.debug("Property [" + this.property + "] of ["
+ metaResultObject.getOriginalObject().getClass() + "] cannot be loaded "
+ "after deserialization. Make sure it's loaded before serializing "
+ "forenamed object.");
}
}
}
}
/**
* 执行懒加载查询
*/
public void load() throws SQLException {
/* These field should not be null unless the loadpair was serialized.
* Yet in that case this method should not be called. */
//这些字段不能空,除非loadpair序列化,然而,在这种情况下这个方法不应该被称为。
if (this.metaResultObject == null) {
throw new IllegalArgumentException("metaResultObject is null");
}
if (this.resultLoader == null) {
throw new IllegalArgumentException("resultLoader is null");
}
this.load(null);
}
/**
* 执行懒加载查询,获取数据并且赋值到userObject中返回
* @param userObject 用户对象
*/
public void load(final Object userObject) throws SQLException {
//合法性校验
if (this.metaResultObject == null || this.resultLoader == null) {
if (this.mappedParameter == null) {
throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
+ "required parameter of mapped statement ["
+ this.mappedStatement + "] is not serializable.");
}
//获取mappedstatement并且校验
final Configuration config = this.getConfiguration();
final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
if (ms == null) {
throw new ExecutorException("Cannot lazy load property [" + this.property
+ "] of deserialized object [" + userObject.getClass()
+ "] because configuration does not contain statement ["
+ this.mappedStatement + "]");
}
//使用userObject构建metaobject
this.metaResultObject = config.newMetaObject(userObject);
//重新构建resultloader对象
this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
metaResultObject.getSetterType(this.property), null, null);
}
/* We are using a new executor because we may be (and likely are) on a new thread
* and executors aren't thread safe. (Is this sufficient?)
*
* A better approach would be making executors thread safe. */
/**
* 我们使用一个新的执行器,因为可能是新的线程和执行器不是线程安全
* 一个更好的方法会使执行器线程安全
*/
/**
* this.serializationCheck == null表示如果序列化过,因为serializationCheck
* 被tranisent关键字修饰着,所以不参与对象序列化,所以在
* 反序列化后serializationCheck会变成null,从而得知loadPair对象是否序列化过
*/
if (this.serializationCheck == null) {
final ResultLoader old = this.resultLoader;
//新建一个新的resultLoader对象,
this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
}
/**
* 调用resultLoader的loadResult获取查询结果并赋值到metaResultObject中。
*
* 这里可能有会有疑问,resultLoader调用loadResult方法因为其resultLoader对象的executoer对象
* 经过反序列后,executoer对象会被赋值成ClosedExecutoer对象,当调用ClosedExecutoer对象的
* 关于执行SQL脚本的方法时会抛出异常,那岂不是得不到查询结果?
* 其实resultLoader并没有使用ClosedExecutoer对象直接执行SQL脚本,而是调用executoer对象的
* isClose()看看执行器是否已经关闭,若关闭了,通过构建一个SimpleExecutor对象来执行SQL脚本
*/
this.metaResultObject.setValue(property, this.resultLoader.loadResult());
}
/**
* 获取配置
*/
private Configuration getConfiguration() {
//如果数据库连接工厂类为null
if (this.configurationFactory == null) {
throw new ExecutorException("Cannot get Configuration as configuration factory was not set.");
}
Object configurationObject;
try {
//获取工厂对象的返回数据库连接方法
final Method factoryMethod = this.configurationFactory.getDeclaredMethod(FACTORY_METHOD);
//如果返回数据库连接方法不是静态方法
if (!Modifier.isStatic(factoryMethod.getModifiers())) {
throw new ExecutorException("Cannot get Configuration as factory method ["
+ this.configurationFactory + "]#["
+ FACTORY_METHOD + "] is not static.");
}
//如果返回数据库连接方法不能访问
if (!factoryMethod.isAccessible()) {
/**
* AccessController.doPrivileged是一个在AccessController类中的静态方法,允许在一个类实例中的代码通知这个AccessController:它的代码主体是享受"privileged(特权的)",它单独负责对它的可得的资源的访问请求,而不管这个请求是由什么代码所引发的。
* 这就是说,一个调用者在调用doPrivileged方法时,可被标识为 "特权"。在做访问控制决策时,如果checkPermission方法遇到一个通过doPrivileged调用而被表示为 "特权"的调用者,并且没有上下文自变量,checkPermission方法则将终止检查。如果那个调用者的域具有特定的许可,则不做进一步检查,checkPermission安静地返回,表示那个访问请求是被允许的;如果那个域没有特定的许可,则象通常一样,一个异常被抛出。
*/
configurationObject = AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
try {
//设置成可访问
factoryMethod.setAccessible(true);
//执行方法
return factoryMethod.invoke(null);
} finally {
//设置成不可访问
factoryMethod.setAccessible(false);
}
});
} else {
//执行方法
configurationObject = factoryMethod.invoke(null);
}
} catch (final ExecutorException ex) {
throw ex;
} catch (final NoSuchMethodException ex) {
throw new ExecutorException("Cannot get Configuration as factory class ["
+ this.configurationFactory + "] is missing factory method of name ["
+ FACTORY_METHOD + "].", ex);
} catch (final PrivilegedActionException ex) {
throw new ExecutorException("Cannot get Configuration as factory method ["
+ this.configurationFactory + "]#["
+ FACTORY_METHOD + "] threw an exception.", ex.getCause());
} catch (final Exception ex) {
throw new ExecutorException("Cannot get Configuration as factory method ["
+ this.configurationFactory + "]#["
+ FACTORY_METHOD + "] threw an exception.", ex);
}
//如果configurationObject没有继承Configuration
if (!(configurationObject instanceof Configuration)) {
throw new ExecutorException("Cannot get Configuration as factory method ["
+ this.configurationFactory + "]#["
+ FACTORY_METHOD + "] didn't return [" + Configuration.class + "] but ["
+ (configurationObject == null ? "null" : configurationObject.getClass()) + "].");
}
//将configuationObject强制转换成Configuration类型
return Configuration.class.cast(configurationObject);
}
private Log getLogger() {
if (this.log == null) {
this.log = LogFactory.getLog(this.getClass());
}
return this.log;
}
}
/**
* 已关闭的执行器,不能执行任何SQL脚本,isClosed方法为true,表示已经关闭。
* 如果执行任何SQL脚本,都会抛出执行UnsupportedOperationException异常
*/
private static final class ClosedExecutor extends BaseExecutor {
public ClosedExecutor() {
super(null, null);
}
@Override
public boolean isClosed() {
return true;
}
@Override
protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
throw new UnsupportedOperationException("Not supported.");
}
@Override
protected List doFlushStatements(boolean isRollback) throws SQLException {
throw new UnsupportedOperationException("Not supported.");
}
@Override
protected List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
throw new UnsupportedOperationException("Not supported.");
}
@Override
protected Cursor doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
throw new UnsupportedOperationException("Not supported.");
}
}
}
RowBound
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.session;
/**
* Mybatis的分页对象
*
* 参考博客:https://www.jianshu.com/p/84599f9a49d0
* @author Clinton Begin
*/
public class RowBounds {
/**
* 从第0条开始
*/
public static final int NO_ROW_OFFSET = 0;
/**
* 取最大记录数
*/
public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
public static final RowBounds DEFAULT = new RowBounds();
/**
* 从第几条开始
*/
private final int offset;
/**
* 取多少条
*/
private final int limit;
public RowBounds() {
this.offset = NO_ROW_OFFSET;
this.limit = NO_ROW_LIMIT;
}
/**
*
* @param offset 从第几条开始
* @param limit 取多少条
*/
public RowBounds(int offset, int limit) {
this.offset = offset;
this.limit = limit;
}
public int getOffset() {
return offset;
}
public int getLimit() {
return limit;
}
}
在平时工作中,难免会遇到把 XML 作为数据存储格式。面对目前种类繁多的解决方案,哪个最适合我们呢?在这篇文章中,我对这四种主流方案做一个不完全评测,仅仅针对遍历 XML 这块来测试,因为遍历 XML 是工作中使用最多的(至少我认为)。 预 备 测试环境: AMD 毒龙1.4G OC 1.5G、256M DDR333、Windows2000 Server
Netty 3.x的user guide里FrameDecoder的例子,有几个疑问:
1.文档说:FrameDecoder calls decode method with an internally maintained cumulative buffer whenever new data is received.
为什么每次有新数据到达时,都会调用decode方法?
2.Dec
hive> select * from t_test where ds=20150323 limit 2;
OK
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
问题原因: hive堆内存默认为256M
这个问题的解决方法为:
修改/us
Simply do the following:
I. Declare a global variable:
var markersArray = [];
II. Define a function:
function clearOverlays() {
for (var i = 0; i < markersArray.length; i++ )
Quick sort is probably used more widely than any other. It is popular because it is not difficult to implement, works well for a variety of different kinds of input data, and is substantially faster t