SqlSessionFactoryBean 是一个Factory类,主要是用来生成SqlSessionFactory的。以下是SqlSessionFactoryBean的主要属性
private Resource configLocation; ///mapper文件Resource 对象数组
private Resource[] mapperLocations;
数据源 private DataSource dataSource; /事务工厂 private TransactionFactory transactionFactory; private Properties configurationProperties; private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); //根据给的入参实例化的 sqlSessionFactory 实例对象 ,getObjec() 方法返回的就是这个对象 private SqlSessionFactory sqlSessionFactory; //EnvironmentAware requires spring 3.1 private String environment = SqlSessionFactoryBean.class.getSimpleName(); private boolean failFast; /拦截器 可以在生成执行sql语句之前 修改sql语句或者是修改数据源 private Interceptor[] plugins; private TypeHandler<?>[] typeHandlers; private String typeHandlersPackage; private Class<?>[] typeAliases; private String typeAliasesPackage; private Class<?> typeAliasesSuperType; //issue #19. No default provider. private DatabaseIdProvider databaseIdProvider; private ObjectFactory objectFactory; private ObjectWrapperFactory objectWrapperFactory;
spring中定义的 sqlsessionFactoryBean 对象通过 Application.getBean 方法获得的对象实际上是 sqlsessionFactoryBean.getObject()方法返回的对象(如果想获得sqlsessionFactoryBean本身的话在对象id前面加一个 & 因为spring 在生成 FactoryBean 对象的时候会在bean id的前面加一个 &) 。
public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } return this.sqlSessionFactory; }
返回的实际上就是sqlsessionFactoryBean 本身的 sqlsessionFactory 这个对象的实例化是 afterPropertiesSet() 方法中 (实现了InitalizingBean 接口)
public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); this.sqlSessionFactory = buildSqlSessionFactory(); }进入 buildsqlsessinofactory() 方法
protected SqlSessionFactory buildSqlSessionFactory() throws IOException { Configuration configuration; XMLConfigBuilder xmlConfigBuilder = null; if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); configuration.setVariables(this.configurationProperties); } if (this.objectFactory != null) { configuration.setObjectFactory(this.objectFactory); } if (this.objectWrapperFactory != null) { configuration.setObjectWrapperFactory(this.objectWrapperFactory); } if (hasLength(this.typeAliasesPackage)) { String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } if (!isEmpty(this.typeAliases)) { for (Class<?> typeAlias : this.typeAliases) { configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } 添加拦截器 if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered plugin: '" + plugin + "'"); } } } if (hasLength(this.typeHandlersPackage)) { String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan : typeHandlersPackageArray) { configuration.getTypeHandlerRegistry().register(packageToScan); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers"); } } } if (!isEmpty(this.typeHandlers)) { for (TypeHandler<?> typeHandler : this.typeHandlers) { configuration.getTypeHandlerRegistry().register(typeHandler); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type handler: '" + typeHandler + "'"); } } } if (xmlConfigBuilder != null) { try { xmlConfigBuilder.parse(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'"); } } catch (Exception ex) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); } finally { ErrorContext.instance().reset(); } } if (this.transactionFactory == null) { this.transactionFactory = new SpringManagedTransactionFactory(); } configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); if (this.databaseIdProvider != null) { try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException e) { throw new NestedIOException("Failed getting a databaseId", e); } } 根据我们配置的mapper.xml文件 if (!isEmpty(this.mapperLocations)) { for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments());
/1. 在这个方法中会去把 mapper 文件中的namespace 转化成Class对象并把 该类存到 configuration对象的mapperRegistry属性中
用来记录 通过接口的函数名字和xml的id关连的 mapper接口
/2. 转化结果集 resultmap 并把他记录在 configuration 的resultMaps中
/3.
/4. 将每个sql 语句转化成 MappedStatement 并把他存在 configuration的 mappedStatements中 xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found"); } } return this.sqlSessionFactoryBuilder.build(configuration); }
了解下 Configuration 这个类 的主要属性 这个类包含了mybatis 的所有配置
//对应<environment> 标签 可以配置数据源 及数据源和环境的关系,可以根据环境切换数据源
protected Environment environment; protected boolean safeRowBoundsEnabled = false; protected boolean safeResultHandlerEnabled = true; protected boolean mapUnderscoreToCamelCase = false; protected boolean aggressiveLazyLoading = true; protected boolean multipleResultSetsEnabled = true; protected boolean useGeneratedKeys = false; protected boolean useColumnLabel = true; protected boolean cacheEnabled = true; protected boolean callSettersOnNulls = false; protected String logPrefix; protected Class <? extends Log> logImpl; protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION; protected JdbcType jdbcTypeForNull = JdbcType.OTHER; protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" })); protected Integer defaultStatementTimeout; protected Integer defaultFetchSize; protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE; protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL; protected Properties variables = new Properties(); protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory(); protected ObjectFactory objectFactory = new DefaultObjectFactory(); protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory(); protected MapperRegistry mapperRegistry = new MapperRegistry(this); protected boolean lazyLoadingEnabled = false; protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL protected String databaseId; /** * Configuration factory class. * Used to create Configuration for loading deserialized unread properties. * * @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300</a> (google code) */ protected Class<?> configurationFactory; //插件链
protected final InterceptorChain interceptorChain = new InterceptorChain(); protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(); protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry(); protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry(); //mappedStatements Map 每个sql语句对应yige MappedStatement对象 包括 入参 对象 出参对象 (都是String 即id) protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection"); protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
///sql语句和结果集 之间的映射关系 protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
//sql语句和 入参集 之间的映射关系 protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection"); protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection"); protected final Set<String> loadedResources = new HashSet<String>();
//一些sql 碎片 自定义的一些sql片段 protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers"); //未解析处理的statements protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>(); protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
//未解析处理的ResultMaps protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
//未解析处理的Methods protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>(); /* * A map holds cache-ref relationship. The key is the namespace that * references a cache bound to another namespace and the value is the * namespace which the actual cache is bound to. */ protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
(插一句工厂类的好处,有些类的属性比较多,一个一个配置会比较麻烦,还有些属性是不想暴露给外面的 使用Factory来生成bean会更好,如果想 生成代理类的话也比较方便,直接在工厂类生成bean的时候修改就好了)
一般我们拿到的sqlSessionFactory 是 DefaultSqlSessionFactory类型的
public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; }
把所有的配置都放在了configuration对象中 这里只是生成了sqlsessionfactory对象
再看看如何直接用配置文件中的id直接访问数据库 主要是利用session来操作数据库的 默认实现是DefaultSqlSession
主要有这几个属性
///mybatis的配置都在这对象中
pivate Configuration configuration;执行器,实际对数据库操作的对象
private Executor executor;//是否自动提交 private boolean autoCommit;private boolean dirty;以查询为例 最后调用的方法是 selectList()public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { 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(); } }在xml配置文件中 一条sql 对应一个 MappedStatement 这个映射是存在configuration中的mappedStatements中的 key是 (如果启用了namespace
是namespace+id)id value是对应的MappedStatement对象 看下MappedStatement属性
真正的执行方法是 executor的queryprivate String resource; private Configuration configuration; private String id; private Integer fetchSize; private Integer timeout; private StatementType statementType; private ResultSetType resultSetType; private SqlSource sqlSource; private Cache cache; private ParameterMap parameterMap; private List<ResultMap> resultMaps; private boolean flushCacheRequired; private boolean useCache; private boolean resultOrdered; private SqlCommandType sqlCommandType; private KeyGenerator keyGenerator; private String[] keyProperties; private String[] keyColumns; private boolean hasNestedResultMaps; private String databaseId; private Log statementLog; private LanguageDriver lang; private String[] resultSets;
执行器 executor有三种 默认的是simpleExecutor 其中query方法
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); }MappedStatement中的getBoundSql 的作用是给入参并解析出 BoundSql BoundSql中的属性//解析好的sql 语句
private String sql; private List<ParameterMapping> parameterMappings; private Object parameterObject; private Map<String, Object> additionalParameters; private MetaObject metaParameters;mybatis访问数据最终还是通过jdbc的方式去访问的 StatementHandler
回顾下jdbc连接数据库
http://www.cnblogs.com/centor/p/6142775.html 转载
看mybatis 是如何获取数据库连接的
通过 Exector中的Transaction获取连接Transaction中存有Datasource 数据源
如果是根据接口去访问 则还需要配置 MapperScannerConfigurer 这类实现了 BeanDefinitionRegistryPostProcessor会将配置的那个包下的所有接口都转成代理类