@ -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,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();
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
类图如下
|
||||
|
||||

|
||||
|
||||
- `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`的。看看设置属性方法
|
||||
|
||||

|
||||
|
||||
方法直接走完
|
||||
|
||||

|
||||
|
@ -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>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
```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`
|
||||
|
||||

|
||||
|
||||
```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`标签
|
||||
|
||||

|
||||
|
||||
- 在解析`trim`的时候会往下解析下级标签
|
||||
|
||||
```java
|
||||
@Override
|
||||
public boolean apply(DynamicContext context) {
|
||||
FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
|
||||
// 解析下级标签的入口
|
||||
boolean result = contents.apply(filteredDynamicContext);
|
||||
filteredDynamicContext.applyAll();
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
```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);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||

|
||||
|
||||
存在返回`true`
|
||||
|
||||
执行完成就得到了一个sql
|
||||
|
||||

|
||||
|
||||
继续执行`org.apache.ibatis.scripting.xmltags.DynamicSqlSource#getBoundSql`方法
|
||||
|
||||

|
||||
|
||||
- 发送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`
|
||||
|
||||

|
||||
|
||||
```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;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||

|
||||
|
||||
- `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`
|
||||
|
||||

|
||||
|
||||
- `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`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
这样就拿到了`value`的值
|
||||
|
||||

|
||||
|
||||
准备工作就绪了发送就可以了
|
||||
|
||||
`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`
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
处理后结果如上
|
||||
|
||||
```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);
|
||||
}
|
||||
|
||||
```
|
||||
|
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 19 KiB |