mybatis-config.xml解析
mybatis-config.xml解析
·
入口
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
build方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
//构建sqlSessionFactory 返回默认实现
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
parse方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
/**
* 3.构建Configuration
* 找到configuration标签
*/
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration方法
private void parseConfiguration(XNode root) {
try {
//解析<properties resource="dbConfig.properties"></properties>节点
propertiesElement(root.evalNode("properties"));
//解析<settings></settings>节点
//拿到所有的子节点
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
//解析typeAliases节点
typeAliasesElement(root.evalNode("typeAliases"));
//解析plugins节点
pluginElement(root.evalNode("plugins"));
//解析objectFactory节点
objectFactoryElement(root.evalNode("objectFactory"));
//解析objectWrapperFactory节点
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析reflectorFactory节点
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
//解析environments节点
environmentsElement(root.evalNode("environments"));
//解析databaseIdProviders节点
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析typeHandlers节点
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers节点
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
Properties标签
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
//获取settings下的子标签
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
// 检查是不是所有的配置已经在Configuration中声明 <setting name="logImpl" value="LOG4J"/>
//首先检查name属性是不是在Configuration类中进行了声明,方法中使用的反射技术进行的检查
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;
}
typeAliases标签
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {//遍历子节点
//子节点有两种类型 一种是 package标签,一种是typeAlias标签
if ("package".equals(child.getName())) {
/**
* 为一个包下的所有类起别名,name为包名。别名和类名相同,省略包名
<typeAliases>
<package name="com.it.entity"></package>
</typeAliases>
*/
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
/**
* 为一个类起别名,type为全类名,alias为别名
<typeAliases>
<typeAlias type="com.it.entity.Student" alias="Student"></typeAlias>
</typeAliases>
*/
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
//如果别名未设置 则查找是有@Alias注解 如果还没有 则使用java中类名作为key
typeAliasRegistry.registerAlias(clazz);
} else {
//如果设置了别名 则直接使用别名作为key
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
plugin标签
/**
* MyBatis 的插件实际上就是拦截器
* 并且提供了可扩展的位置有4个
* Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
注意:分页助手的插件 配置在mapper之前
<plugin interceptor="com.github.pagehelper.PageHelper">
指定方言
<property name="dialect" value="mysql"/>
* @param parent
* @throws Exception
*/
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
objectFactory标签
/**
* 对象工厂 用于创建和表对应的实体类对象
* MyBatis 内置了一个 DefaultObjectFactory 来实现结果对象的创建,而我们定义的结果对象通常都有默认的无参构造器,
* 或者有显式定义无参构造器,这样也是方便 DefaultObjectFactory 帮我们创建结果对象
* @param context
* @throws Exception
*/
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
//注册到mybatis中ExtendsObjectFactory 继承了DefaultObjectFactory 也可以实现 ObjectFactory
//<objectFactory type="com.linkedbear.mybatis.factory.ExtendsObjectFactory"/>
String type = context.getStringAttribute("type");
Properties properties = context.getChildrenAsProperties();
ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
factory.setProperties(properties);
configuration.setObjectFactory(factory);
}
}
environments标签
/**
* 它类似于 SpringFramework 中的 profiles 的概念,它允许我们定义多个环境,以分别定义不同的数据库连接配置。
* 这种场景在我们平时开发中是很常见的:开发、测试、生产分别要用到 3 种不同的环境,连接 3 个不同的数据库,这个时候就需要分别配置了。
* 尽管我们可以定义多个环境配置,但激活生效的只允许选择一个,激活的方式是在 <environments> 标签上声明 default 属性。
* <environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="production">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${prod.driverClassName}"/>
<property name="url" value="${prod.url}"/>
<property name="username" value="${prod.username}"/>
<property name="password" value="${prod.password}"/>
</dataSource>
</environment>
</environments>
* @param context
* @throws Exception
*/
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)) {//默认环境default值与其他环境的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());
}
}
}
}
/**
* 数据库厂商标识
* databaseIdProvider 数据库厂商标识,它为我们提供了数据库可移植性的支持。
* 了解 Hibernate 的小伙伴应该知道,Hibernate 的数据库可移植性是天然的,
* MyBatis 虽然不像 Hibernate 那样全自动,但它也提供了支持的方案:我们在编写 CmpRecordMapper.xml 时,
* 针对不同的 statement ,声明不同的数据库厂商标识,MyBatis 即会动态的根据数据库厂商标识,使用不同的 statement ,
* 从而达到不同的数据库发送不同 SQL 的效果。
* <databaseIdProvider type="DB_VENDOR"> DB_VENDOR是固定的
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="PostgreSQL" value="postgresql"/>
</databaseIdProvider>
每个 statement 都可以声明 databaseId 属性,以指定不同的数据库厂商。
<select id="findAllByDepartmentId" parameterType="string" resultType="com.linkedbear.mybatis.entity.User"
databaseId="mysql">
select * from tbl_user where department_id = #{departmentId}
</select>
<select id="findAllByDepartmentId" parameterType="string" resultType="com.linkedbear.mybatis.entity.User"
databaseId="postgresql">
注意这里查的表不一样
select * from users where department_id = #{departmentId}
</select>
* @param context
* @throws Exception
*/
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);
}
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
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.");
}
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
typeHandlers标签
/**
* 类型处理器 用于java数据类型与jdbc数据类型之间的转换
*
* 类型处理器 javaType jdbcType
IntegerTypeHandler Integer 、int int / numeric
DoubleTypeHandler Double 、double double / numberic
StringTypeHandler String varchar / char
DateTypeHandler Date timestamp
EnumTypeHandler Enum varchar / char
<typeHandlers>
<typeHandler handler="com.linkedbear.mybatis.handler.DepartmentTypeHandler"
javaType="com.linkedbear.mybatis.entity.Department" jdbcType="VARCHAR"/>
</typeHandlers>
<select id="findAllUseTypeHandler" resultMap="userHandlerMap">
select * from tbl_user
</select>
<resultMap id="userHandlerMap" type="com.linkedbear.mybatis.entity.User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="birthday" column="birthday"/>
<association property="department" javaType="com.linkedbear.mybatis.entity.Department"/>
</resultMap>
* @param parent
*/
private void typeHandlerElement(XNode parent) {
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);
}
}
}
}
}
mappers标签
/**
* 映射器
* <mappers>
<mapper resource="mapper/department.xml"/> 直接加载mapper.xml
<mapper class="com.linkedbear.mybatis.mapper.UserMapper"/> 加载Mapper接
<package name="com.linkedbear.mybatis.mapper"/> 包扫描Mapper接口
</mappers>
* @param parent
* @throws Exception
*/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//包扫描 获取Mapper接口的包路径
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//获取mapper.xml 配置文件的路径(一般再类路径(classpath))
String resource = child.getStringAttribute("resource");
//也可以指定网络mapper.xml的路径
String url = child.getStringAttribute("url");
//获取mapper接口
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {//从类路径下加载mapper.xml
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) {//从网络上加载mapper.xml
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) {//加载mapper接口
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.");
}
}
}
}
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)