Merge pull request #9 from huifer/master

mybatis source code
pull/10/head
AmyliaY 6 years ago committed by GitHub
commit 4a21af6e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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<Object, ReentrantLock> 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<Object> 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<Object, Object> 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<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> 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;
}
}
}
```

@ -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<String, Class<?>> 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`

@ -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<T> extends Closeable, Iterable<T> {
/**
* 游标开始从数据库获取数据,返回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<T> implements Cursor<T> {
/**
* 对象包装结果处理类
*/
protected final ObjectWrapperResultHandler<T> 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<T> 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 <T>
*/
protected static class ObjectWrapperResultHandler<T> implements ResultHandler<T> {
/**
* 数据结果
*/
protected T result;
/**
* 是否null
*/
protected boolean fetched;
/**
* 从{@link ResultContext} 获取结果对象
* @param context
*/
@Override
public void handleResult(ResultContext<? extends T> context) {
this.result = context.getResultObject();
context.stop();
fetched = true;
}
}
/**
* 游标迭代器
*/
protected class CursorIterator implements Iterator<T> {
/**
* 下一个数据
* 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");
}
}
}
```

@ -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
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="mybatis"/>
<property name="password" value="mybatis"/>
</dataSource>
```
- 在`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<Object, Object> 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 标签
* <dataSource type="POOLED">
* <property name="driver" value="com.mysql.jdbc.Driver"/>
* <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
* <property name="username" value="root"/>
* <property name="password" value="root"/>
* </dataSource>
*
* @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)

@ -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
<select id="list" resultType="com.huifer.mybatis.entity.HsSell">
select * from hs_sell
<trim prefix="WHERE" prefixOverrides="AND |OR">
<if test="ID != null">
and ID = #{ID,jdbcType=INTEGER}
</if>
</trim>
</select>
```
![image-20191219151247240](/image/mybatis/image-20191219151247240.png)
![image-20191219151408597](/image/mybatis/image-20191219151408597.png)
```java
public class MixedSqlNode implements SqlNode {
private final List<SqlNode> contents;
public MixedSqlNode(List<SqlNode> 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 <E> List<E> 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<E> list;
try {
// 堆栈+1,防止重新清理缓存
queryStack++;
// 通过 缓存key 在本地缓存中获取
list = resultHandler == null ? (List<E>) 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 <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> 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 <E> List<E> 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<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> 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);
}
```

@ -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 <E>
*/
<E> void addAll(List<E> 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 <E> void addAll(List<E> list) {
throw new UnsupportedOperationException();
}
}
```
## MapWrapper
```java
public class MapWrapper extends BaseWrapper {
private final Map<String, Object> map;
public MapWrapper(MetaObject metaObject, Map<String, Object> 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<String, Object> 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 <E> void addAll(List<E> element) {
throw new UnsupportedOperationException();
}
}
```
## CollectionWrapper
```java
public class CollectionWrapper implements ObjectWrapper {
private final Collection<Object> object;
public CollectionWrapper(MetaObject metaObject, Collection<Object> 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 <E>
*/
@Override
public <E> void addAll(List<E> element) {
object.addAll(element);
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Loading…
Cancel
Save