java基礎的用法

Mybatis的日誌

通常都會使用工廠類來生成log對象,mybatis使用的本身的LogFactory,使用靜態代碼塊來初始化logger對象 java

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優先選用這個框架。

有兩種方案能夠採用: sql

第一種是裝飾器的模式,持有實際操做的對象,使用統一的接口來操做日誌。 apache

第二種是採用適配器模式,將某個類適配到目標類上面。繼承目標接口,操做實際類的方法。 緩存

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來存儲數據。 session

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;
  }
相關文章
相關標籤/搜索