目录
- 目录
- 1) 简介(特点)
- 2) 项目历史
- 3) Configuration
1) 简介(特点)
- ORM(Object Relation Mapping)
- 半自动:全自动框架如 Hibernate
- 轻量级:在启动过程中需要的资源较少
2) 项目历史
- 前身为 Apache 的 iBatis(Internet Abatis)
- 2010.06 开发团队转投迁移到 Google Code,iBatis3 更名为 MyBatis
- 2013.11 迁移到 Github
3) Configuration
3.1) SqlMapConfig.xml
MyBatis 的核心配置文件一般命名为 SqlMapConfig.xml,遵循 mybatis-3.x.x.jar!/org/apache/ibatis/builder/xml/mybatis-3-config.dtd 此 DTD 规范,值得一提的是,此 DTD 限制了 XML 的标签顺序如下:
- properties
- settings
- typeAliases
- typeHandlers
- objectFactory
- objectWrapperFactory
- reflectorFactory
- plugins
- environments
- databaseIdProvider
- mappers
3.1.1) Properties
可用于在子标签中定义一些常量,也可引入一个 prop 文件,还能通过 SqlSessionFactoryBuilder.build() 传入。在 XML 中可以使用 ${key} 引用它们,3.4.2 后支持 ${key:default} 默认值语法,通常用于将 SqlMapConfig 中的 JDBC 配置和其他配置解耦
- 通过 properties 属性引入 prop:
- resource:通过项目内资源引入
- url:通过 url 引入
示例:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
</properties>
3.1.2) Setting
此配置可改变 MyBatis 在运行时的行为,这里就不复制了,直接上官方文档地址: https://mybatis.org/mybatis-3/configuration.html#settings
示例:
<settings>
<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"/>
<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.1.3) TypeAliase
词如其名,给类型起别名,可以解耦并提升配置重用性,调用时不区分大小写
需要提到的是,MyBatis 内置的一些常见别名如下,规律为基本类型前加下划线,引用类型适用原类名即可:
_byte, _long, _short, _int, _integer, _double, _float, _boolean, string, byte, long, short, int, integer, double, float, boolean, date, decimal, bigdecimal, object, map, hashmap, list, arraylist, collection, iterator
可通过子标签,和 (包扫描 + 注解重写) 这两种方式定义别名
子标签定义示例:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
</typeAliases>
包扫描定义示例:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
@Alias("author2")
public class Author {
...
}
3.1.4) TypeHandler
可以通过定义 TypeHandler 来实现 mapper 中入参的类型转换。
例子:mapper 中插入 DATE 数据,转换为 VARCHAR
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import org.apache.log4j.Logger;
@MappedJdbcTypes({JdbcType.VARCHAR}) // 对应数据库类型
@MappedTypes({Date.class}) // java数据类型
public class MyDateTypeHandler implements TypeHandler<Date>{
private Logger logger = Logger.getLogger(MyDateTypeHandler.class);
// 入库前的类型转换
@Override
public void setParameter(PreparedStatement ps, int i, Date parameter,
JdbcType jdbcType) throws SQLException {
logger.info("setParameter(PreparedStatement ps, int i, Date parameter,JdbcType jdbcType)....");
ps.setString(i, String.valueOf(parameter.getTime()));
}
// 查询后的数据处理
@Override
public Date getResult(ResultSet rs, String columnName) throws SQLException {
logger.info("getResult(ResultSet rs, String columnName)....");
return new Date(rs.getLong(columnName));
}
@Override
public Date getResult(ResultSet rs, int columnIndex) throws SQLException {
logger.info("getResult(ResultSet rs, int columnIndex)....");
return new Date(rs.getLong(columnIndex));
}
@Override
public Date getResult(CallableStatement cs, int columnIndex)
throws SQLException {
logger.info("getResult(CallableStatement cs, int columnIndex)....");
return cs.getDate(columnIndex);
}
}
3.1.4.1) HandlingEnum
Handling Enum 也属于 typeHandler 的一部分,用于处理输入输出 mapper 的枚举映射,MyBatis 内置了两种 Handler:EnumTypeHandler、EnumOrdinalTypeHandler
一般开发中如果需要用到,都会使用自定义 Handler, 先简单了解,以后用到再深究下下。
可参考文章:
3.1.5) ObjectFactory
MyBatis 在返回结果时,会构造它的实例,默认使用 ObjectFactory 的实现 DefaultObjectFactory 来构造,我们可以通过实现 ObjectFactory 来扩展一些功能。
示例:
<objectFactory type="org.mybatis.example.ExampleObjectFactory"/>
3.1.6) ObjectWrapperFactory
TODO
3.1.7) Plugin(Interceptor)
MyBatis 的插件基于拦截器思想,基于动态代理实现,我们可以对 MyBatis 中以下四大组件中的 method 进行拦截
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
3.1.7.1) 原理:
// InterceptorChain.java
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target); // 执行定义的 plugin 实现动态代理
}
return target;
}
// Configuratoin.java
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
// Create instance
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 将创建的 instance 交给 Interceptor Chain,执行完 pluginAll 后会返回一个重重(loop)代理对象
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler; // 返回代理对象
}
3.1.7.2) 介绍:
我们可以通过实现 Interceptor 接口,实现和重写接口中的三个扩展方法:
- intercept(Invocation invocation):此接口为主扩展方法,invocation.proceed() 可以继续运行被拦截的方法,使我们可以在前后扩展。
- Object plugin(Object target):是的它就是上一节我们分析原理时执行的 plugin 函数,此函数是一个默认方法,可以重写它对创建代理时扩展。
- setProperties(Properties properties):在 SqlMapConfig.xml 中声明插件的 plugin 标签定义的参数,会传递到 properties 中。
3.1.7.3) 栗子:
定义:
@Intercepts({@Signature(
type = Executor.class, method = "update",
args = {MappedStatement.class, Object.class}
)})
public class ExampleInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
/* Before process */ Object result = invocation.proceed(); /* After process */
return result;
}
}
声明:
<plugins>
<plugin interceptor="cat.wars.ExampleInterceptor"></plugin>
</plugins>
3.1.8) Environment
MyBatis 可以定义多个 environment 来实现如 dev、prod 环境数据源切换问题。
但要注意的是,虽然可以配置多个 environment,但每个 SqlSessionFactory 实例只能选择一个。
定义:
<environments default="dev"> <!-- 可以通过修改 default 属性实现数据源简单切换 -->
<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>
3.1.8.1) TransactionManager
MyBatis 的事务有两种类型:
- JDBC:直接使用 JDBC 提供的 commit 和 rollback,换句话说使用数据源提供的事务
- MANAGED:让容器来接管整个事务,默认会关闭数据库连接,如果不希望这样,可以将 closeConnection 属性设置为 false
定义:<transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>
3.1.8.2) DataSource
MyBatis 有三种内置数据源类型:
- UNPOOLED:不使用连接池,每次操作都打开关闭连接
- driver
- url
- username
- password
- defaultTransactionIsolationLevel
- defaultNetworkTimeout(ms)
- driver.encoding
- POOLED
- 上面列出的属性
- poolMaximumActiveConnections
- poolMaximumIdleConnections
- poolMaximumCheckoutTime
- poolTimeToWait
- poolMaximumLocalBadConnectionTolerance
- poolPingQuery
- poolPingEnabled
- poolPingConnectionsNotUsedFor
- JNDI:这个数据源的实现是为了能在 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文引用
- initial_context
- data_source
- env.encoding=UTF8
3.1.9) DatabaseIdProvider
通过 DatabaseIdProvider 配置可以实现多种 vender 数据源的支持。
原理是从 JDBC 的 DatabaseMetaData#getDatabaseProductName() 元信息中取得 vender name 来实现数据源 vender 的鉴别。
我们可以在 mapper 中的 statement 中加入 databaseId 属性来指定此 statement 优先供应给哪个 vender 使用。
栗子:
<databaseIdProvider type="DB_VENDOR" />
可以通过 property 属性为各 vender 起别名,使得不用考虑 vender 大小写问题:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
3.1.10) Mapper
最后是 mapper 的配置,用于声明 mapper 的存在 -。-,一共有 4 种声明方式:
- XML 声明:通过 mapper.xml 中定义的 namespace 找到对应的 class
- resource:使用项目中相对路径声明
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
- url:URL 声明
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
- resource:使用项目中相对路径声明
- CLASS 声明:当使用 class 声明时,由于 class 中没有定义 mapper.xml 的存在,所以对应的 mapper.xml 必须和 class 路径保持一致供 MyBatis 使用
- class:class 相对路径声明
<mapper class="org.mybatis.builder.AuthorMapper"/>
- package:class 所在包扫描声明
<package name="org.mybatis.builder"/>
- class:class 相对路径声明
3.2) mapper.xml
mapper.xml 中的内容太过繁多,赶脚可以用经验代替笔记,暂搁吧,丢官方文档地址:
https://mybatis.org/mybatis-3/sqlmap-xml.html