Mybatis 源码
技术深度决定了技术功底,决定了处理生产事故的速度
本文简单介绍一下 mybatis的源码
1.mybatis-config.xml
Configuration:
properties
settings
typeAliases
typeHandlers
ObjectFactory
plugins
environments
environment:
transactionManager
dataSource
databaseIdProviders
mappers
####################################################################
1.properties:
mybatis 可以通过properties来引入外部配置文件的内容
resource:引入类路径下的资源
url:引入磁盘路径下的资源
<configuration> <properties resource="dbConfig.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url"} /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> </configuration>
2.settings
一个配置完整的 settings 元素的示例如下:
<settings> name:设置项名 value:设置项取值
//影响所有映射器中配置的缓存全局开关 <setting name="cacheEnabled" value="true"/>
//延迟加载的全局开关,当开启时,所有关联对象都会延迟加载 <setting name="lazyLoadingEnabled" value="true"/>
// <setting name="multipleResultSetsEnabled" value="true"/>
// <setting name="useColumnLabel" value="true"/>
// <setting name="useGeneratedKeys" value="false"/>
// <setting name="autoMappingBehavior" value="PARTIAL"/>
// <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
// <setting name="defaultExecutorType" value="SIMPLE"/>
// <setting name="defaultStatementTimeout" value="25"/>
// <setting name="defaultFetchSize" value="100"/>
// <setting name="safeRowBoundsEnabled" value="false"/>
//是否开启驼峰命名规则映射,默认是false <setting name="mapUnderscoreToCamelCase" value="false"/>
// <setting name="localCacheScope" value="SESSION"/>
// <setting name="jdbcTypeForNull" value="OTHER"/>
// <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
3.typeAliases :别名处理器,可以为Java类型起别名,别名不区分大小写
<typeAliases> <!--typeAlias:为某个Java类型起别名 type: 指定要起别名的类型全类名;默认别名就是类名小写:employee alias: 指定新的别名 --> <typeAlias type="com.feng.pojo.Employee" alias="emp" /> </typeAliases>
<select id="getEmployeeById" resultType="emp">
select * from tab_employee where id = #{id}
</select>
推荐全类名作为返回类型
select id="getEmployeeById" resultType="com.feng.pojo.Employee">
select * from tab_employee where id = #{id}
</select>
也可以批量取别名:
<typeAliases>
<!--package:为某个包下的所有类批量起别名 name: 指定包名(为当前包以及下面所有的子包的每一个类都起一个默认别名:类名小写) -->
<package name="com.feng.pojo" />
</typeAliases>
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
4.typeHandlers
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
你可以重写已有的类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler
接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler
, 并且可以(可选地)将它映射到一个 JDBC 类型。比如:
@MappedJdbcTypes(JdbcType.VARCHAR) public class ExampleTypeHandler extends BaseTypeHandler<String> { @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter); } @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { return rs.getString(columnName); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return rs.getString(columnIndex); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return cs.getString(columnIndex); } }
<!-- mybatis-config.xml --> <typeHandlers> <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/> </typeHandlers>
5. plugins
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
// ExamplePlugin.java @Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})}) public class ExamplePlugin implements Interceptor { private Properties properties = new Properties(); public Object intercept(Invocation invocation) throws Throwable { // implement pre processing if need Object returnObject = invocation.proceed(); // implement post processing if need return returnObject; } public void setProperties(Properties properties) { this.properties = properties; } }
<!-- mybatis-config.xml --> <plugins> <plugin interceptor="org.mybatis.example.ExamplePlugin"> <property name="someProperty" value="100"/> </plugin> </plugins>
上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行底层映射语句的内部对象。
提示 覆盖配置类
除了用插件来修改 MyBatis 核心行为以外,还可以通过完全覆盖配置类来达到目的。只需继承配置类后覆盖其中的某个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会极大影响 MyBatis 的行为,务请慎之又慎。
6.environments
<!-- environments:环境,mybatis可以配置多种环境 environment:配置一个具体的环境信息;必须有以下两个标签;ID代表当前环境的唯一标识
transactionManager:事物管理器
type: 管理器类型;JDBC(JdbcTransactionFactory) | MANAGED(ManagedTransactionFactory)
自定义管理器:实现TransactionFactory接口,type指定为全类名
dataSource:数据源
type:数据源类型: UNPOOLED(UnpooledDataSourceFactory) :不支持连接池
| JNDI(JndiDataSourceFactory) 支持JNDI
| POOLED(PooledDataSourceFactory) :支持连接池
自定义数据源:实现DataSourceFactory接口;type指定为全类名
-->
<environments default="dev">
<environment id="test">
<transactionManager type="JDBC"> </transactionManager>
<dataSource type="POOLED"></dataSource>
</environment>
<environment id="dev"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
7.数据库厂商标识(databaseIdProvider)
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId
属性。 MyBatis 会加载带有匹配当前数据库 databaseId
属性和所有不带 databaseId
属性的语句。 如果同时找到带有 databaseId
和不带 databaseId
的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider
即可:
<databaseIdProvider type="DB_VENDOR" />
databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName()
返回的字符串。 由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:
<databaseIdProvider type="DB_VENDOR">
<property name="MYSQL" value="mysql"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<select id="getEmployeeById" resultType="emp" databaseId="mysql">
select * from emp_tab where identity = #{id}
</select>
8.映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:///
形式的 URL),或类名和包名等。例如:
resource: classpath下的sql映射文件
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
URL: 磁盘路径或网络路径下的sql映射文件
<!-- 使用完全限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers>
class: 引用(注册)接口
1. 有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
2. 没有映射文件,所有的sql都是利用注解写在接口上;
<!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers>
package: 批量注册
<!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了