Mybatis的日志
一般都会使用工厂类来生成log对象,mybatis使用的自己的LogFactory,使用静态代码块来初始化logger对象
static {
tryImplementation(new Runnable() {
public void run() {
useSlf4jLogging(); //这个地方没有使用线程的方式,都是按顺序执行的。当某一个logger类被初始化成功后,后面的logger类就不会在初始化。
}
});
tryImplementation(new Runnable() {
public void run() {
useCommonsLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
useLog4J2Logging();
}
});
tryImplementation(new Runnable() {
public void run() {
useLog4JLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
useJdkLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
useNoLogging();
}
});
}
mybatis对大部分主流的日志框架都包装了一层。已slf4j为例:
setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
实例化封装好的slf4j类。
public Slf4jImpl(String clazz) {
Logger logger = LoggerFactory.getLogger(clazz);
if (logger instanceof LocationAwareLogger) {
try {
// check for slf4j >= 1.6 method signature
logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger); //采用的slf4j的LocationAwareLogger
return;
} catch (SecurityException e) {
// fail-back to Slf4jLoggerImpl
} catch (NoSuchMethodException e) {
// fail-back to Slf4jLoggerImpl
}
}
// Logger is not LocationAwareLogger or slf4j version < 1.6
log = new Slf4jLoggerImpl(logger);//这里做了一个兼容操作,低版本的时候采用的装饰器的操作,实际操作的是slf4j的logger日志操作
}
取得slf4j的logger对象,应为slf4j本身已经对主流框架做了选择的操作,所有mybatis优先选用这个框架。
有两种方案可以采用:
第一种是装饰器的模式,持有实际操作的对象,使用统一的接口来操作日志。
第二种是采用适配器模式,将某个类适配到目标类上面。继承目标接口,操作实际类的方法。
Mybatis的ErrorContext
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
内部是从线程里面拿取的ErrorContext的实例,保证在一个线程内部都可以取到同一个
ErrorContext
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();
public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) { --从ThreadLocal里面获取ErrorContext,如果没有context实例,先建一个。
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
public ErrorContext store() {
stored = this;
LOCAL.set(new ErrorContext());
return LOCAL.get();
}
Mybatis的session缓存
在BaseExecutor里面,在查询里面在session内,都保持同一个localCache来存储数据,内部使用的是HashMap来存储数据。
PerpetualCache:
private Map<Object, Object> cache = new HashMap<Object, Object>(); --存储结果集,根据key值来获取和存储数据
key的生成规则是CacheKey来决定的。
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
看下里面的equals和hashcode方法就知道哪些决定这个cachekey唯一性:
public boolean equals(Object object) {
if (this == object)
return true;
if (!(object instanceof CacheKey))
return false;
final CacheKey cacheKey = (CacheKey) object;
if (hashcode != cacheKey.hashcode)
return false;
if (checksum != cacheKey.checksum)
return false;
if (count != cacheKey.count)
return false;
for (int i = 0; i < updateList.size(); i++) {
Object thisObject = updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
if (thisObject == null) {
if (thatObject != null)
return false;
} else {
if (!thisObject.equals(thatObject))
return false;
}
}
return true;
}
比较的是updateList里面的值是否相等,在前面放置了多个值,确定每一个都是相等的时候,产生的cachekey才是一致的。
private void doUpdate(Object object) {
int baseHashCode = object == null ? 1 : object.hashCode();
count++;
checksum += baseHashCode;
baseHashCode *= count; --这个是用来检验个数的
hashcode = multiplier * hashcode + baseHashCode; --这个是用来生成新的HashCode,使用数据的hashCode来生成新的hashCode,来作为判断依据。
updateList.add(object);
}
有个注意的点是外部的对象的如果非java基本数据类型的时候,hashCode的生成都需要被重写,保证在某些情况下是一致的。
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) throw new ExecutorException("Executor was closed.");
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId()); --String对象
cacheKey.update(rowBounds.getOffset()); --int型的位移
cacheKey.update(rowBounds.getLimit()); --int型的限制
cacheKey.update(boundSql.getSql()); --String型的sql
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
for (int i = 0; i < parameterMappings.size(); i++) { // mimic DefaultParameterHandler logic
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
return cacheKey;
}