一、SqlSessionFactory
用于在连接或数据源中创建SqlSession,是一个单纯的创建SqlSession的工厂接口。
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
二、SqlSession
The primary Java interface for working with MyBatis. Through this interface you can execute commands, get mappers and manage transactions.
如下图可见,SqlSession封装了我们所有的增删改查操作,是一个很重要的接口
三、mapper的生成
1、猜想的生成过程
自旋可以回忆:https://www.yuque.com/wangchao-volk4/fdw9ek/xmkynl
public class SupkingxLock {
private static final Unsafe unsafe;
private static final long valueOffset;
private volatile int value = 0;
static {
try {
Class<Unsafe> unsafeClass = Unsafe.class;
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
valueOffset = unsafe.objectFieldOffset
(SupkingxLock.class.getDeclaredField("value"));
} catch (Exception ex) {
throw new Error(ex);
}
}
public void lock() {
// 自旋
for (; ; ) {
// this 当前对象本身,valueOffset 位移偏移量,0:内存中的真值,1:将要变成的值
if (unsafe.compareAndSwapInt(this, valueOffset, 0, 1)) {
return;
}
// 如果不是自己想要的值,则将当前线程让出去
Thread.yield();
}
}
public void unlock() {
value = 0;
}
public static void main(String[] args) {
System.out.println(unsafe);
}
}
public class MyBatisEnhance {
public static final Map<Class<?>, Object> objectMap = new HashMap<>();
private static SupkingxLock supkingxLock = new SupkingxLock();
static class MapperScanner {
private String mapperLocation;
public List<Class<?>> scanMapper() {
List<Class<?>> classList = new ArrayList<>();
for (int i = 0; i < 500000000; i++) {
classList.add(MyBatisEnhance.class);
}
return new ArrayList<>();
}
public String getMapperLocation() {
return mapperLocation;
}
public void setMapperLocation(String mapperLocation) {
this.mapperLocation = mapperLocation;
}
}
/**
* 可以优化来加个锁,实际Mybatis中并没有加锁
* @param object
*/
public static void put(Object object) {
supkingxLock.lock();
objectMap.put(object.getClass(), object);
supkingxLock.unlock();
}
public static void main(String[] args) {
String mapperLocation = "classpath:mapper/*.xml";
MapperScanner mapperScanner = new MapperScanner();
mapperScanner.setMapperLocation(mapperLocation);
/**
* 一千个??
*/
List<Class<?>> classes = mapperScanner.scanMapper();
// 解析xml
// 。。。。。。。。
long start = System.currentTimeMillis();
// 将这1000个mapper添加到map中
classes.forEach(aClass -> objectMap.put(aClass, new Object()));
// classes.parallelStream().forEach(aClass -> put(new Object()));
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
2、MyBatis中mapper产生的实现方式
2.1 代码入口
public static void main(String[] args) throws IOException {
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession session = factory.openSession();
//获得dao 的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryById(21);
System.out.println(user.toString());
}
通过上述代码,很明显可以只知道,第一步载入 xml 文件,是从 SqlSessionFactoryBuilder 开始的
2.2 SqlSessioinFactoryBuilder
这个类负责解析 xml 并 build出 SqlSessionFactory
2.2.1 解析xml的入口
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
2.2.2 解析 xml 并将 mapper(interface)放入到configuration中
parse—>parseConfiguration—>mapperElement—>configuration.addMappers(mapperPackage);
根据配置文件中
如果是指定了标签mapper,则进入下图红框标出的位置,mapperParser.parse->bindMapperForNamespace
这个parse是用来解析写了sql的mapper.xml文件的
在 bindMapperForNamespace 方法中,通过书写了 sql 的 mapper.xml 文件中的 nameSpace 标签获取 mapper接口的全类名,然后通过反射获取到这个接口,最后将其 add 到 configuration 中
2.2.3 addMapper
addMapper方法就是将 接口mapper的位置作为key,value是一个封装了接口mapper的 MapperProxyFactory 对象。
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 接口mapper的位置作为key,value是一个封装了接口mapper的 MapperProxyFactory 对象。
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
2.2.4 MapperProxyFactory
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
上述代码中的 MapperProxyFactory 中只是将 mapper 直接放到了 mapperInterface中,并没有区分这个mapper有注解的(@Select),还是用xml的。
这里接着 addMapper说
接着parser.parse()—>loadXmlResource()载入带sql的xml文件—->parseStatement(method),解析mapper接口上的注解方法,失败之后catch放入到configuration.addIncompleteMethod,然后再次parsePendingMethods执行失败的东西。
parse结束后,回到最上面 https://www.yuque.com/wangchao-volk4/fdw9ek/xpdqrv#lp5zo build返回 DefaultSqlSessionFactory 默认的。
3、是否可以优化 addMapper 方法
�这段代码并没用锁,也没有用ConcurrentHashMap。也没有使用 并行流(parallelStream().forEach(aClass -> put(new Object()) 的 这种添加方式。是不是可以优化一下???
什么时候应该用 parallelStream?可以参考现成的案例 java.util.Arrays#parallelSort(byte[])。
当数量小于 2的13次方或者并行数量是1的时候,用串行流,否则就要用并行流。
/**
* The minimum array length below which a parallel sorting
* algorithm will not further partition the sorting task. Using
* smaller sizes typically results in memory contention across
* tasks that makes parallel speedups unlikely.
*/
private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;
public static void parallelSort(byte[] a) {
int n = a.length, p, g;
if (n <= MIN_ARRAY_SORT_GRAN ||
(p = ForkJoinPool.getCommonPoolParallelism()) == 1)
DualPivotQuicksort.sort(a, 0, n - 1);
else
new ArraysParallelSortHelpers.FJByte.Sorter
(null, a, new byte[n], 0, n, 0,
((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
MIN_ARRAY_SORT_GRAN : g).invoke();
}
四、获取mapper
public static void main(String[] args) throws IOException {
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession session = factory.openSession();
//获得dao 的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.queryById(21);
System.out.println(user.toString());
}
2.2.3 结束之后,我们获取到了一个 SqlSessionFactory,接口用这个工厂生产一个 SqlSession。
1、openSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获得配置
final Environment environment = configuration.getEnvironment();
// 从配置中获得事务
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建执行器
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
2、getMapper
通过 创建的 SqlSession 中的 getMapper方法来获取mapper。其实就是获取 knownMappers 中的值(MapperProxyFactory)类型,接着,利用 MapperProxyFactory 中的 newInstance 方法创建一个代理对象(用的是jdk的proxy代理)。
knownMappers 中的值 是在 addMapper 时放入的
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
3、执行mapper
从MapperProxyFactory中获取到代理mapper后,就要开始执行。jdk的proxy的执行核心就是 InvocationHandler。请移步到下一节 3、MyBatis中关于mapper的执行过程
�