简单读!Mybatis源码(一)一条select的一生
工具除了会用,还应该多做点。我觉得使用一个软件工具(开源类),一般会经历几个步骤:
1. 通过wiki了解大致作用,然后开始码代码;
2. 系统性地学习其特性,找出可能需要的点,用上去;
3. 通过阅读其源码,清楚其来龙去脉;
4. 有能力你就去超越别人;
mybatis作为orm框架给我们带来了很多方便,其定制功能也让我们惊喜!还是来看看别人怎么做到的吧!
1. 下载git仓库, https://github.com/mybatis/mybatis-3
2. 打开IDE, 找到 test 包
3. 进入 org.apache.ibatis.autoconstructor.AutoConstructorTest, 有一个完整的sql 样例
- public class AutoConstructorTest {
- private static SqlSessionFactory sqlSessionFactory;
- @BeforeClass
- public static void setUp() throws Exception {
- // create a SqlSessionFactory
- try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
- sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
- }
- // populate in-memory database
- BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
- "org/apache/ibatis/autoconstructor/CreateDB.sql");
- }
- @Test
- public void fullyPopulatedSubject() {
- try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
- final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
- final Object subject = mapper.getSubject(1);
- assertNotNull(subject);
- }
- }
- @Test(expected = PersistenceException.class)
- public void primitiveSubjects() {
- try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
- final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
- mapper.getSubjects();
- }
- }
- @Test
- public void annotatedSubject() {
- try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
- final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
- verifySubjects(mapper.getAnnotatedSubjects());
- }
- }
- @Test(expected = PersistenceException.class)
- public void badSubject() {
- try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
- final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
- mapper.getBadSubjects();
- }
- }
- @Test
- public void extensiveSubject() {
- try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
- final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
- verifySubjects(mapper.getExtensiveSubject());
- }
- }
- private void verifySubjects(final List<?> subjects) {
- assertNotNull(subjects);
- Assertions.assertThat(subjects.size()).isEqualTo(3);
- }
- }
目标方法:fullyPopulatedSubject(),最终进行一条 select 查询:
- SELECT * FROM subject WHERE id = #{id}
首先执行的肯定 setup()方法了,setup()做了三件事:
1. 加载mybatis-config文件读取到reader中;
2. 将配置文件传递工厂builder中创建会话工厂;
3. 执行hsqldb内存数据库的初始化工作;(非本文重点)
接下来,咱们从源码的角度,主要看会话工厂的创建过程!
创建 sqlSessionFactory
- try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
- sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
- }
- // SqlSessionFactoryBuilder.build(reader) -> XMLConfigBuilder.parse()
- // org.apache.ibatis.session.SqlSessionFactoryBuilder
- public SqlSessionFactory build(Reader reader) {
- return build(reader, null, null);
- }
- // 建立factory
- public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
- try {
- XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
- return build(parser.parse());
- } catch (Exception e) {
- throw ExceptionFactory.wrapException("Error building SqlSession.", e);
- } finally {
- // 加载完成后,上下文管理器重置
- ErrorContext.instance().reset();
- try {
- reader.close();
- } catch (IOException e) {
- // Intentionally ignore. Prefer previous error.
- }
- }
- }
标签解析:
- /**
- * Offline entity resolver for the MyBatis DTDs
- *
- * @author Clinton Begin
- * @author Eduardo Macarron
- */
- public class XMLMapperEntityResolver implements EntityResolver {
- private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
- private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
- private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
- private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";
- private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
- private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
- /**
- * Converts a public DTD into a local one
- *
- * @param publicId The public id that is what comes after "PUBLIC"
- * @param systemId The system id that is what comes after the public id.
- * @return The InputSource for the DTD
- *
- * @throws org.xml.sax.SAXException If anything goes wrong
- */
- @Override
- public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
- try {
- if (systemId != null) {
- String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);
- if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {
- return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);
- } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {
- return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);
- }
- }
- return null;
- } catch (Exception e) {
- throw new SAXException(e.toString());
- }
- }
- private InputSource getInputSource(String path, String publicId, String systemId) {
- InputSource source = null;
- if (path != null) {
- try {
- InputStream in = Resources.getResourceAsStream(path);
- source = new InputSource(in);
- source.setPublicId(publicId);
- source.setSystemId(systemId);
- } catch (IOException e) {
- // ignore, null is ok
- }
- }
- return source;
- }
- }
parser
- // org.apache.ibatis.parsing.XPathParser.XPathParser(), 设备一些必要参数
- public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
- commonConstructor(validation, variables, entityResolver);
- this.document = createDocument(new InputSource(reader));
- }
- private Document createDocument(InputSource inputSource) {
- // important: this must only be called AFTER common constructor
- try {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setValidating(validation);
- factory.setNamespaceAware(false);
- factory.setIgnoringComments(true);
- factory.setIgnoringElementContentWhitespace(false);
- factory.setCoalescing(false);
- factory.setExpandEntityReferences(true);
- DocumentBuilder builder = factory.newDocumentBuilder();
- builder.setEntityResolver(entityResolver);
- builder.setErrorHandler(new ErrorHandler() {
- @Override
- public void error(SAXParseException exception) throws SAXException {
- throw exception;
- }
- @Override
- public void fatalError(SAXParseException exception) throws SAXException {
- throw exception;
- }
- @Override
- public void warning(SAXParseException exception) throws SAXException {
- }
- });
- return builder.parse(inputSource);
- } catch (Exception e) {
- throw new BuilderException("Error creating document instance. Cause: " + e, e);
- }
- }
// 在返回fatory的时候调用 return build(parser.parse()); 将 config.xml 的配置全部解析到 configuration 实例中
- // 在返回fatory的时候调用 return build(parser.parse()); 将 config.xml 的配置全部解析到 configuration 实例中
- // org.apache.ibatis.builder.xml.XMLConfigBuilder.parse()
- public Configuration parse() {
- if (parsed) {
- throw new BuilderException("Each XMLConfigBuilder can only be used once.");
- }
- parsed = true;
- // 根元素为 /configuration, 依次向下解析
- parseConfiguration(parser.evalNode("/configuration"));
- return configuration;
- }
- // org.apache.ibatis.parsing.XPathParser.evalNode() 映射 xml 属性到 bean
- public XNode evalNode(Object root, String expression) {
- Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
- if (node == null) {
- return null;
- }
- return new XNode(this, node, variables);
- }
- private Object evaluate(String expression, Object root, QName returnType) {
- try {
- return xpath.evaluate(expression, root, returnType);
- } catch (Exception e) {
- throw new BuilderException("Error evaluating XPath. Cause: " + e, e);
- }
- }
具体解析项如下,从这里也一目了然,配置项支持什么:
- // 解析各配置参数到实例中
- private void parseConfiguration(XNode root) {
- try {
- //issue #117 read properties first
- propertiesElement(root.evalNode("properties"));
- Properties settings = settingsAsProperties(root.evalNode("settings"));
- // 这个配置没有用过吧
- loadCustomVfs(settings);
- // 解析别名设置
- typeAliasesElement(root.evalNode("typeAliases"));
- pluginElement(root.evalNode("plugins"));
- objectFactoryElement(root.evalNode("objectFactory"));
- objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
- reflectorFactoryElement(root.evalNode("reflectorFactory"));
- 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);
- }
- }
下面依次来看一下都怎么解析各配置项的吧~
properties 解析
- // propertiesElement(root.evalNode("properties")); url, resource 解析
- private void propertiesElement(XNode context) throws Exception {
- if (context != null) {
- Properties defaults = context.getChildrenAsProperties();
- String resource = context.getStringAttribute("resource");
- String url = context.getStringAttribute("url");
- if (resource != null && url != null) {
- throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
- }
- if (resource != null) {
- defaults.putAll(Resources.getResourceAsProperties(resource));
- } else if (url != null) {
- defaults.putAll(Resources.getUrlAsProperties(url));
- }
- Properties vars = configuration.getVariables();
- if (vars != null) {
- defaults.putAll(vars);
- }
- // 解析完成后,放到 parser 和 configuration 实例中
- parser.setVariables(defaults);
- configuration.setVariables(defaults);
- }
- }
settings 配置项解析,返回内容待处理
- // Properties settings = settingsAsProperties(root.evalNode("settings"));
- private Properties settingsAsProperties(XNode context) {
- if (context == null) {
- return new Properties();
- }
- Properties props = context.getChildrenAsProperties();
- // Check that all settings are known to the configuration class
- MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
- for (Object key : props.keySet()) {
- if (!metaConfig.hasSetter(String.valueOf(key))) {
- throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
- }
- }
- return props;
- }
- // loadCustomVfs(settings); 这个...
- private void loadCustomVfs(Properties props) throws ClassNotFoundException {
- String value = props.getProperty("vfsImpl");
- if (value != null) {
- String[] clazzes = value.split(",");
- for (String clazz : clazzes) {
- if (!clazz.isEmpty()) {
- @SuppressWarnings("unchecked")
- Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
- configuration.setVfsImpl(vfsImpl);
- }
- }
- }
- }
typeAliases 别名设置解析,主要做类型检查,及别名的注册工作
- // typeAliasesElement(root.evalNode("typeAliases"));
- private void typeAliasesElement(XNode parent) {
- if (parent != null) {
- for (XNode child : parent.getChildren()) {
- if ("package".equals(child.getName())) {
- // 针对package配置,需注册一系列别名,以 simpleName 代替
- String typeAliasPackage = child.getStringAttribute("name");
- configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
- } else {
- String alias = child.getStringAttribute("alias");
- String type = child.getStringAttribute("type");
- try {
- Class<?> clazz = Resources.classForName(type);
- if (alias == null) {
- typeAliasRegistry.registerAlias(clazz);
- } else {
- typeAliasRegistry.registerAlias(alias, clazz);
- }
- } catch (ClassNotFoundException e) {
- throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
- }
- }
- }
- }
- }
- // org.apache.ibatis.type.TypeAliasRegistry.registerAlias(), 具体数据结构为 map 封装
- public void registerAlias(Class<?> type) {
- String alias = type.getSimpleName();
- Alias aliasAnnotation = type.getAnnotation(Alias.class);
- if (aliasAnnotation != null) {
- alias = aliasAnnotation.value();
- }
- registerAlias(alias, type);
- }
- 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 (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
- throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
- }
- TYPE_ALIASES.put(key, value);
- }
plugins 配置项解析,将属性注入的plugin, 添加 plugin 到 configuration
- // pluginElement(root.evalNode("plugins"));
- private void pluginElement(XNode parent) throws Exception {
- if (parent != null) {
- for (XNode child : parent.getChildren()) {
- String interceptor = child.getStringAttribute("interceptor");
- Properties properties = child.getChildrenAsProperties();
- // 先创建一个plugin实例,备用
- Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
- // 配置属性的注入,回调
- interceptorInstance.setProperties(properties);
- // 添加到 configuration
- configuration.addInterceptor(interceptorInstance);
- }
- }
- }
- public class InterceptorChain {
- private final List<Interceptor> interceptors = new ArrayList<>();
- public Object pluginAll(Object target) {
- for (Interceptor interceptor : interceptors) {
- target = interceptor.plugin(target);
- }
- return target;
- }
- public void addInterceptor(Interceptor interceptor) {
- interceptors.add(interceptor);
- }
- public List<Interceptor> getInterceptors() {
- return Collections.unmodifiableList(interceptors);
- }
- }
objectFactory 配置项解析,与plugin原理相似,做替换自定义作用
- // objectFactoryElement(root.evalNode("objectFactory"));
- private void objectFactoryElement(XNode context) throws Exception {
- if (context != null) {
- String type = context.getStringAttribute("type");
- Properties properties = context.getChildrenAsProperties();
- ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
- factory.setProperties(properties);
- configuration.setObjectFactory(factory);
- }
- }
- // objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 没什么特别
- private void objectWrapperFactoryElement(XNode context) throws Exception {
- if (context != null) {
- String type = context.getStringAttribute("type");
- ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
- configuration.setObjectWrapperFactory(factory);
- }
- }
- // reflectorFactoryElement(root.evalNode("reflectorFactory"));
- private void reflectorFactoryElement(XNode context) throws Exception {
- if (context != null) {
- String type = context.getStringAttribute("type");
- ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
- configuration.setReflectorFactory(factory);
- }
- }
settings 选项解析,设置各种开关, 如缓存
- // settingsElement(settings); 设置各种开关, 如缓存
- private void settingsElement(Properties props) throws Exception {
- configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
- configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
- configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
- configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
- configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
- configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
- configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
- configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
- configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
- configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
- configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
- configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
- configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
- configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
- configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
- configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
- configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
- configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
- configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
- @SuppressWarnings("unchecked")
- Class<? extends TypeHandler> typeHandler = (Class<? extends TypeHandler>)resolveClass(props.getProperty("defaultEnumTypeHandler"));
- configuration.setDefaultEnumTypeHandler(typeHandler);
- configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
- configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
- configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
- configuration.setLogPrefix(props.getProperty("logPrefix"));
- @SuppressWarnings("unchecked")
- Class<? extends Log> logImpl = (Class<? extends Log>)resolveClass(props.getProperty("logImpl"));
- configuration.setLogImpl(logImpl);
- configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
- }
environments 环境配置设置,根据指定环境,获取相应配置,加载数据源配置
- // environmentsElement(root.evalNode("environments")); 环境配置
- private void environmentsElement(XNode context) throws Exception {
- if (context != null) {
- if (environment == null) {
- // 默认为 development
- environment = context.getStringAttribute("default");
- }
- for (XNode child : context.getChildren()) {
- String id = child.getStringAttribute("id");
- // 查找当前环境配置,指定加载
- if (isSpecifiedEnvironment(id)) {
- // 事务管理、数据源
- TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
- 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());
- }
- }
- }
- }
- // 环境配置包含, id, 事务管理, 数据源
- public Environment build() {
- return new Environment(this.id, this.transactionFactory, this.dataSource);
- }
databaseIdProvider 解析,对多数据源,包含多 机器,或多类型数据库 mysql,sqlserver
- // databaseIdProviderElement(root.evalNode("databaseIdProvider"));
- private void databaseIdProviderElement(XNode context) throws Exception {
- DatabaseIdProvider databaseIdProvider = null;
- if (context != null) {
- String type = context.getStringAttribute("type");
- // awful patch to keep backward compatibility
- if ("VENDOR".equals(type)) {
- type = "DB_VENDOR";
- }
- Properties properties = context.getChildrenAsProperties();
- databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
- databaseIdProvider.setProperties(properties);
- }
- Environment environment = configuration.getEnvironment();
- if (environment != null && databaseIdProvider != null) {
- String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
- configuration.setDatabaseId(databaseId);
- }
- }
自定义 typeHandler
- // typeHandlerElement(root.evalNode("typeHandlers")); 自定义 typeHandler
- private void typeHandlerElement(XNode parent) throws Exception {
- if (parent != null) {
- for (XNode child : parent.getChildren()) {
- if ("package".equals(child.getName())) {
- String typeHandlerPackage = child.getStringAttribute("name");
- typeHandlerRegistry.register(typeHandlerPackage);
- } else {
- String javaTypeName = child.getStringAttribute("javaType");
- String jdbcTypeName = child.getStringAttribute("jdbcType");
- String handlerTypeName = child.getStringAttribute("handler");
- Class<?> javaTypeClass = resolveClass(javaTypeName);
- JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
- Class<?> typeHandlerClass = resolveClass(handlerTypeName);
- if (javaTypeClass != null) {
- if (jdbcType == null) {
- typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
- } else {
- typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
- }
- } else {
- typeHandlerRegistry.register(typeHandlerClass);
- }
- }
- }
- }
- }
- // org.apache.ibatis.type.TypeHandlerRegistry.register(), 以map保存映射
- private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
- if (javaType != null) {
- Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
- if (map == null || map == NULL_TYPE_HANDLER_MAP) {
- map = new HashMap<>();
- TYPE_HANDLER_MAP.put(javaType, map);
- }
- map.put(jdbcType, handler);
- }
- ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
- }
mapper 解析,crud
- // mapperElement(root.evalNode("mappers"));
- 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);
- } else {
- String resource = child.getStringAttribute("resource");
- String url = child.getStringAttribute("url");
- String mapperClass = child.getStringAttribute("class");
- if (resource != null && url == null && mapperClass == null) {
- ErrorContext.instance().resource(resource);
- InputStream inputStream = Resources.getResourceAsStream(resource);
- XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
- mapperParser.parse();
- } else if (resource == null && url != null && mapperClass == null) {
- ErrorContext.instance().resource(url);
- InputStream inputStream = Resources.getUrlAsStream(url);
- XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
- mapperParser.parse();
- } else if (resource == null && url == null && mapperClass != null) {
- Class<?> mapperInterface = Resources.classForName(mapperClass);
- configuration.addMapper(mapperInterface);
- } else {
- throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
- }
- }
- }
- }
- }
mapper解析细节
- // org.apache.ibatis.builder.xml.XMLMapperBuilder.parse() mapper解析细节
- public void parse() {
- // 只解析一次 namespace
- if (!configuration.isResourceLoaded(resource)) {
- configurationElement(parser.evalNode("/mapper"));
- configuration.addLoadedResource(resource);
- bindMapperForNamespace();
- }
- // 解析 resultMaps
- parsePendingResultMaps();
- parsePendingCacheRefs();
- parsePendingStatements();
- }
- private void configurationElement(XNode context) {
- try {
- String namespace = context.getStringAttribute("namespace");
- if (namespace == null || namespace.equals("")) {
- throw new BuilderException("Mapper's namespace cannot be empty");
- }
- builderAssistant.setCurrentNamespace(namespace);
- cacheRefElement(context.evalNode("cache-ref"));
- cacheElement(context.evalNode("cache"));
- parameterMapElement(context.evalNodes("/mapper/parameterMap"));
- resultMapElements(context.evalNodes("/mapper/resultMap"));
- sqlElement(context.evalNodes("/mapper/sql"));
- buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
- } catch (Exception e) {
- throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
- }
- }
- private void parsePendingResultMaps() {
- Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
- synchronized (incompleteResultMaps) {
- Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
- while (iter.hasNext()) {
- try {
- iter.next().resolve();
- iter.remove();
- } catch (IncompleteElementException e) {
- // ResultMap is still missing a resource...
- }
- }
- }
- }
- private void parsePendingCacheRefs() {
- Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
- synchronized (incompleteCacheRefs) {
- Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
- while (iter.hasNext()) {
- try {
- iter.next().resolveCacheRef();
- iter.remove();
- } catch (IncompleteElementException e) {
- // Cache ref is still missing a resource...
- }
- }
- }
- }
- private void parsePendingStatements() {
- Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
- synchronized (incompleteStatements) {
- Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
- while (iter.hasNext()) {
- try {
- iter.next().parseStatementNode();
- iter.remove();
- } catch (IncompleteElementException e) {
- // Statement is still missing a resource...
- }
- }
- }
- }
// 最后,返回一个 DefaultSqlSessionFactory, 载入配置项
- public SqlSessionFactory build(Configuration config) {
- return new DefaultSqlSessionFactory(config);
- }
// 会话创建好后,可以执行sql了
- public void fullyPopulatedSubject() {
- // 打开一个连接,使用完成后关闭
- try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
- final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
- final Object subject = mapper.getSubject(1);
- assertNotNull(subject);
- }
- }
2. 打开获得一个数据库会话连接
- // org.apache.ibatis.session.defaults.SqlSession.openSession(); 打开一个数据库连接
- @Override
- public SqlSession openSession() {
- return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
- }
- 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);
- // new Executor
- final Executor executor = configuration.newExecutor(tx, execType);
- // 最后返回 DefaultSqlSession 使用
- 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();
- }
- }
- // org.apache.ibatis.session.Configuration.newExecutor()
- public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
- executorType = executorType == null ? defaultExecutorType : executorType;
- executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
- Executor executor;
- // 根据不同请求类型创建不同的 executor
- if (ExecutorType.BATCH == executorType) {
- executor = new BatchExecutor(this, transaction);
- } else if (ExecutorType.REUSE == executorType) {
- executor = new ReuseExecutor(this, transaction);
- } else {
- // 默认为 SIMPLE
- executor = new SimpleExecutor(this, transaction);
- }
- if (cacheEnabled) {
- // 创建缓存管理,将缓存请求委托给 executor 处理
- executor = new CachingExecutor(executor);
- }
- // 处理plugin 请求
- executor = (Executor) interceptorChain.pluginAll(executor);
- return executor;
- }
- // org.apache.ibatis.executor.CachingExecutor
- public CachingExecutor(Executor delegate) {
- this.delegate = delegate;
- delegate.setExecutorWrapper(this);
- }
- // 最后将加载好的各种配置和实际处理,由 DefaultSqlSession 包装好后返回
- public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
- this.configuration = configuration;
- this.executor = executor;
- this.dirty = false;
- this.autoCommit = autoCommit;
- }
3. 获取mapper,准备调用查询
- // org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper()
- @Override
- public <T> T getMapper(Class<T> type) {
- return configuration.<T>getMapper(type, this);
- }
- // org.apache.ibatis.binding.MapperRegistry.getMapper()
- @SuppressWarnings("unchecked")
- public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
- // 获取 MapperProxyFactory
- final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
- if (mapperProxyFactory == null) {
- throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
- }
- try {
- // 创建 mapper 实例
- return mapperProxyFactory.newInstance(sqlSession);
- } catch (Exception e) {
- throw new BindingException("Error getting mapper instance. Cause: " + e, e);
- }
- }
- // org.apache.ibatis.binding.newInstance();
- public T newInstance(SqlSession sqlSession) {
- final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
- return newInstance(mapperProxy);
- }
4. select, 查询数据
- //mapper.getSubject(1); 查询数据
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- if (Object.class.equals(method.getDeclaringClass())) {
- return method.invoke(this, args);
- } else if (isDefaultMethod(method)) {
- return invokeDefaultMethod(proxy, method, args);
- }
- } catch (Throwable t) {
- throw ExceptionUtil.unwrapThrowable(t);
- }
- // 加载缓存包装方法
- final MapperMethod mapperMethod = cachedMapperMethod(method);
- // 执行查询
- return mapperMethod.execute(sqlSession, args);
- }
- private MapperMethod cachedMapperMethod(Method method) {
- return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
- }
- // org.apache.ibatis.binding.MapperMethod.execute(sqlSession, args); 针对不同类型语句,做不同调用
- public Object execute(SqlSession sqlSession, Object[] args) {
- Object result;
- switch (command.getType()) {
- case INSERT: {
- Object param = method.convertArgsToSqlCommandParam(args);
- result = rowCountResult(sqlSession.insert(command.getName(), param));
- break;
- }
- case UPDATE: {
- Object param = method.convertArgsToSqlCommandParam(args);
- result = rowCountResult(sqlSession.update(command.getName(), param));
- break;
- }
- case DELETE: {
- Object param = method.convertArgsToSqlCommandParam(args);
- result = rowCountResult(sqlSession.delete(command.getName(), param));
- break;
- }
- case SELECT:
- 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 if (method.returnsCursor()) {
- result = executeForCursor(sqlSession, args);
- } else {
- // 转换参数
- Object param = method.convertArgsToSqlCommandParam(args);
- // 执行单条查询
- result = sqlSession.selectOne(command.getName(), param);
- if (method.returnsOptional() &&
- (result == null || !method.getReturnType().equals(result.getClass()))) {
- result = Optional.ofNullable(result);
- }
- }
- break;
- case FLUSH:
- result = sqlSession.flushStatements();
- break;
- default:
- 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;
- }
- // org.apache.ibatis.reflection.ParamNameResolver.getNamedParams() 返回参数,如果是单个参数,直接返回参数值
- // 如果是多个参数,则以 map 形式返回
- public Object getNamedParams(Object[] args) {
- final int paramCount = names.size();
- if (args == null || paramCount == 0) {
- return null;
- } else if (!hasParamAnnotation && paramCount == 1) {
- return args[names.firstKey()];
- } else {
- final Map<String, Object> param = new ParamMap<>();
- int i = 0;
- for (Map.Entry<Integer, String> entry : names.entrySet()) {
- param.put(entry.getValue(), args[entry.getKey()]);
- // add generic param names (param1, param2, ...)
- final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
- // ensure not to overwrite parameter named with @Param
- if (!names.containsValue(genericParamName)) {
- param.put(genericParamName, args[entry.getKey()]);
- }
- i++;
- }
- return param;
- }
- }
selectOne
- // org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(command.getName(), param);
- @Override
- public <T> T selectOne(String statement, Object parameter) {
- // Popular vote was to return null on 0 results and throw exception on too many.
- // 查询单条记录时,默认也是先查询list,然后取第一条
- List<T> list = this.<T>selectList(statement, parameter);
- if (list.size() == 1) {
- return list.get(0);
- } else if (list.size() > 1) {
- throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
- } else {
- return null;
- }
- }
- // 查询列表
- @Override
- public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
- try {
- // 先获取执行语句,此处 statement 为 id
- MappedStatement ms = configuration.getMappedStatement(statement);
- return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
- } catch (Exception e) {
- throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
- } finally {
- ErrorContext.instance().reset();
- }
- }
- // org.apache.ibatis.session.Configuration.getMappedStatement(id), 获取初始化解析出来的语句
- public MappedStatement getMappedStatement(String id) {
- return this.getMappedStatement(id, true);
- }
- public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
- if (validateIncompleteStatements) {
- buildAllStatements();
- }
- return mappedStatements.get(id);
- }
包装执行 query
- // executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
- // org.apache.ibatis.executor.CachingExecutor.query()
- @Override
- public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
- // 获取sql
- BoundSql boundSql = ms.getBoundSql(parameterObject);
- CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
- return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
- }
- // org.apache.ibatis.mapping.MappedStatement.getBoundSql()
- public BoundSql getBoundSql(Object parameterObject) {
- BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
- // 字段映射
- List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
- if (parameterMappings == null || parameterMappings.isEmpty()) {
- boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
- }
- // check for nested result maps in parameter mappings (issue #30)
- for (ParameterMapping pm : boundSql.getParameterMappings()) {
- String rmId = pm.getResultMapId();
- if (rmId != null) {
- ResultMap rm = configuration.getResultMap(rmId);
- if (rm != null) {
- hasNestedResultMaps |= rm.hasNestedResultMaps();
- }
- }
- }
- return boundSql;
- }
- // CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
- // return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
- // org.apache.ibatis.executor.CachingExecutor.query()
- @Override
- public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
- throws SQLException {
- Cache cache = ms.getCache();
- if (cache != null) {
- flushCacheIfRequired(ms);
- if (ms.isUseCache() && resultHandler == null) {
- ensureNoOutParams(ms, boundSql);
- @SuppressWarnings("unchecked")
- List<E> list = (List<E>) tcm.getObject(cache, key);
- if (list == null) {
- list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
- tcm.putObject(cache, key, list); // issue #578 and #116
- }
- return list;
- }
- }
- return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
- }
前面的query是一个cache的包装类,其实际查询动作需要委托给excuttor,而我们主要关心 queryFromDb 逻辑!
- // org.apache.ibatis.executor.SimpleExecutor.query() / BaseExecutor
- @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()) {
- clearLocalCache();
- }
- List<E> list;
- try {
- queryStack++;
- list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
- if (list != null) {
- handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
- } else {
- // 没有缓存,走db
- list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
- }
- } finally {
- queryStack--;
- }
- if (queryStack == 0) {
- for (DeferredLoad deferredLoad : deferredLoads) {
- deferredLoad.load();
- }
- // issue #601
- deferredLoads.clear();
- if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
- // issue #482
- clearLocalCache();
- }
- }
- return list;
- }
- // BaseExecutor
- 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;
- }
执行 doQuery,主要是获取完整的 statement,使handler有据可查!
- @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();
- // 获取statement, 处理 JDBC 接口
- StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
- stmt = prepareStatement(handler, ms.getStatementLog());
- return handler.<E>query(stmt, resultHandler);
- } finally {
- closeStatement(stmt);
- }
- }
- // org.apache.ibatis.session.Configuration.newStatementHandler(), 兼顾 plugins,
- public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
- StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
- statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
- return statementHandler;
- }
db查询
- // 为不同处理类型,建立不同的 handler
- public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
- switch (ms.getStatementType()) {
- case STATEMENT:
- delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
- break;
- case PREPARED:
- delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
- break;
- case CALLABLE:
- delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
- break;
- default:
- throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
- }
- }
- //stmt = prepareStatement(handler, ms.getStatementLog());
- // org.apache.ibatis.executor.SimpleExecutor.prepareStatement(), 获取 statement, JDBC
- private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
- Statement stmt;
- Connection connection = getConnection(statementLog);
- // sql录入
- stmt = handler.prepare(connection, transaction.getTimeout());
- // 参数绑定
- handler.parameterize(stmt);
- return stmt;
- }
- // BaseExecutor
- protected Connection getConnection(Log statementLog) throws SQLException {
- Connection connection = transaction.getConnection();
- if (statementLog.isDebugEnabled()) {
- return ConnectionLogger.newInstance(connection, statementLog, queryStack);
- } else {
- return connection;
- }
- }
- // org.apache.ibatis.transaction.jdbc.JdbcTransaction.getConnection()
- @Override
- public Connection getConnection() throws SQLException {
- if (connection == null) {
- // 为空时新建 connection
- openConnection();
- }
- return connection;
- }
- protected void openConnection() throws SQLException {
- if (log.isDebugEnabled()) {
- log.debug("Opening JDBC Connection");
- }
- connection = dataSource.getConnection();
- if (level != null) {
- connection.setTransactionIsolation(level.getLevel());
- }
- setDesiredAutoCommit(autoCommit);
- }
- // org.apache.ibatis.datasource.unpooled.getConnection()
- @Override
- public Connection getConnection() throws SQLException {
- return doGetConnection(username, password);
- }
- 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);
- }
- private Connection doGetConnection(Properties properties) throws SQLException {
- // 如果没有初始化驱动,初始化
- initializeDriver();
- //
- Connection connection = DriverManager.getConnection(url, properties);
- configureConnection(connection);
- return connection;
- }
- 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.newInstance();
- DriverManager.registerDriver(new DriverProxy(driverInstance));
- registeredDrivers.put(driver, driverInstance);
- } catch (Exception e) {
- throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
- }
- }
- }
- private void configureConnection(Connection conn) throws SQLException {
- if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
- conn.setAutoCommit(autoCommit);
- }
- if (defaultTransactionIsolationLevel != null) {
- conn.setTransactionIsolation(defaultTransactionIsolationLevel);
- }
- }
- // stmt = handler.prepare(connection, transaction.getTimeout()); 组装sql
- // org.apache.ibatis.executor.statement.RoutingStatementHandler.prepare()
- @Override
- public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
- return delegate.prepare(connection, transactionTimeout);
- }
- // org.apache.ibatis.executor.statement.PreparedStatementHandler.
- @Override
- public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
- ErrorContext.instance().sql(boundSql.getSql());
- Statement statement = null;
- try {
- // select * from tab where id = ?
- 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);
- }
- }
- // 调用jdbc connection.prepareStatement(sql), 初始化语句
- @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);
- }
- }
- // PreparedStatementHandler handler.parameterize(stmt);
- @Override
- public void parameterize(Statement statement) throws SQLException {
- parameterHandler.setParameters((PreparedStatement) statement);
- }
- // org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters()
- @Override
- public void setParameters(PreparedStatement ps) {
- ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
- List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
- if (parameterMappings != null) {
- for (int i = 0; i < parameterMappings.size(); i++) {
- ParameterMapping parameterMapping = parameterMappings.get(i);
- if (parameterMapping.getMode() != ParameterMode.OUT) {
- Object value;
- String propertyName = parameterMapping.getProperty();
- if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
- value = boundSql.getAdditionalParameter(propertyName);
- } else if (parameterObject == null) {
- value = null;
- } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
- value = parameterObject;
- } else {
- MetaObject metaObject = configuration.newMetaObject(parameterObject);
- value = metaObject.getValue(propertyName);
- }
- // TypeHandler 处理参数类型
- TypeHandler typeHandler = parameterMapping.getTypeHandler();
- JdbcType jdbcType = parameterMapping.getJdbcType();
- if (value == null && jdbcType == null) {
- jdbcType = configuration.getJdbcTypeForNull();
- }
- try {
- typeHandler.setParameter(ps, i + 1, value, jdbcType);
- } catch (TypeException e) {
- throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
- } catch (SQLException e) {
- throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
- }
- }
- }
- }
- }
- // return handler.<E>query(stmt, resultHandler);
- // org.apache.ibatis.executor.statement.RoutingStatementHandler.query()
- @Override
- public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
- return delegate.<E>query(statement, resultHandler);
- }
- // PreparedStatementHandler.query()
- @Override
- public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
- PreparedStatement ps = (PreparedStatement) statement;
- // 调用驱动程序执行 execute
- ps.execute();
- // 处理结果集映射
- return resultSetHandler.<E> handleResultSets(ps);
- }
- // org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(), 处理结果集映射
- @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);
- }
- // org.apache.ibatis.executor.resultset.ResultSetWrapper, 根据第一行返回数据集,初始化字段名及字段类型
- public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
- super();
- this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
- this.resultSet = rs;
- final ResultSetMetaData metaData = rs.getMetaData();
- final int columnCount = metaData.getColumnCount();
- for (int i = 1; i <= columnCount; i++) {
- columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
- // java.sql.Types, 处理返回的类型为具体的java类型映射到 org.apache.ibatis.type.JdbcType
- jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
- // java 类型全名
- classNames.add(metaData.getColumnClassName(i));
- }
- }
数据库结果映射到javabean
- // DefaultResultSetHandler.handleResultSet() 处理单条记录映射关系
- private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
- try {
- if (parentMapping != null) {
- handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
- } else {
- if (resultHandler == null) {
- DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
- handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
- multipleResults.add(defaultResultHandler.getResultList());
- } else {
- handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
- }
- }
- } finally {
- // issue #228 (close resultsets)
- closeResultSet(rsw.getResultSet());
- }
- }
- // handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
- public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
- if (resultMap.hasNestedResultMaps()) {
- // 嵌套数据的映射
- ensureNoRowBounds();
- checkResultHandler();
- handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
- } else {
- // 简单数据映射
- handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
- }
- }
- // 简单数据映射
- private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
- throws SQLException {
- DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
- ResultSet resultSet = rsw.getResultSet();
- // 跳过行偏移,难道不是在数据sql中添加 limit 进行数据筛选的?
- skipRows(resultSet, rowBounds);
- while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
- // 1. Discriminated 鉴别器
- ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
- //
- Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
- storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
- }
- }
- // 检测超过 limit 后,不再继续获取结果
- private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
- return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
- }
- // Discriminated
- public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
- Set<String> pastDiscriminators = new HashSet<>();
- Discriminator discriminator = resultMap.getDiscriminator();
- while (discriminator != null) {
- final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
- final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
- if (configuration.hasResultMap(discriminatedMapId)) {
- resultMap = configuration.getResultMap(discriminatedMapId);
- Discriminator lastDiscriminator = discriminator;
- discriminator = resultMap.getDiscriminator();
- if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
- break;
- }
- } else {
- break;
- }
- }
- return resultMap;
- }
- // 从结果set中获取值
- private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
- final ResultLoaderMap lazyLoader = new ResultLoaderMap();
- Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
- if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
- final MetaObject metaObject = configuration.newMetaObject(rowValue);
- boolean foundValues = this.useConstructorMappings;
- if (shouldApplyAutomaticMappings(resultMap, false)) {
- foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
- }
- foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
- foundValues = lazyLoader.size() > 0 || foundValues;
- rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
- }
- return rowValue;
- }
- // DefaultResultSetHandler
- private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
- throws SQLException {
- final Class<?> resultType = resultMap.getType();
- final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
- final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
- if (hasTypeHandlerForResultObject(rsw, resultType)) {
- return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
- } else if (!constructorMappings.isEmpty()) {
- return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
- } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
- return objectFactory.create(resultType);
- } else if (shouldApplyAutomaticMappings(resultMap, false)) {
- return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
- }
- throw new ExecutorException("Do not know how to create an instance of " + resultType);
- }
- // 构造器注入方式
- private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs,
- String columnPrefix) throws SQLException {
- final Constructor<?>[] constructors = resultType.getDeclaredConstructors();
- final Constructor<?> defaultConstructor = findDefaultConstructor(constructors);
- if (defaultConstructor != null) {
- return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, defaultConstructor);
- } else {
- for (Constructor<?> constructor : constructors) {
- if (allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
- return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, constructor);
- }
- }
- }
- throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
- }
- private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix, Constructor<?> constructor) throws SQLException {
- boolean foundValues = false;
- for (int i = 0; i < constructor.getParameterTypes().length; i++) {
- Class<?> parameterType = constructor.getParameterTypes()[i];
- String columnName = rsw.getColumnNames().get(i);
- TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
- Object value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(columnName, columnPrefix));
- constructorArgTypes.add(parameterType);
- constructorArgs.add(value);
- foundValues = value != null || foundValues;
- }
- // 调用构造方法注入值
- return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
- }
- // org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(), 调用构造器返回 bean 实例
- @SuppressWarnings("unchecked")
- @Override
- public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
- Class<?> classToCreate = resolveInterface(type);
- // we know types are assignable
- return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
- }
- private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
- try {
- Constructor<T> constructor;
- if (constructorArgTypes == null || constructorArgs == null) {
- constructor = type.getDeclaredConstructor();
- try {
- return constructor.newInstance();
- } catch (IllegalAccessException e) {
- if (Reflector.canControlMemberAccessible()) {
- constructor.setAccessible(true);
- return constructor.newInstance();
- } else {
- throw e;
- }
- }
- }
- constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
- try {
- return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
- } catch (IllegalAccessException e) {
- if (Reflector.canControlMemberAccessible()) {
- constructor.setAccessible(true);
- return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
- } else {
- throw e;
- }
- }
- } catch (Exception e) {
- StringBuilder argTypes = new StringBuilder();
- if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
- for (Class<?> argType : constructorArgTypes) {
- argTypes.append(argType.getSimpleName());
- argTypes.append(",");
- }
- argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
- }
- StringBuilder argValues = new StringBuilder();
- if (constructorArgs != null && !constructorArgs.isEmpty()) {
- for (Object argValue : constructorArgs) {
- argValues.append(String.valueOf(argValue));
- argValues.append(",");
- }
- argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
- }
- throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
- }
- }
数据集获取
- // 保存结果
- private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
- if (parentMapping != null) {
- linkToParents(rs, parentMapping, rowValue);
- } else {
- callResultHandler(resultHandler, resultContext, rowValue);
- }
- }
- @SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
- private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
- resultContext.nextResultObject(rowValue);
- ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
- }
- //
- private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
- throws SQLException {
- final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
- boolean foundValues = false;
- final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
- for (ResultMapping propertyMapping : propertyMappings) {
- String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
- if (propertyMapping.getNestedResultMapId() != null) {
- // the user added a column attribute to a nested result map, ignore it
- column = null;
- }
- if (propertyMapping.isCompositeResult()
- || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
- || propertyMapping.getResultSet() != null) {
- Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
- // issue #541 make property optional
- final String property = propertyMapping.getProperty();
- if (property == null) {
- continue;
- } else if (value == DEFERED) {
- foundValues = true;
- continue;
- }
- if (value != null) {
- foundValues = true;
- }
- if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
- // gcode issue #377, call setter on nulls (value is not 'found')
- metaObject.setValue(property, value);
- }
- }
- }
- return foundValues;
- }
- // collapseSingleResultList(multipleResults); 转换多级 list 为1级list
- @SuppressWarnings("unchecked")
- private List<Object> collapseSingleResultList(List<Object> multipleResults) {
- return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
- }
至此,一条select 语句搞定!
主要经历几个阶段:
1. 加载配置
2. 创建sqlSessionFactory
3. 获取sqlSession
4. jdbc连接
5. 获取mapper
6. 查询参数绑定
7. 获取db结果
8. 处理返回集字段映射
9. 缓存结果
10. 返回结果
简单读!Mybatis源码(一)一条select的一生的更多相关文章
- MyBatis源码解读(1)——SqlSessionFactory
在前面对MyBatis稍微有点了解过后,现在来对MyBatis的源码试着解读一下,并不是解析,暂时定为解读.所有对MyBatis解读均是基于MyBatis-3.4.1,官网中文文档:http://ww ...
- Mybatis源码解析-DynamicSqlSource和RawSqlSource的区别
XMLLanguageDriver是ibatis的默认解析sql节点帮助类,其中的方法其会调用生成DynamicSqlSource和RawSqlSource这两个帮助类,本文将对此作下简单的简析 应用 ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- MyBatis源码分析-SQL语句执行的完整流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- MyBatis源码分析(4)—— Cache构建以及应用
@(MyBatis)[Cache] MyBatis源码分析--Cache构建以及应用 SqlSession使用缓存流程 如果开启了二级缓存,而Executor会使用CachingExecutor来装饰 ...
- 读jQuery源码 - Deferred
Deferred首次出现在jQuery 1.5中,在jQuery 1.8之后被改写,它的出现抹平了javascript中的大量回调产生的金字塔,提供了异步编程的能力,它主要服役于jQuery.ajax ...
- 【MyBatis源码分析】select源码分析及小结
示例代码 之前的文章说过,对于MyBatis来说insert.update.delete是一组的,因为对于MyBatis来说它们都是update:select是一组的,因为对于MyBatis来说它就是 ...
- 【MyBatis源码解析】MyBatis一二级缓存
MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...
- 跟我一起读postgresql源码(八)——Executor(查询执行模块之——可优化语句的执行)
2.可优化语句的执行 可优化语句的共同特点是它们被查询编译器处理后都会生成査询计划树,这一类语句由执行器(Executor)处理.该模块对外提供了三个接口: ExecutorStart.Executo ...
- 学习笔记02(随便看看mybatis源码)
两个很有名的持久层hibernate和mybatis应该很熟悉不过了,两者最大相同点是底层都是对jdbc的封装,最大的不同点是前者是自动生成sql语句,后者是需要我们在映射文件中写出sql. 其实从以 ...
随机推荐
- Spring Cloud的概述(二)
1.什么是spring cloud? spring cloud,基于spring boot提供了一套微服务的解决方案,包括服务的注册与发现,配置中心,全链路监控,服务网管,负载均衡,熔断等组件,除了基 ...
- github node.js
#安装githubyum install git -y #下载nvmgit clone git://github.com/creationix/nvm.git ~/nvm #设置nvm 自动运行;ec ...
- linux中du与df的区别和联系
1,两者区别 du,disk usage,是通过搜索文件来计算每个文件的大小然后累加,du能看到的文件只是一些当前存在 的,没有被删除的.他计算的大小就是当前他认为存在的所有文件大小的累加和. df, ...
- python11 装饰器与闭包
一.装饰器定义 本质:一种函数,为其他函数增加新功能 原则: 1.不修改被修饰函数的源代码 2.不修改被修饰函数的调用方式 需要技能:装饰器=高阶函数+函数嵌套+闭包 二.高阶函数 定义:函数接收的参 ...
- Laravel 中使用 JWT 认证的 Restful API
Laravel 中使用 JWT 认证的 Restful API 5天前/ 678 / 3 / 更新于 3天前 在此文章中,我们将学习如何使用 JWT 身份验证在 Laravel 中构建 r ...
- Java 字符编码(二)Java 中的编解码
Java 字符编码(二)Java 中的编解码 java.nio.charset 包中提供了一套处理字符编码的工具类,主要有 Charset.CharsetDecoder.CharsetEncoder. ...
- eclipse汉化包
把eclipse英文汉化成中文,首先我们要知道自己安装的eclipse版本,可以在eclipse的安装目录下找到readme用浏览器打开查看版本,或者用记事本打开.eclipseproduct文件,查 ...
- mysql too many connection 解决
最近的项目用了动态切换数据源起初感觉还好,后来发现每次切换数据库都会创建一个新的连接,这样就导致大量的sleep线程.而mysql的默认sleep时间是28800秒....默认最大连接数为151,这就 ...
- 让粒子可以在白色背景显示 [Blending Shader 实操]
Unity3D 提供了粒子特效的各种shader,今天要说的是 Additive(因为项目最初就是用了Additive 发生了问题.. ε=ε=ε=┏(゜ロ゜;)┛) Additive Particl ...
- css摘要
由于需要,今天花三个小时了解一下css,在此记录一些摘要: 参考w3school 1. 当同一个 HTML 元素被不止一个样式定义时,会使用哪个样式呢? 一般而言,所有的样式会根据下面的规则层叠于一个 ...