diff --git a/docs/Mybatis/基础支持层/Mybatis-Cache.md b/docs/Mybatis/基础支持层/Mybatis-Cache.md new file mode 100644 index 0000000..512b1bc --- /dev/null +++ b/docs/Mybatis/基础支持层/Mybatis-Cache.md @@ -0,0 +1,319 @@ +# mybatis 缓存 +- Author: [HuiFer](https://github.com/huifer) +- Description: 该文介绍 mybatis Cache 源码 +- `org.apache.ibatis.cache.Cache` +```java +public interface Cache { + + String getId(); + + /** + * 放入数据 + */ + void putObject(Object key, Object value); + + /** + * 获取数据 + */ + Object getObject(Object key); + + /** + * 移除数据 + */ + Object removeObject(Object key); + + /** + * 清空数据 + */ + void clear(); + + /** + * 有多少缓存数据 + */ + int getSize(); + + /** + * 重入锁 + * @return A ReadWriteLock + */ + default ReadWriteLock getReadWriteLock() { + return null; + } + +} +``` + +- BlockingCache: 阻塞的缓存 +- FifoCache: 按对象进入缓存的顺序来移除它们。 +- LruCache: 最近最少使用的:移除最长时间不被使用的对象。 +- SoftCache: 软引用:移除基于垃圾回收器状态和软引用规则的对象 +- WeakCache: 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 + +## BlockingCache +- BlockingCache 内部使用了`ReentrantLock`来进行加锁开锁这个操作.在插入缓存时上锁,插入缓存后释放.请求缓存值得时候同理 +```java +public class BlockingCache implements Cache { + + private final Cache delegate; + /** + * 线程安全的map + */ + private final ConcurrentHashMap locks; + private long timeout; + + public BlockingCache(Cache delegate) { + this.delegate = delegate; + this.locks = new ConcurrentHashMap<>(); + } + + @Override + public String getId() { + return delegate.getId(); + } + + @Override + public int getSize() { + return delegate.getSize(); + } + + @Override + public void putObject(Object key, Object value) { + try { + delegate.putObject(key, value); + } finally { + releaseLock(key); + } + } + + @Override + public Object getObject(Object key) { + acquireLock(key); + Object value = delegate.getObject(key); + if (value != null) { + // 释放锁 + releaseLock(key); + } + return value; + } + + @Override + public Object removeObject(Object key) { + // despite of its name, this method is called only to release locks + releaseLock(key); + return null; + } + + @Override + public void clear() { + delegate.clear(); + } + + private ReentrantLock getLockForKey(Object key) { + return locks.computeIfAbsent(key, k -> new ReentrantLock()); + } + + /** + * 请求锁 + * @param key + */ + private void acquireLock(Object key) { + Lock lock = getLockForKey(key); + if (timeout > 0) { + try { + // 上锁 + boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS); + if (!acquired) { + throw new CacheException("Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId()); + } + } catch (InterruptedException e) { + throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e); + } + } else { + lock.lock(); + } + } + + /** + * 释放锁 + * @param key + */ + private void releaseLock(Object key) { + ReentrantLock lock = locks.get(key); + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + + public long getTimeout() { + return timeout; + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } +} +``` + +## FifoCache +- 存储结构是`java.util.LinkedList` +```java +public class FifoCache implements Cache { + + private final Cache delegate; + /** + * 队列 + */ + private final Deque keyList; + private int size; + + public FifoCache(Cache delegate) { + this.delegate = delegate; + this.keyList = new LinkedList<>(); + this.size = 1024; + } + + @Override + public String getId() { + return delegate.getId(); + } + + @Override + public int getSize() { + return delegate.getSize(); + } + + public void setSize(int size) { + this.size = size; + } + + @Override + public void putObject(Object key, Object value) { + cycleKeyList(key); + delegate.putObject(key, value); + } + + @Override + public Object getObject(Object key) { + return delegate.getObject(key); + } + + @Override + public Object removeObject(Object key) { + return delegate.removeObject(key); + } + + @Override + public void clear() { + delegate.clear(); + keyList.clear(); + } + + /** + * 添加 key 删除最开始的一个 + * + * @param key + */ + private void cycleKeyList(Object key) { + keyList.addLast(key); + if (keyList.size() > size) { + Object oldestKey = keyList.removeFirst(); + delegate.removeObject(oldestKey); + } + } + +} +``` + +## LruCache +- 存储结构是`java.util.LinkedHashMap` +```java +/** + * Lru (least recently used) cache decorator. + * LRU 緩存策略 最近最少使用的:移除最长时间不被使用的对象。 + * + * @author Clinton Begin + */ +public class LruCache implements Cache { + + private final Cache delegate; + /** + * {@link LinkedHashMap} + */ + private Map keyMap; + private Object eldestKey; + + public LruCache(Cache delegate) { + this.delegate = delegate; + setSize(1024); + } + + @Override + public String getId() { + return delegate.getId(); + } + + @Override + public int getSize() { + return delegate.getSize(); + } + + /** + * 设置大小 + * + * @param size + */ + public void setSize(final int size) { + keyMap = new LinkedHashMap(size, .75F, true) { + private static final long serialVersionUID = 4267176411845948333L; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + // 数量超出预设值 执行 + boolean tooBig = size() > size; + if (tooBig) { +// 获取被移除的key + eldestKey = eldest.getKey(); + } + return tooBig; + } + }; + } + + @Override + public void putObject(Object key, Object value) { + delegate.putObject(key, value); + cycleKeyList(key); + } + + @Override + public Object getObject(Object key) { + keyMap.get(key); //touch + return delegate.getObject(key); + } + + @Override + public Object removeObject(Object key) { + return delegate.removeObject(key); + } + + @Override + public void clear() { + delegate.clear(); + keyMap.clear(); + } + + /** + * 删除最早的一个key + * @param key + */ + private void cycleKeyList(Object key) { + keyMap.put(key, key); + if (eldestKey != null) { + delegate.removeObject(eldestKey); + eldestKey = null; + } + } + +} +``` + diff --git a/docs/Mybatis/核心处理层/Mybatis-Alias.md b/docs/Mybatis/核心处理层/Mybatis-Alias.md new file mode 100644 index 0000000..e0d4b38 --- /dev/null +++ b/docs/Mybatis/核心处理层/Mybatis-Alias.md @@ -0,0 +1,90 @@ +# Mybatis Alias +- Author: [HuiFer](https://github.com/huifer) +- Description: 该文介绍 mybatis Alias 源码 +- 源码位置 :`org.apache.ibatis.type.Alias` +- 与 Alias 相关的一个方法`org.apache.ibatis.type.TypeAliasRegistry.registerAlias(java.lang.String, java.lang.Class)`(别名注册) + + +```java + /** + * 别名注册, + * typeAliases 是一个map key=>别名,value=>字节码 + * + * @param alias 别名名称 + * @param value 别名的字节码 + */ + public void registerAlias(String alias, Class value) { + if (alias == null) { + throw new TypeException("The parameter alias cannot be null"); + } + // issue #748 + String key = alias.toLowerCase(Locale.ENGLISH); + if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) { + throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'."); + } + typeAliases.put(key, value); + } + +``` + +- registerAlias 操作的对象是一个`map`对象 +```java + + /** + * 别名存放仓库 + * 是一个map key=>别名,value=>字节码 + */ + private final Map> typeAliases = new HashMap<>(); +``` +不难看出这个对象存放的内容是 别名 -> clazz. +- 相关注解`Alias` +```java +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Alias { + /** + * Return the alias name. + * + * @return the alias name + */ + String value(); +} +``` +- 看一下实现方式 +```java + /** + * 加载{@link Alias} 注解的内容 + * + * @param type + */ + public void registerAlias(Class type) { + String alias = type.getSimpleName(); + Alias aliasAnnotation = type.getAnnotation(Alias.class); + if (aliasAnnotation != null) { + // 获取 别名注解 + alias = aliasAnnotation.value(); + } + // 转换为 别名,clazz + registerAlias(alias, type); + } +``` +最后回到了`org.apache.ibatis.type.TypeAliasRegistry.registerAlias(java.lang.String, java.lang.Class)`方法 +我们可以简单编写一个测试类 +```java +@Alias(value = "hc") +public class Hc { +} + + /** + * 对注解 {@link Alias} 的测试用例 + */ + @Test + void testAnnotation() { + TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry(); + typeAliasRegistry.registerAlias(Hc.class); + assertEquals("org.apache.ibatis.type.Hc", typeAliasRegistry.resolveAlias("hc").getName()); + } + +``` +其他与`Alias`相关的测试类位于: `org.apache.ibatis.type.TypeAliasRegistryTest` \ No newline at end of file diff --git a/docs/Mybatis/核心处理层/Mybatis-Cursor.md b/docs/Mybatis/核心处理层/Mybatis-Cursor.md new file mode 100644 index 0000000..249d9b2 --- /dev/null +++ b/docs/Mybatis/核心处理层/Mybatis-Cursor.md @@ -0,0 +1,337 @@ +# Mybatis Cursor +- Author: [HuiFer](https://github.com/huifer) +- Description: 该文介绍 mybatis Cursor 源码 +## Cursor +- 源码位置:`org.apache.ibatis.cursor.Cursor` +- 继承`Iterable`说明是一个迭代器,继承`Closeable`说明有一个东西需要关闭 +```java +public interface Cursor extends Closeable, Iterable { + + /** + * 游标开始从数据库获取数据,返回true,反之false + * + * @return true if the cursor has started to fetch items from database. + */ + boolean isOpen(); + + /** + * 数据库元素都被获取,返回true,反之false + * + * @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 +```java +public class DefaultCursor implements Cursor { + + /** + * 对象包装结果处理类 + */ + protected final ObjectWrapperResultHandler objectWrapperResultHandler = new ObjectWrapperResultHandler<>(); + // ResultSetHandler stuff + /** + * ResultSet 处理器 + */ + private final DefaultResultSetHandler resultSetHandler; + /** + * 结果映射 + */ + private final ResultMap resultMap; + /** + * ResultSet 包装对象 + */ + private final ResultSetWrapper rsw; + /** + * 分页的 + */ + private final RowBounds rowBounds; + /** + * 游标的迭代器 + */ + private final CursorIterator cursorIterator = new CursorIterator(); + + /** + * 游标开启判断 + */ + private boolean iteratorRetrieved; + + /** + * 游标状态,默认是创建未使用 + */ + private CursorStatus status = CursorStatus.CREATED; + /** + * 分页索引,默认-1 + */ + private int indexWithRowBound = -1; + + /** + * 构造方法 + * + * @param resultSetHandler + * @param resultMap + * @param rsw + * @param rowBounds + */ + 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() { + return status == CursorStatus.OPEN; + } + + @Override + public boolean isConsumed() { + return status == CursorStatus.CONSUMED; + } + + /** + * 当前索引 + * @return + */ + @Override + public int getCurrentIndex() { + return rowBounds.getOffset() + cursorIterator.iteratorIndex; + } + + /** + * 迭代器获取 + * @return + */ + @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; + } + + /** + * {@link Closeable} 关闭{@link ResultSet} + */ + @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; + } + } + + /** + * 去到真正的数据行 + * @return + */ + protected T fetchNextUsingRowBound() { + T result = fetchNextObjectFromDatabase(); + while (objectWrapperResultHandler.fetched && indexWithRowBound < rowBounds.getOffset()) { + result = fetchNextObjectFromDatabase(); + } + return result; + } + + /** + * 从数据库获取数据 + * @return + */ + protected T fetchNextObjectFromDatabase() { + if (isClosed()) { + return null; + } + + try { + objectWrapperResultHandler.fetched = false; + // 游标状态设置 + status = CursorStatus.OPEN; + if (!rsw.getResultSet().isClosed()) { + // 处理数据结果放入,objectWrapperResultHandler + resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + + // 获取处理结果 + T next = objectWrapperResultHandler.result; + // 结果不为空 + if (objectWrapperResultHandler.fetched) { + // 索引+1 + indexWithRowBound++; + } + // No more object or limit reached + // 如果没有数据, 或者 当前读取条数= 偏移量+限额量 + if (!objectWrapperResultHandler.fetched || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit()) { + // 关闭游标 + close(); + status = CursorStatus.CONSUMED; + } + // 设置结果为null + objectWrapperResultHandler.result = null; + + return next; + } + + /** + * 是否关闭状态判断 + * + * @return + */ + private boolean isClosed() { + return status == CursorStatus.CLOSED || status == CursorStatus.CONSUMED; + } + + /** + * 下一个索引 + * @return + */ + private int getReadItemsCount() { + return indexWithRowBound + 1; + } + + /** + * 游标的状态 + */ + private enum CursorStatus { + + /** + * 新创建的游标, ResultSet 还没有使用过 + * A freshly created cursor, database ResultSet consuming has not started. + */ + CREATED, + /** + * 游标使用过, ResultSet 被使用 + * 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 + */ + protected static class ObjectWrapperResultHandler implements ResultHandler { + + /** + * 数据结果 + */ + protected T result; + /** + * 是否null + */ + protected boolean fetched; + + /** + * 从{@link ResultContext} 获取结果对象 + * @param context + */ + @Override + public void handleResult(ResultContext context) { + this.result = context.getResultObject(); + context.stop(); + fetched = true; + } + } + + /** + * 游标迭代器 + */ + protected 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; + + /** + * 是否有下一个值 + * @return + */ + @Override + public boolean hasNext() { + if (!objectWrapperResultHandler.fetched) { + object = fetchNextUsingRowBound(); + } + return objectWrapperResultHandler.fetched; + } + + /** + * 下一个值 + * @return + */ + @Override + public T next() { + // Fill next with object fetched from hasNext() + T next = object; + + if (!objectWrapperResultHandler.fetched) { + next = fetchNextUsingRowBound(); + } + + if (objectWrapperResultHandler.fetched) { + objectWrapperResultHandler.fetched = false; + object = null; + iteratorIndex++; + return next; + } + throw new NoSuchElementException(); + } + + /** + * 不可执行抛出异常 + */ + @Override + public void remove() { + throw new UnsupportedOperationException("Cannot remove element from Cursor"); + } + } +} +``` \ No newline at end of file diff --git a/docs/Mybatis/核心处理层/Mybatis-DataSource.md b/docs/Mybatis/核心处理层/Mybatis-DataSource.md new file mode 100644 index 0000000..2865813 --- /dev/null +++ b/docs/Mybatis/核心处理层/Mybatis-DataSource.md @@ -0,0 +1,358 @@ +# Mybatis DataSource +- Author: [HuiFer](https://github.com/huifer) +- Description: 该文介绍 mybatis DataSource 源码 +- `org.apache.ibatis.datasource.DataSourceFactory` +```java +/** + * 数据源工厂 + * @author Clinton Begin + */ +public interface DataSourceFactory { + + /** + * 设置 dataSource 属性 + * @param props + */ + void setProperties(Properties props); + + /** + * 获取 dataSource + * @return {@link DataSource} + */ + DataSource getDataSource(); + +} + +``` + +类图如下 + +![image-20191223081023730](/images/mybatis/image-20191223081023730.png) + +- `setProperties`会将下列标签放入`datasource`中 + +```java + + + + + + +``` + + + + + + + + + +- 在`org.apache.ibatis.session.Configuration`中有配置下面三个信息 +```java + typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); + typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); + typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); + +``` + + + + + + + + + +## JndiDataSourceFactory + +```java +/** + * @author Clinton Begin + */ +public class JndiDataSourceFactory implements DataSourceFactory { + + public static final String INITIAL_CONTEXT = "initial_context"; + public static final String DATA_SOURCE = "data_source"; + public static final String ENV_PREFIX = "env."; + + /** + * 直接 java 数据源 + */ + private DataSource dataSource; + + /** + * 获取数据源的配置信息 + * @param allProps + * @return + */ + private static Properties getEnvProperties(Properties allProps) { + final String PREFIX = ENV_PREFIX; + Properties contextProperties = null; + for (Entry entry : allProps.entrySet()) { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + // 只获取前缀`env` + if (key.startsWith(PREFIX)) { + if (contextProperties == null) { + contextProperties = new Properties(); + } + // 放入数据 + contextProperties.put(key.substring(PREFIX.length()), value); + } + } + return contextProperties; + } + + /** + * 设置数据源属性 + * @param properties + */ + @Override + public void setProperties(Properties properties) { + try { + InitialContext initCtx; + Properties env = getEnvProperties(properties); + if (env == null) { + initCtx = new InitialContext(); + } else { + initCtx = new InitialContext(env); + } + + if (properties.containsKey(INITIAL_CONTEXT) + && properties.containsKey(DATA_SOURCE)) { + // 如果包含`initial_context`和`data_source` + Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT)); + dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE)); + } else if (properties.containsKey(DATA_SOURCE)) { + dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE)); + } + + } catch (NamingException e) { + throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e); + } + } + + @Override + public DataSource getDataSource() { + return dataSource; + } + +} +``` + +## PooledDataSource + +```java + protected int poolMaximumActiveConnections = 10; + protected int poolMaximumIdleConnections = 5; + protected int poolMaximumCheckoutTime = 20000; + protected int poolTimeToWait = 20000; + protected int poolMaximumLocalBadConnectionTolerance = 3; + protected String poolPingQuery = "NO PING QUERY SET"; + protected boolean poolPingEnabled; + protected int poolPingConnectionsNotUsedFor; +``` + + + +## PooledDataSourceFactory + +```java +public class PooledDataSourceFactory extends UnpooledDataSourceFactory { + + + public PooledDataSourceFactory() { + this.dataSource = new PooledDataSource(); + } + +} + + // 初始化 + public PooledDataSource() { + dataSource = new UnpooledDataSource(); + } +``` + + + +## UnpooledDataSourceFactory + +```java + @Override + public void setProperties(Properties properties) { + Properties driverProperties = new Properties(); + //metaDataSource 现在是一个dataSource + MetaObject metaDataSource = SystemMetaObject.forObject(dataSource); + for (Object key : properties.keySet()) { + String propertyName = (String) key; + if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) { + // 如果是 driver. 前缀开头 + String value = properties.getProperty(propertyName); + driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value); + } else if (metaDataSource.hasSetter(propertyName)) { + String value = (String) properties.get(propertyName); + Object convertedValue = convertValue(metaDataSource, propertyName, value); + // 通过 metaDataSource 来对 dataSource 进行设置属性 + metaDataSource.setValue(propertyName, convertedValue); + } else { + throw new DataSourceException("Unknown DataSource property: " + propertyName); + } + } + if (driverProperties.size() > 0) { + metaDataSource.setValue("driverProperties", driverProperties); + } + } + +``` + + + +## UnpooledDataSource + +- `org.apache.ibatis.datasource.unpooled.UnpooledDataSource`主要定义数据库连接相关的一些属性,以及与数据库的链接对象创建 + + ```java + // 一些配置信息 + private ClassLoader driverClassLoader; + private Properties driverProperties; + private String driver; + private String url; + private String username; + private String password; + private Boolean autoCommit; + private Integer defaultTransactionIsolationLevel; + private Integer defaultNetworkTimeout; + ``` + + + +- 初始化连接对象 + + ```java + /** + * 加载链接驱动 如 mysql 链接驱动 + * @throws SQLException + */ + private synchronized void initializeDriver() throws SQLException { + if (!registeredDrivers.containsKey(driver)) { + Class driverType; + try { + if (driverClassLoader != null) { + driverType = Class.forName(driver, true, driverClassLoader); + } else { + driverType = Resources.classForName(driver); + } + // DriverManager requires the driver to be loaded via the system ClassLoader. + // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html + Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance(); + DriverManager.registerDriver(new DriverProxy(driverInstance)); + registeredDrivers.put(driver, driverInstance); + } catch (Exception e) { + throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e); + } + } + } + + ``` + + + +- 设置连接对象的属性 + + ```java + /** + * 设置连接对象 , 超时时间,是否自动提交事物 + * @param conn + * @throws SQLException + */ + private void configureConnection(Connection conn) throws SQLException { + if (defaultNetworkTimeout != null) { + conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout); + } + if (autoCommit != null && autoCommit != conn.getAutoCommit()) { + conn.setAutoCommit(autoCommit); + } + if (defaultTransactionIsolationLevel != null) { + conn.setTransactionIsolation(defaultTransactionIsolationLevel); + } + } + + ``` + + + +- 获取连接对象 + + ```java + /** + * 获取链接对象 + * @param username + * @param password + * @return + * @throws SQLException + */ + private Connection doGetConnection(String username, String password) throws SQLException { + Properties props = new Properties(); + if (driverProperties != null) { + props.putAll(driverProperties); + } + if (username != null) { + props.setProperty("user", username); + } + if (password != null) { + props.setProperty("password", password); + } + return doGetConnection(props); + } + + ``` + + + + + + + +## 解析流程 + +- 在xml解析的过程中会执行`DataSourceFactory`相关内容 + +```java + /** + * 解析 dataSourceElement 标签 + * + * + * + * + * + * + * + * @param context + * @return + * @throws Exception + */ + private DataSourceFactory dataSourceElement(XNode context) throws Exception { + if (context != null) { + String type = context.getStringAttribute("type"); + Properties props = context.getChildrenAsProperties(); + //org.apache.ibatis.session.Configuration.Configuration() + DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance(); + + // PooledDataSourceFactory -> UnpooledDataSourceFactory + factory.setProperties(props); + return factory; + } + throw new BuilderException("Environment declaration requires a DataSourceFactory."); + } + +``` + +从类图上或者代码中我们可以发现`PooledDataSourceFactory`是继承`UnpooledDataSourceFactory`那么方法应该也是`UnpooledDataSourceFactory`的。看看设置属性方法 + +![image-20191223083610214](/images/mybatis/image-20191223083610214.png) + +方法直接走完 + +![image-20191223083732972](/images/mybatis/image-20191223083732972.png) + diff --git a/docs/Mybatis/核心处理层/Mybatis-DyanmicSqlSourcce.md b/docs/Mybatis/核心处理层/Mybatis-DyanmicSqlSourcce.md new file mode 100644 index 0000000..2404a37 --- /dev/null +++ b/docs/Mybatis/核心处理层/Mybatis-DyanmicSqlSourcce.md @@ -0,0 +1,433 @@ +# Mybatis DyanmicSqlSourcce +- Author: [HuiFer](https://github.com/huifer) + +- `org.apache.ibatis.scripting.xmltags.DynamicSqlSource` +- `org.apache.ibatis.scripting.xmltags.DynamicContext.DynamicContext` + + + +```XML + + +``` + + + +![image-20191219151247240](/image/mybatis/image-20191219151247240.png) + +![image-20191219151408597](/image/mybatis/image-20191219151408597.png) + +```java +public class MixedSqlNode implements SqlNode { + private final List contents; + + public MixedSqlNode(List contents) { + this.contents = contents; + } + + @Override + public boolean apply(DynamicContext context) { + // 调用 salNode 对象本身的 apply 方法解析 sql + contents.forEach(node -> node.apply(context)); + return true; + } +} +``` + + + +- 根据mapper.xml文件中的代码流程 需要走 + + `org.apache.ibatis.scripting.xmltags.StaticTextSqlNode#apply` + + `org.apache.ibatis.scripting.xmltags.TrimSqlNode#apply` + + `org.apache.ibatis.scripting.xmltags.IfSqlNode#apply` + +![image-20191219152254274](/image/mybatis/image-20191219152254274.png) + +```java +/** + * @author Clinton Begin + */ +public class StaticTextSqlNode implements SqlNode { + private final String text; + + public StaticTextSqlNode(String text) { + this.text = text; + } + + /** + * 静态文本apply 方法 + * @param context + * @return + */ + @Override + public boolean apply(DynamicContext context) { + context.appendSql(text); + return true; + } + +} +``` + +- `org.apache.ibatis.scripting.xmltags.DynamicContext#appendSql` + + ```JAVA + public void appendSql(String sql) { + sqlBuilder.add(sql); + } + ``` + +- 解析`trim`标签 + +![image-20191219152502960](/image/mybatis/image-20191219152502960.png) + +- 在解析`trim`的时候会往下解析下级标签 + + ```java + @Override + public boolean apply(DynamicContext context) { + FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context); + // 解析下级标签的入口 + boolean result = contents.apply(filteredDynamicContext); + filteredDynamicContext.applyAll(); + return result; + } + ``` + + + +![image-20191219152655746](/image/mybatis/image-20191219152655746.png) + +```JAVA + @Override + public boolean apply(DynamicContext context) { + if (evaluator.evaluateBoolean(test, context.getBindings())) { + contents.apply(context); + return true; + } + return false; + } + +``` + +- `evaluator.evaluateBoolean(test, context.getBindings())`方法 + +```JAVA + /** + * @param expression 判断语句,ID != null + * @param parameterObject 参数列表 + * @return + */ + public boolean evaluateBoolean(String expression, Object parameterObject) { + Object value = OgnlCache.getValue(expression, parameterObject); + if (value instanceof Boolean) { + return (Boolean) value; + } + if (value instanceof Number) { + return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0; + } + return value != null; + } + +``` + +```JAVA + /** + * 取值 + * @param expression 判断语句,ID=NULL + * @param root 参数列表 + * @return + */ + public static Object getValue(String expression, Object root) { + try { + Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null); + // 判断是否存在 expression 的判断内容 (判断ID是否存在) + return Ognl.getValue(parseExpression(expression), context, root); + } catch (OgnlException e) { + throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e); + } + } + +``` + +![image-20191219153341466](/image/mybatis/image-20191219153341466.png) + +存在返回`true` + +执行完成就得到了一个sql + +![image-20191219153553127](/image/mybatis/image-20191219153553127.png) + +继续执行`org.apache.ibatis.scripting.xmltags.DynamicSqlSource#getBoundSql`方法 + +![image-20191219155129772](/image/mybatis/image-20191219155129772.png) + +- 发送sql`org.apache.ibatis.executor.SimpleExecutor#doQuery` + + + +- 调用链路如下 + +- `org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)` + + - `org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)` + + - `org.apache.ibatis.executor.Executor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)` + + - `org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)` + + ```java + @SuppressWarnings("unchecked") + @Override + public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { + ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); + if (closed) { + // 判断当前是否关闭 + throw new ExecutorException("Executor was closed."); + } + if (queryStack == 0 && ms.isFlushCacheRequired()) { + // 查询堆栈==0 和 是否需要刷新缓存 + // 清理本地缓存 + clearLocalCache(); + } + List list; + try { + // 堆栈+1,防止重新清理缓存 + queryStack++; + // 通过 缓存key 在本地缓存中获取 + list = resultHandler == null ? (List) localCache.getObject(key) : null; + if (list != null) { + // 通过缓存 key 查到后处理 localOutputParameterCache + handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); + } else { + // 没有查询到从数据库查询 + list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); + } + } finally { + // 堆栈-1 + queryStack--; + } + if (queryStack == 0) { + for (DeferredLoad deferredLoad : deferredLoads) { + deferredLoad.load(); + } + // 清空线程安全队列(延迟队列) + // issue #601 + deferredLoads.clear(); + if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { + // STATEMENT 清空本地缓存 + // issue #482 + clearLocalCache(); + } + } + return list; + } + + ``` + + - `org.apache.ibatis.executor.BaseExecutor#queryFromDatabase` + + ```java + private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { + List list; + localCache.putObject(key, EXECUTION_PLACEHOLDER); + try { + list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); + } finally { + localCache.removeObject(key); + } + localCache.putObject(key, list); + if (ms.getStatementType() == StatementType.CALLABLE) { + localOutputParameterCache.putObject(key, parameter); + } + return list; + } + + ``` + + - `org.apache.ibatis.executor.BaseExecutor#doQuery` + - `org.apache.ibatis.executor.SimpleExecutor#doQuery` + +![image-20191219160832704](/image/mybatis/image-20191219160832704.png) + +```java + private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { + Statement stmt; + // 数据库连接 + Connection connection = getConnection(statementLog); + // stms 创建 + // org.apache.ibatis.executor.statement.BaseStatementHandler.prepare + stmt = handler.prepare(connection, transaction.getTimeout()); + // 参数放入 + handler.parameterize(stmt); + return stmt; + } + +``` + +![image-20191219160908212](/image/mybatis/image-20191219160908212.png) + +- `org.apache.ibatis.executor.statement.BaseStatementHandler#prepare` + - `org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement` + + + +```java + @Override + public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { + ErrorContext.instance().sql(boundSql.getSql()); + Statement statement = null; + try { + statement = instantiateStatement(connection); + setStatementTimeout(statement, transactionTimeout); + setFetchSize(statement); + return statement; + } catch (SQLException e) { + closeStatement(statement); + throw e; + } catch (Exception e) { + closeStatement(statement); + throw new ExecutorException("Error preparing statement. Cause: " + e, e); + } + } + +``` + +```java + @Override + protected Statement instantiateStatement(Connection connection) throws SQLException { + String sql = boundSql.getSql(); + if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { + String[] keyColumnNames = mappedStatement.getKeyColumns(); + if (keyColumnNames == null) { + return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); + } else { + return connection.prepareStatement(sql, keyColumnNames); + } + } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { + return connection.prepareStatement(sql); + } else { + return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); + } + } + +``` + +- 这个方法都去了`java.sql.Connection#prepareStatement(java.lang.String, java.lang.String[])` + + + +- 接下来需要考虑的问题是如何将`?`换成我们的参数`2` + + ![image-20191219161555793](/image/mybatis/image-20191219161555793.png) + +- `org.apache.ibatis.executor.statement.StatementHandler#parameterize` + - `org.apache.ibatis.executor.statement.RoutingStatementHandler#parameterize` + - `org.apache.ibatis.executor.statement.StatementHandler#parameterize` + - `org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize` + - `org.apache.ibatis.executor.parameter.ParameterHandler` + - `org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters` + + + + + +![image-20191219162258040](/image/mybatis/image-20191219162258040.png) + +这样就拿到了`value`的值 + +![image-20191219162506920](/image/mybatis/image-20191219162506920.png) + +准备工作就绪了发送就可以了 + +`doQuery`的工作完成了继续往下走 + +```java + @Override + public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + Statement stmt = null; + try { + Configuration configuration = ms.getConfiguration(); + StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); + stmt = prepareStatement(handler, ms.getStatementLog()); + return handler.query(stmt, resultHandler); + } finally { + closeStatement(stmt); + } + } + +``` + +- `org.apache.ibatis.executor.statement.RoutingStatementHandler#query` + - `org.apache.ibatis.executor.statement.PreparedStatementHandler#query` + - `org.apache.ibatis.executor.resultset.ResultSetHandler#handleResultSets` + - `org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets` + + + +![image-20191219163628214](/image/mybatis/image-20191219163628214.png) + +![image-20191219163640968](/image/mybatis/image-20191219163640968.png) + +![image-20191219163957488](/image/mybatis/image-20191219163957488.png) + +处理后结果如上 + +```java + /** + * 处理查询结果 + * @param stmt + * @return + * @throws SQLException + */ + @Override + public List handleResultSets(Statement stmt) throws SQLException { + ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); + + final List multipleResults = new ArrayList<>(); + + int resultSetCount = 0; + ResultSetWrapper rsw = getFirstResultSet(stmt); + + List resultMaps = mappedStatement.getResultMaps(); + int resultMapCount = resultMaps.size(); + validateResultMapsCount(rsw, resultMapCount); + while (rsw != null && resultMapCount > resultSetCount) { + ResultMap resultMap = resultMaps.get(resultSetCount); + handleResultSet(rsw, resultMap, multipleResults, null); + rsw = getNextResultSet(stmt); + cleanUpAfterHandlingResultSet(); + resultSetCount++; + } + + String[] resultSets = mappedStatement.getResultSets(); + if (resultSets != null) { + while (rsw != null && resultSetCount < resultSets.length) { + ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); + if (parentMapping != null) { + String nestedResultMapId = parentMapping.getNestedResultMapId(); + ResultMap resultMap = configuration.getResultMap(nestedResultMapId); + handleResultSet(rsw, resultMap, null, parentMapping); + } + rsw = getNextResultSet(stmt); + cleanUpAfterHandlingResultSet(); + resultSetCount++; + } + } + + // 查询结果 + return collapseSingleResultList(multipleResults); + } + +``` + diff --git a/docs/Mybatis/核心处理层/Mybatis-ObjectWrapper.md b/docs/Mybatis/核心处理层/Mybatis-ObjectWrapper.md new file mode 100644 index 0000000..511d6c6 --- /dev/null +++ b/docs/Mybatis/核心处理层/Mybatis-ObjectWrapper.md @@ -0,0 +1,677 @@ +# Mybatis ObjectWrapper +- Author: [HuiFer](https://github.com/huifer) +- 源码位于: `org.apache.ibatis.reflection.wrapper.ObjectWrapper`‘ + +类图: + +![image-20191223100956713](/images/mybatis/image-20191223100956713.png) +```java +public interface ObjectWrapper { + + /** + * 根据 prop 获取属性值 + * + * @param prop + * @return + */ + Object get(PropertyTokenizer prop); + + /** + * 设置属性 + * + * @param prop 属性值名称 + * @param value 属性值 + */ + void set(PropertyTokenizer prop, Object value); + + /** + * 获取属性 + * + * @param name + * @param useCamelCaseMapping + * @return + */ + String findProperty(String name, boolean useCamelCaseMapping); + + /** + * get 方法名,可读方法名 + * + * @return + */ + String[] getGetterNames(); + + /** + * set 方法名,可写方法名 + * + * @return + */ + String[] getSetterNames(); + + /** + * set 数据类型, 获取可写的数据类型 + * + * @param name + * @return + */ + Class getSetterType(String name); + + /** + * get 数据类型, 获取可读的数据类型 + * + * @param name + * @return + */ + Class getGetterType(String name); + + /** + * 判断是否包含set方法 + * + * @param name + * @return + */ + boolean hasSetter(String name); + + /** + * 判断是否包含get方法 + * + * @param name + * @return + */ + boolean hasGetter(String name); + + /** + * 初始化数据 + * + * @param name + * @param prop + * @param objectFactory + * @return + */ + MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory); + + /** + * 判断是不是 list + * + * @return + */ + boolean isCollection(); + + /** + * list add + * + * @param element + */ + void add(Object element); + + /** + * list addAll + * + * @param element + * @param + */ + void addAll(List element); + +} +``` +## BaseWrapper +```java +/** + * @author Clinton Begin + */ +public abstract class BaseWrapper implements ObjectWrapper { + + protected static final Object[] NO_ARGUMENTS = new Object[0]; + protected final MetaObject metaObject; + + protected BaseWrapper(MetaObject metaObject) { + this.metaObject = metaObject; + } + + /** + * 处理集合对象 + * @param prop + * @param object + * @return + */ + protected Object resolveCollection(PropertyTokenizer prop, Object object) { + if ("".equals(prop.getName())) { + return object; + } else { + return metaObject.getValue(prop.getName()); + } + } + + /** + * 对象获取,根据index + * map.get(index) + * 数组 array[index] + * list list.get(index) + * @param prop + * @param collection + * @return + */ + protected Object getCollectionValue(PropertyTokenizer prop, Object collection) { + if (collection instanceof Map) { + // 如果是Map类型,则index为key + return ((Map) collection).get(prop.getIndex()); + } else { + // index 作为下标直接获取 + int i = Integer.parseInt(prop.getIndex()); + if (collection instanceof List) { + return ((List) collection).get(i); + } else if (collection instanceof Object[]) { + return ((Object[]) collection)[i]; + } else if (collection instanceof char[]) { + return ((char[]) collection)[i]; + } else if (collection instanceof boolean[]) { + return ((boolean[]) collection)[i]; + } else if (collection instanceof byte[]) { + return ((byte[]) collection)[i]; + } else if (collection instanceof double[]) { + return ((double[]) collection)[i]; + } else if (collection instanceof float[]) { + return ((float[]) collection)[i]; + } else if (collection instanceof int[]) { + return ((int[]) collection)[i]; + } else if (collection instanceof long[]) { + return ((long[]) collection)[i]; + } else if (collection instanceof short[]) { + return ((short[]) collection)[i]; + } else { + throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array."); + } + } + } + + /** + * 设置属性值 ,List , object[] , char[] boolean byte double float int long short + * map -> put(index,value) + * list -> list.set(index,value) + * array -> array[index] = value + * @param prop + * @param collection + * @param value + */ + protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) { + if (collection instanceof Map) { + // map -> index:value + ((Map) collection).put(prop.getIndex(), value); + } else { + // 数组 -> array[index]=value + int i = Integer.parseInt(prop.getIndex()); + if (collection instanceof List) { + ((List) collection).set(i, value); + } else if (collection instanceof Object[]) { + ((Object[]) collection)[i] = value; + } else if (collection instanceof char[]) { + ((char[]) collection)[i] = (Character) value; + } else if (collection instanceof boolean[]) { + ((boolean[]) collection)[i] = (Boolean) value; + } else if (collection instanceof byte[]) { + ((byte[]) collection)[i] = (Byte) value; + } else if (collection instanceof double[]) { + ((double[]) collection)[i] = (Double) value; + } else if (collection instanceof float[]) { + ((float[]) collection)[i] = (Float) value; + } else if (collection instanceof int[]) { + ((int[]) collection)[i] = (Integer) value; + } else if (collection instanceof long[]) { + ((long[]) collection)[i] = (Long) value; + } else if (collection instanceof short[]) { + ((short[]) collection)[i] = (Short) value; + } else { + throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array."); + } + } + } + +} +``` + +## BeanWrapper +```java +public class BeanWrapper extends BaseWrapper { + + private final Object object; + + private final MetaClass metaClass; + + /** + * 构造 + * @param metaObject + * @param object + */ + public BeanWrapper(MetaObject metaObject, Object object) { + super(metaObject); + this.object = object; + this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory()); + } + + @Override + public Object get(PropertyTokenizer prop) { + // 索引不为空 + if (prop.getIndex() != null) { + // 实例化集合对象 + Object collection = resolveCollection(prop, object); + return getCollectionValue(prop, collection); + } else { + // 没有索引 + return getBeanProperty(prop, object); + } + } + + @Override + public void set(PropertyTokenizer prop, Object value) { + // 是否存在索引 + if (prop.getIndex() != null) { + Object collection = resolveCollection(prop, object); + // 向上层调用 BaseWrapper + setCollectionValue(prop, collection, value); + } else { + // 本类方法 + setBeanProperty(prop, object, value); + } + } + + @Override + public String findProperty(String name, boolean useCamelCaseMapping) { + return metaClass.findProperty(name, useCamelCaseMapping); + } + + @Override + public String[] getGetterNames() { + return metaClass.getGetterNames(); + } + + @Override + public String[] getSetterNames() { + return metaClass.getSetterNames(); + } + + @Override + public Class getSetterType(String name) { + PropertyTokenizer prop = new PropertyTokenizer(name); + if (prop.hasNext()) { + + MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName()); + // 是否null + if (metaValue == SystemMetaObject.NULL_META_OBJECT) { + return metaClass.getSetterType(name); + } else { + return metaValue.getSetterType(prop.getChildren()); + } + } else { + return metaClass.getSetterType(name); + } + } + + @Override + public Class getGetterType(String name) { + PropertyTokenizer prop = new PropertyTokenizer(name); + if (prop.hasNext()) { + MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName()); + if (metaValue == SystemMetaObject.NULL_META_OBJECT) { + return metaClass.getGetterType(name); + } else { + return metaValue.getGetterType(prop.getChildren()); + } + } else { + return metaClass.getGetterType(name); + } + } + + @Override + public boolean hasSetter(String name) { + PropertyTokenizer prop = new PropertyTokenizer(name); + if (prop.hasNext()) { + if (metaClass.hasSetter(prop.getIndexedName())) { + MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName()); + if (metaValue == SystemMetaObject.NULL_META_OBJECT) { + return metaClass.hasSetter(name); + } else { + return metaValue.hasSetter(prop.getChildren()); + } + } else { + return false; + } + } else { + return metaClass.hasSetter(name); + } + } + + /** + * 是否包含 name 的get 方法 + * @param name + * @return + */ + @Override + public boolean hasGetter(String name) { + PropertyTokenizer prop = new PropertyTokenizer(name); + if (prop.hasNext()) { + if (metaClass.hasGetter(prop.getIndexedName())) { + MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName()); + if (metaValue == SystemMetaObject.NULL_META_OBJECT) { + return metaClass.hasGetter(name); + } else { + return metaValue.hasGetter(prop.getChildren()); + } + } else { + return false; + } + } else { + return metaClass.hasGetter(name); + } + } + + /** + * 数据嵌套处理 a.b.c 需要处理成 a->b->c + * @param name + * @param prop + * @param objectFactory + * @return + */ + @Override + public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) { + MetaObject metaValue; + Class type = getSetterType(prop.getName()); + try { + Object newObject = objectFactory.create(type); + // 出现嵌套处理 instantiatePropertyValue->set + metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory()); + set(prop, newObject); + } catch (Exception e) { + throw new ReflectionException("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + e.toString(), e); + } + return metaValue; + } + + /** + * 获取 object 的 prop 属性值 + * @param prop + * @param object + * @return + */ + private Object getBeanProperty(PropertyTokenizer prop, Object object) { + try { + // 获取get 方法 + Invoker method = metaClass.getGetInvoker(prop.getName()); + try { + // 获取属性值 + return method.invoke(object, NO_ARGUMENTS); + } catch (Throwable t) { + throw ExceptionUtil.unwrapThrowable(t); + } + } catch (RuntimeException e) { + throw e; + } catch (Throwable t) { + throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t); + } + } + + /** + * 设置 object 的属性 prop 值为 value + * @param prop + * @param object + * @param value + */ + private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) { + try { + // 获取set 方法 + Invoker method = metaClass.getSetInvoker(prop.getName()); + Object[] params = {value}; + try { + // 设置属性 + method.invoke(object, params); + } catch (Throwable t) { + throw ExceptionUtil.unwrapThrowable(t); + } + } catch (Throwable t) { + throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t); + } + } + + @Override + public boolean isCollection() { + return false; + } + + @Override + public void add(Object element) { + throw new UnsupportedOperationException(); + } + + @Override + public void addAll(List list) { + throw new UnsupportedOperationException(); + } + +} +``` +## MapWrapper +```java +public class MapWrapper extends BaseWrapper { + + private final Map map; + + public MapWrapper(MetaObject metaObject, Map map) { + super(metaObject); + this.map = map; + } + + @Override + public Object get(PropertyTokenizer prop) { + // 是否有索引 + if (prop.getIndex() != null) { + Object collection = resolveCollection(prop, map); + // 嗲用 BaseWrapper + return getCollectionValue(prop, collection); + } else { + // 获取 + return map.get(prop.getName()); + } + } + + @Override + public void set(PropertyTokenizer prop, Object value) { + if (prop.getIndex() != null) { + Object collection = resolveCollection(prop, map); + setCollectionValue(prop, collection, value); + } else { + map.put(prop.getName(), value); + } + } + + @Override + public String findProperty(String name, boolean useCamelCaseMapping) { + return name; + } + + @Override + public String[] getGetterNames() { + return map.keySet().toArray(new String[map.keySet().size()]); + } + + @Override + public String[] getSetterNames() { + return map.keySet().toArray(new String[map.keySet().size()]); + } + + @Override + public Class getSetterType(String name) { + PropertyTokenizer prop = new PropertyTokenizer(name); + if (prop.hasNext()) { + MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName()); + if (metaValue == SystemMetaObject.NULL_META_OBJECT) { + return Object.class; + } else { + return metaValue.getSetterType(prop.getChildren()); + } + } else { + if (map.get(name) != null) { + return map.get(name).getClass(); + } else { + return Object.class; + } + } + } + + @Override + public Class getGetterType(String name) { + PropertyTokenizer prop = new PropertyTokenizer(name); + if (prop.hasNext()) { + MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName()); + if (metaValue == SystemMetaObject.NULL_META_OBJECT) { + return Object.class; + } else { + return metaValue.getGetterType(prop.getChildren()); + } + } else { + if (map.get(name) != null) { + return map.get(name).getClass(); + } else { + return Object.class; + } + } + } + + @Override + public boolean hasSetter(String name) { + return true; + } + + @Override + public boolean hasGetter(String name) { + PropertyTokenizer prop = new PropertyTokenizer(name); + if (prop.hasNext()) { + if (map.containsKey(prop.getIndexedName())) { + MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName()); + if (metaValue == SystemMetaObject.NULL_META_OBJECT) { + return true; + } else { + return metaValue.hasGetter(prop.getChildren()); + } + } else { + return false; + } + } else { + return map.containsKey(prop.getName()); + } + } + + @Override + public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) { + HashMap map = new HashMap<>(); + set(prop, map); + return MetaObject.forObject(map, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory()); + } + + @Override + public boolean isCollection() { + return false; + } + + @Override + public void add(Object element) { + throw new UnsupportedOperationException(); + } + + @Override + public void addAll(List element) { + throw new UnsupportedOperationException(); + } + +} +``` +## CollectionWrapper +```java +public class CollectionWrapper implements ObjectWrapper { + + private final Collection object; + + public CollectionWrapper(MetaObject metaObject, Collection object) { + this.object = object; + } + + @Override + public Object get(PropertyTokenizer prop) { + throw new UnsupportedOperationException(); + } + + @Override + public void set(PropertyTokenizer prop, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public String findProperty(String name, boolean useCamelCaseMapping) { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getGetterNames() { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getSetterNames() { + throw new UnsupportedOperationException(); + } + + @Override + public Class getSetterType(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public Class getGetterType(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasSetter(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasGetter(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) { + throw new UnsupportedOperationException(); + } + + /** + * 是否是list + * + * @return true + */ + @Override + public boolean isCollection() { + return true; + } + + /** + * java {@link Collection#add(Object)} + * + * @param element + */ + @Override + public void add(Object element) { + object.add(element); + } + + /** + * java {@link Collection#addAll(Collection)} + * + * @param element + * @param + */ + @Override + public void addAll(List element) { + object.addAll(element); + } + +} +``` \ No newline at end of file diff --git a/images/mybatis/image-20191219151247240.png b/images/mybatis/image-20191219151247240.png new file mode 100644 index 0000000..ebdcc67 Binary files /dev/null and b/images/mybatis/image-20191219151247240.png differ diff --git a/images/mybatis/image-20191219151408597.png b/images/mybatis/image-20191219151408597.png new file mode 100644 index 0000000..e2b4499 Binary files /dev/null and b/images/mybatis/image-20191219151408597.png differ diff --git a/images/mybatis/image-20191219152254274.png b/images/mybatis/image-20191219152254274.png new file mode 100644 index 0000000..a97f92e Binary files /dev/null and b/images/mybatis/image-20191219152254274.png differ diff --git a/images/mybatis/image-20191219152502960.png b/images/mybatis/image-20191219152502960.png new file mode 100644 index 0000000..0377bed Binary files /dev/null and b/images/mybatis/image-20191219152502960.png differ diff --git a/images/mybatis/image-20191219152655746.png b/images/mybatis/image-20191219152655746.png new file mode 100644 index 0000000..99a197d Binary files /dev/null and b/images/mybatis/image-20191219152655746.png differ diff --git a/images/mybatis/image-20191219153341466.png b/images/mybatis/image-20191219153341466.png new file mode 100644 index 0000000..56e9469 Binary files /dev/null and b/images/mybatis/image-20191219153341466.png differ diff --git a/images/mybatis/image-20191219153553127.png b/images/mybatis/image-20191219153553127.png new file mode 100644 index 0000000..459bedb Binary files /dev/null and b/images/mybatis/image-20191219153553127.png differ diff --git a/images/mybatis/image-20191219155129772.png b/images/mybatis/image-20191219155129772.png new file mode 100644 index 0000000..664c16e Binary files /dev/null and b/images/mybatis/image-20191219155129772.png differ diff --git a/images/mybatis/image-20191219160832704.png b/images/mybatis/image-20191219160832704.png new file mode 100644 index 0000000..f8186f0 Binary files /dev/null and b/images/mybatis/image-20191219160832704.png differ diff --git a/images/mybatis/image-20191219160908212.png b/images/mybatis/image-20191219160908212.png new file mode 100644 index 0000000..caf887d Binary files /dev/null and b/images/mybatis/image-20191219160908212.png differ diff --git a/images/mybatis/image-20191219161555793.png b/images/mybatis/image-20191219161555793.png new file mode 100644 index 0000000..5cbe5de Binary files /dev/null and b/images/mybatis/image-20191219161555793.png differ diff --git a/images/mybatis/image-20191219162258040.png b/images/mybatis/image-20191219162258040.png new file mode 100644 index 0000000..b586e45 Binary files /dev/null and b/images/mybatis/image-20191219162258040.png differ diff --git a/images/mybatis/image-20191219162506920.png b/images/mybatis/image-20191219162506920.png new file mode 100644 index 0000000..2d28d2d Binary files /dev/null and b/images/mybatis/image-20191219162506920.png differ diff --git a/images/mybatis/image-20191219163628214.png b/images/mybatis/image-20191219163628214.png new file mode 100644 index 0000000..fa5053e Binary files /dev/null and b/images/mybatis/image-20191219163628214.png differ diff --git a/images/mybatis/image-20191219163640968.png b/images/mybatis/image-20191219163640968.png new file mode 100644 index 0000000..c8ae659 Binary files /dev/null and b/images/mybatis/image-20191219163640968.png differ diff --git a/images/mybatis/image-20191219163957488.png b/images/mybatis/image-20191219163957488.png new file mode 100644 index 0000000..f981230 Binary files /dev/null and b/images/mybatis/image-20191219163957488.png differ diff --git a/images/mybatis/image-20191223081023730.png b/images/mybatis/image-20191223081023730.png new file mode 100644 index 0000000..438447f Binary files /dev/null and b/images/mybatis/image-20191223081023730.png differ diff --git a/images/mybatis/image-20191223083610214.png b/images/mybatis/image-20191223083610214.png new file mode 100644 index 0000000..ddf1b17 Binary files /dev/null and b/images/mybatis/image-20191223083610214.png differ diff --git a/images/mybatis/image-20191223083732972.png b/images/mybatis/image-20191223083732972.png new file mode 100644 index 0000000..d9138cf Binary files /dev/null and b/images/mybatis/image-20191223083732972.png differ diff --git a/images/mybatis/image-20191223100956713.png b/images/mybatis/image-20191223100956713.png new file mode 100644 index 0000000..d470d86 Binary files /dev/null and b/images/mybatis/image-20191223100956713.png differ