mybatis源码注解sql
Demo
主启动类
java public class MybatisHelloWorld { public static void main(String[] args) throws Exception { String resource = "org/mybatis/config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); List users = mapper.getUsers(1); session.close(); } }
userMapper.class
java public interface UserMapper { @Select({"select * from user where age=#{age}"}) List getUsers(int age); }
config.xml
xml <?xml version="1.0" encoding="UTF-8"?> //控制台输出sql
Mybatis通过session来进行数据库的操作,sqlSessionFactory封装了session的创建,而SqlSessionFactoryBuilder又封装了sqlSessionFactory的创建
从上面代码来看总共做了两件事 读取配置文件,通过SqlSessionFactoryBuilder创建sqlSessionFactory继而创建session 获取mapper进行读取数据库
先来看如何将xml配置文件封装为对象的 解析配置文件
java new SqlSessionFactoryBuilder().build(inputStream);
这里使用构造者模式来创建一个sqlSessionFactory,里面使用重载
java public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); }
最终调用
SqlSessionFactoryBuilder.java
java public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //创建一个xml解析类 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //解析xml中配置,转换为configuration类 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
mybatis是把一些配置类以及它自己需要使用的各种类封装成一个大的config对象
org.apache.ibatis.session.Configuration 里面有很多环境,mapper等等的信息,内容太多就不粘贴了
XMLConfigBuilder.java
java public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { //创建了一个Configuration 对象 super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; //这一行设置环境id this.environment = environment; this.parser = parser; }
XMlConfigBuilder类关系图
BaseBuilder.java
java public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); }
解析主配置文件.xml
SqlSessionFactoryBuilder.java
java public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { //.... return build(parser.parse()); //.... } public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; //读取configuration节点下的node传入 parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { //issue #117 read properties first //读取properties propertiesElement(root.evalNode("properties")); //读取一些setting设置 Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); //注册别名 typeAliasesElement(root.evalNode("typeAliases")); //插件,进行增强-先略过 pluginElement(root.evalNode("plugins")); //对象工厂,自定义实例化方法--略过 objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectionFactoryElement(root.evalNode("reflectionFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 //配置环境 environmentsElement(root.evalNode("environments")); //数据厂商表示--略过 databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); //配置mapper mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
解析的东西很多,我们只先看environments和mapper environmentsElement
XMLConfigBuilder.java
java private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); //可以配置多个环境,判断是不是指定的环境 if (isSpecifiedEnvironment(id)) { //获取事物管理器,创建事物管理器工厂 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); //获取datasource工厂-UnpooledDataSourceFactory默认 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } }
进入发现第一件是就是判断环境,没有指定就使用 中default的环境id,在上面的XMLConfigBuilder的有参构造中this.environment = environment; 将环境配置设置给了XMLConfigBuilder的environment 点我跳转到XMLConfigBuilder-有参构造
我们在使用时可以这样,在配置文件xml中,声明多个环境
xml
主启动类中,手动指明一个配置环境
java public static void main(String[] args) throws Exception { //.... SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"myTest"); //.... }
回到代码第一步就是判断用户选择的那个id的环境,之后创建事务管理器
XMLConfigBuilder.java
java private void environmentsElement(XNode context) throws Exception { //.. TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); //.. } private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) { //获取我们在xml中声明的事务管理类型,当前是JDBC String type = context.getStringAttribute("type"); //获取节点下的子节点,当前案例没有子节点 Properties props = context.getChildrenAsProperties(); //这里只是创建工厂类 TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); }
这里调用 resolveClass() 方法是父类BaseBuilder的方法
一直点进去最后如下
TypeAliasRegistry.java
java public class TypeAliasRegistry { private final Map> TYPE_ALIASES = new HashMap>(); //..... public Class resolveAlias(String string) { try { if (string == null) { return null; } // issue #748 String key = string.toLowerCase(Locale.ENGLISH); Class value; if (TYPE_ALIASES.containsKey(key)) { value = (Class) TYPE_ALIASES.get(key); } else { value = (Class) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias "" + string + "". Cause: " + e, e); } } }
判断这个TYPE_ALIASES map中是否存在JDBC这个key,如果不存在,则去加载
按理来说这里应该是不存在的,因为你在TypeAliasRegistry中找不到任何一个地方对TYPE_ALIASES添加一个JDBC的key
但是实际它却存在这个key,在Configuration类的无参构造时,对这个TypeAliasRegistry进行的添加
Configuration.java
java public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); //... typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); //...... }
这个过程如下图
回到代码因为我们这次案例的配置为 所以不会存在子节点context.getChildrenAsProperties(); 返回的结果0个配置项,transactionManagerElement 方法结束
之后去解析数据库配置文件
XMLConfigBulider.java
java private void environmentsElement(XNode context) throws Exception { //... //获取datasource工厂-UnpooledDataSourceFactory默认 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); //... }
和解析环境基本一样的代码,不过解析dataSource的时候,子节点就不为空了
会有四个属性
xml
java private DataSourceFactory dataSourceElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); //type为POOLED的默认实现是PooledDataSourceFactory DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory."); }
进入 factory.setProperties(props);
java public class UnpooledDataSourceFactory implements DataSourceFactory { private static final String DRIVER_PROPERTY_PREFIX = "driver."; private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length(); protected DataSource dataSource; //无参默认将dataSource设置为UnpooledDataSource public UnpooledDataSourceFactory() { this.dataSource = new UnpooledDataSource(); } @Override public void setProperties(Properties properties) { Properties driverProperties = new Properties(); //将工厂对象进行包装 MetaObject metaDataSource = SystemMetaObject.forObject(dataSource); for (Object key : properties.keySet()) { String propertyName = (String) key; //如果存在driver if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) { 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.setValue(propertyName, convertedValue); } else { throw new DataSourceException("Unknown DataSource property: " + propertyName); } } //如果属性不为空,则设置给meatDataSource if (driverProperties.size() > 0) { metaDataSource.setValue("driverProperties", driverProperties); } } //...... }
一顿设置后回到XMLConfigurationBuilder中的environmentsElement方法
最后将读取出的配置封装为Environment,赋值给BaseBuilder中的environment
java private void environmentsElement(XNode context) throws Exception { //..... DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); //..... } mapperElement
回到XMLConfigBuilder中的parseConfiguration
java private void parseConfiguration(XNode root) { //..... //配置mapper mapperElement(root.evalNode("mappers")); }
我们只看根据包扫描的,给Configuration中添加了mapper包名
java private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { //使用包,默认查找指定包下位置 if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } //..... } } }
Configuration.java
java public void addMappers(String packageName) { mapperRegistry.addMappers(packageName); }
MapperRegistry.java
java public void addMappers(String packageName) { addMappers(packageName, Object.class); } //根据包名去查询该包下的类 public void addMappers(String packageName, Class<?> superType) { ResolverUtil> resolverUtil = new ResolverUtil>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set>> mapperSet = resolverUtil.getClasses(); for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } }
之后就是动态代理对应的mapper
MapperRegistry.java
java public void addMapper(Class type) { if (type.isInterface()) { //判断是否已经存在 if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory(type)); MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
java public class MapperProxyFactory { private final Class mapperInterface; private final Map methodCache = new ConcurrentHashMap(); public MapperProxyFactory(Class mapperInterface) { this.mapperInterface = mapperInterface; } public Class getMapperInterface() { return mapperInterface; } public Map getMethodCache() { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
主要来看这段
java MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse();
MapperAnnotationBuilder.java
java public class MapperAnnotationBuilder { private final Set> sqlAnnotationTypes = new HashSet>(); private final Set> sqlProviderAnnotationTypes = new HashSet>(); //.... /** * 在构造时添加mybatis的注解 */ public MapperAnnotationBuilder(Configuration configuration, Class<?> type) { String resource = type.getName().replace(".", "/") + ".java (best guess)"; this.assistant = new MapperBuilderAssistant(configuration, resource); this.configuration = configuration; this.type = type; sqlAnnotationTypes.add(Select.class); sqlAnnotationTypes.add(Insert.class); sqlAnnotationTypes.add(Update.class); sqlAnnotationTypes.add(Delete.class); sqlProviderAnnotationTypes.add(SelectProvider.class); sqlProviderAnnotationTypes.add(InsertProvider.class); sqlProviderAnnotationTypes.add(UpdateProvider.class); sqlProviderAnnotationTypes.add(DeleteProvider.class); } public void parse() { String resource = type.toString(); if (!configuration.isResourceLoaded(resource)) { loadXmlResource(); configuration.addLoadedResource(resource); assistant.setCurrentNamespace(type.getName()); parseCache(); parseCacheRef(); Method[] methods = type.getMethods(); for (Method method : methods) { try { // issue #237 if (!method.isBridge()) { parseStatement(method); } } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } parsePendingMethods(); } }
java void parseStatement(Method method) { Class<?> parameterTypeClass = getParameterType(method); LanguageDriver languageDriver = getLanguageDriver(method); SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver); //... }
首先第一步是获取参数类型-代码如下,如果mapper的入参数量大于1,则返回的就是ParamMap.class
java private Class<?> getParameterType(Method method) { Class<?> parameterType = null; Class<?>[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { if (!RowBounds.class.isAssignableFrom(parameterTypes[i]) && !ResultHandler.class.isAssignableFrom(parameterTypes[i])) { if (parameterType == null) { parameterType = parameterTypes[i]; } else { // issue #135 parameterType = ParamMap.class; } } } return parameterType; }
之后获取语言解析,没有指定就去找默认-默认的是XMLLanguageDriver.class 还是在Configuration类无参构造时添加进去的点我跳转到Configuration-无参构造
java private LanguageDriver getLanguageDriver(Method method) { Lang lang = method.getAnnotation(Lang.class); Class<?> langClass = null; if (lang != null) { langClass = lang.value(); } return assistant.getLanguageDriver(langClass); }
获取注解上的内容,以及封装sql就在这个方法
java private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) { try { //获取是否存在@Select,@Insert.... Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method); //获取是否存在@SelectProvider,@InsertProvider... Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method); if (sqlAnnotationType != null) { if (sqlProviderAnnotationType != null) { throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName()); } Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType); //获取注解上的值 final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation); //返回sqlSource //这个时候还没有进行填充值 return buildSqlSourceFromStrings(strings, parameterType, languageDriver); } else if (sqlProviderAnnotationType != null) { Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType); return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation); } return null; } catch (Exception e) { throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e); } }
此时的 strings 值还是 select * from user where age = #{age} 需要给替换为 select * from user where age =?
java private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) { final StringBuilder sql = new StringBuilder(); for (String fragment : strings) { sql.append(fragment); sql.append(" "); } return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass); }
默认的语言驱动是XMLLanguageDriver
XMLLanguageDriver.java
首先判断注解上的内容是否存在脚本,在mybatis官网,动态SQL下的script有使用案例,使得在注解中可以像在xml中使用 parameterType) { // issue #3 if (script.startsWith("<script>")) { XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver()); return createSqlSource(configuration, parser.evalNode("/script"), parameterType); } else { // issue #127 script = PropertyParser.parse(script, configuration.getVariables()); TextSqlNode textSqlNode = new TextSqlNode(script); //判断是否为动态的sql就取决于使用的是${} 还是#{} 当使用${}时就是动态sql if (textSqlNode.isDynamic()) { return new DynamicSqlSource(configuration, textSqlNode); } else { return new RawSqlSource(configuration, script, parameterType); } } }
之后在RewSqlSource中对sql进行解析
RewSqlSource.java
java public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) { SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> clazz = parameterType == null ? Object.class : parameterType; sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap()); }
解析完最终结果如下
回到上个方法
java void parseStatement(Method method) { Class<?> parameterTypeClass = getParameterType(method); LanguageDriver languageDriver = getLanguageDriver(method); SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver); //从这里继续 if (sqlSource != null) { Options options = method.getAnnotation(Options.class); //mappedStatementId=类全限定+方法名 final String mappedStatementId = type.getName() + "." + method.getName(); //设置获取数据的大小 Integer fetchSize = null; //设置此次查询超时时间 Integer timeout = null; //https://blog.csdn.net/king101125s/article/details/104167493 StatementType statementType = StatementType.PREPARED; //resultSet结果类型 FORWARD_ONLY 光标只能向前移动 ResultSetType resultSetType = ResultSetType.FORWARD_ONLY; //设置sql类型,当前案例是SELECT SqlCommandType sqlCommandType = getSqlCommandType(method); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = !isSelect; boolean useCache = isSelect; KeyGenerator keyGenerator; String keyProperty = "id"; String keyColumn = null; if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { // first check for SelectKey annotation - that overrides everything else SelectKey selectKey = method.getAnnotation(SelectKey.class); if (selectKey != null) { keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver); keyProperty = selectKey.keyProperty(); } else if (options == null) { keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator(); } else { keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator(); keyProperty = options.keyProperty(); keyColumn = options.keyColumn(); } } else { keyGenerator = new NoKeyGenerator(); } if (options != null) { flushCache = options.flushCache(); useCache = options.useCache(); fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348 timeout = options.timeout() > -1 ? options.timeout() : null; statementType = options.statementType(); resultSetType = options.resultSetType(); } String resultMapId = null; ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class); if (resultMapAnnotation != null) { String[] resultMaps = resultMapAnnotation.value(); StringBuilder sb = new StringBuilder(); for (String resultMap : resultMaps) { if (sb.length() > 0) { sb.append(","); } sb.append(resultMap); } resultMapId = sb.toString(); } else if (isSelect) { resultMapId = parseResultMap(method); } //将参数传给小助手 assistant.addMappedStatement( mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout, // ParameterMapID null, parameterTypeClass, resultMapId, getReturnType(method), resultSetType, flushCache, useCache, // TODO issue #577 false, keyGenerator, keyProperty, keyColumn, // DatabaseID null, languageDriver, // ResultSets null); } }
添加mappedStatement
java public MappedStatement addMappedStatement( String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) { if (unresolvedCacheRef) { throw new IncompleteElementException("Cache-ref not yet resolved"); } id = applyCurrentNamespace(id, false); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resulSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } MappedStatement statement = statementBuilder.build(); //这里将这个MappedStatement放入configuration configuration.addMappedStatement(statement); return statement; }
将各种参数最后封装为一个MappedStatement,放入configuration中,这样一个addMapper的方法就结束了
之后回到SqlSessionFactory的build中,执行重载的build
java public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //回到这里 return build(parser.parse()); } //默认为DefaultSqlSessionFactory public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
DefaultSqlSessionFactory.java
java public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } //..... }
至此 new SqlSessionFactoryBuilder().build(inputStream) 这段代码的解析环境和加载mapper就分析完了sqlSessionFactory
回到主代码
java public static void main(String[] args) throws Exception { String resource = "org/mybatis/config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); //.. }
开始分析sqlSessionFactory.openSession();
点进入发现有两个实现类,在上面的build中已经明确了创建的就是DefaultSqlSessionFactory
DefaultSqlSessionFactory.java
java @Override public SqlSession openSession() { //参数:执行器,默认为simple,每次关闭statement SimpleExecutor return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); }
configuration.getDefaultExecutorType() 在Configuration类中获取的是本身的一个属性,类型是一个ExecutorType的枚举,默认为SIMPE
java private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { //获取配置类中的事务,datasource封装类 final Environment environment = configuration.getEnvironment(); //通过配置的环境中获取事务工厂 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //创建对象/datasource/隔离等级/是否自动提交 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(); } }
获取configuration中的事务工厂之后创建一个执行器
Configuration.java
java public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
这里缓存是默认开启的,所以最后返回的是一个 CachingExecutor 包含着一个SimpleExecutor
最后返回一个默认的 DefaultSqlSession
DefaultSqlSession.java
java public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; }
到这里session也创建完成了之后就是获取mapper执行查询了 session.getMapper
DefaultSqlSession.java
java @Override public T getMapper(Class type) { return configuration.getMapper(type, this); }
Configuration.java
java public T getMapper(Class type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
MapperRegistry.java
java public T getMapper(Class type, SqlSession sqlSession) { final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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); } }
MapperProxyFactory.java
java protected T newInstance(MapperProxy mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
MapperProxy.java
动态代理来实现mapper的方法调用
java public class MapperProxy implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class mapperInterface; private final Map methodCache; public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } }
当我们执行userMapper.getUsers()的时候,通过动态代理进入invoke方法,之后获取缓存的方法,进入 cachedMapperMethod
先找是否已经创建过这个方法的封装类了,如果没有则去创建
MapperMethod.java
java public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, method); }
MapperMethod.java-SqlCommand.java 静态内部类
java public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) { String statementName = mapperInterface.getName() + "." + method.getName(); MappedStatement ms = null; //当程序走到这里的时候就会为true if (configuration.hasStatement(statementName)) { ms = configuration.getMappedStatement(statementName); } ///...... if (ms == null) { //..... } else { name = ms.getId(); type = ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) { throw new BindingException("Unknown execution method for: " + name); } } }
Configuration.java
java public boolean hasStatement(String statementName) { return hasStatement(statementName, true); } public boolean hasStatement(String statementName, boolean validateIncompleteStatements) { if (validateIncompleteStatements) { buildAllStatements(); } //主要这行 return mappedStatements.containsKey(statementName); }
可能忘了它什么时候添加进去的了,在那个小助手中的addMappedStatement方法,最后的时候进行的添加点我跳转到addMappedStatement
那么这里就直接走到最后的if了,将name和type赋值给SqlCommand,方法结束
之后还有创建MethodSignature
MapperMethod.java-MethodSignature.java 静态内部类
java public MethodSignature(Configuration configuration, Method method) { this.returnType = method.getReturnType(); this.returnsVoid = void.class.equals(this.returnType); this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); this.mapKey = getMapKey(method); this.returnsMap = (this.mapKey != null); //是否存在@Param注解 this.hasNamedParameters = hasNamedParams(method); this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters)); }
走完这一系列后回到MapperProxy.java中的invoke方法,最后执行 mapperMethod.execute(sqlSession, args)
MapperMethod.java
java public Object execute(SqlSession sqlSession, Object[] args) { Object result; if(){ //....... } else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method "" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
根据command的类型,当前这个案例是SELECT,进入后判断返回类型,当前是返回一个集合,returnsMany属性为ture,进入到 result = executeForMany(sqlSession, args);
java private Object executeForMany(SqlSession sqlSession, Object[] args) { List result; //封装入参 Object param = method.convertArgsToSqlCommandParam(args); //这个为false,没有设置过逻辑分页 if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.selectList(command.getName(), param, rowBounds); } else { result = sqlSession.selectList(command.getName(), param); } // issue #510 Collections & arrays support if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result; }
java public Object convertArgsToSqlCommandParam(Object[] args) { final int paramCount = params.size(); if (args == null || paramCount == 0) { return null; } else if (!hasNamedParameters && paramCount == 1) { return args[params.keySet().iterator().next()]; } else { final Map param = new ParamMap
陕西一妈妈凌晨求助孩子不能走我的老路,初三考成这样怎么办?读书不是人生唯一的出路,可对普通人家的孩子来说,通过读书可以获得更公平的发展机会,可以成就更好的自己。上一辈人经历过没有读书的苦,在教育下一代的时候,总是期望他们能够好好读书,每一
小雪后,3招提高抵抗力!孩子不生病,比吃什么都要补分享文章许尤佳育儿月令七十二候集解中对小雪的形容是(农历)十月中,雨下而为寒气所薄,故凝而为雪。小者未盛之辞。意思是小雪后,天气会变得寒冷,雨水会凝结成雪,但由于还没到最冷的时候,
你家的孩子会熬夜吗?你知道充足的睡眠对儿童有多重要?儿童睡眠不足会导致免疫功能下降,免疫功能低下以及生长发育出现障碍。同时,还会影响儿童生长发育影响儿童的心理健康增加家庭压力等。对于儿童来说,充足的睡眠是其生长发育与身心健康不可或缺
张馨月跟组林峰晒上海街拍,却被拍成五五身,气质跟吴千语有差距文忍耐娱乐林峰目前在上海拍戏,张馨月也带着女儿跟组,最新动态,张馨月跟组林峰晒上海街拍。张馨月挺喜欢晒自己的模特照和日常照的,毕竟之前是模特出身,女孩子也比较爱美,张馨月性格挺乖巧
我用最对得起上海的方式,走进上海,走进绝美的她本文作者爱水我是四个月之前的7月1日那天来到上海的。中午,和同事们小酌之后,到楼下闲逛时,才发现,上海保利剧院,居然就在我居住的大楼隔壁。古时候,嘉定的标志,是法华塔。那时候的嘉定
育儿经孟母三迁误颂几千年,孩子本是那块料相信大家都学过孟母三迁的故事,并把它当成育儿励志的信条来传颂。孟子(约公元前372年公元前289年),姬姓,孟氏,名轲,字子舆,战国时期邹国(今山东济宁邹城)人。战国时期著名哲学家
散文南方的冬季杜观水当早上的露水,沾湿村前的草垛当正午的阳光,少了往日的炽热当山梁的风,不再温柔抚摸脸颊我知道,冬天来了。雾是南方冬季的使者。多数时候,当村庄还在沉睡时,晨雾便游荡开了。那淡淡的
男女之间的暧昧关系,一般都是在这些情况下结束前言在我们的生活当中,有些事情并不是一辈子的,包括男女之间的感情也是一样的,当你觉得很美好的时候可能就会很糟糕。不要总是觉得生活很简单,其实在大多数时候生活都会很复杂,因为每一个人
沉默,是一种境界不是何事都需要喋喋不休,也不是何时都需要滔滔不绝,沉默在很多时候就是一种素养与见识。藏峰守拙,不轻言不妄语,就算有真知灼见也无需与不懂你的人乱费口舌,因为一些自以为是刚蛮自负的人,
茅台附赠的两个小酒杯只是用来喝酒的吗?建议弄懂,以免闹笑话相信很多人都有这种感觉当喝到一瓶优质的酱香酒后,会对它的味道难以忘怀。因为酱酒相对其它香型的酒来说,酿造周期会更长,所以酒体相对更醇厚,入口滋味也会更丰富,所以有这种感觉也是情理之
一到冬天就想吃肉,肉类烹饪想营养翻倍,搭配有讲究天气越来越冷,人体内的新陈代谢也在不断地增加,在这个过程中,体内的能量消耗也越来越大。有消耗就有吸收,给人体提供能量占据很大的比例是蛋白质和脂肪,所以寒冷天气一到,我们就馋肉了。从