java开发需要学习什么

Mybatis插件原理 - 简书
<div class="fixed-btn note-fixed-download" data-toggle="popover" data-placement="left" data-html="true" data-trigger="hover" data-content=''>
写了13328字,被19人关注,获得了54个喜欢
Mybatis插件原理
记录是一种精神,是加深理解最好的方式之一。
最近看了下Mybatis的源码,分析了Mybatis插件的实现方式,在这里把他记下来。
(如有欠缺还请指教)时间:日16:00
Mybatis插件又称拦截器,本篇文章中出现的拦截器都表示插件
Mybatis采用责任链模式,通过动态代理组织多个插件(拦截器),通过这些插件可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件。
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
总体概括为:
拦截执行器的方法
拦截参数的处理
拦截结果集的处理
拦截Sql语法构建的处理
Mybatis是通过动态代理的方式实现拦截的,阅读此篇文章需要先对Java的动态代理机制有所了解。可以参考博客
Mybatis四大接口
竟然Mybatis是对四大接口进行拦截的,那我们药先要知道Mybatis的四大接口对象 Executor, StatementHandler, ResultSetHandler, ParameterHandler。
上图Mybatis框架的整个执行过程。Mybatis插件能够对则四大对象进行拦截,可以包含到了Mybatis一次会议的所有操作。可见Mybatis的的插件很强大。
Executor是 Mybatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过 ResultSetHandler进行自动映射,另外,他还处理了二级缓存的操作。从这里可以看出,我们也是可以通过插件来实现自定义的二级缓存的。
StatementHandler是Mybatis直接和数据库执行sql脚本的对象。另外它也实现了Mybatis的一级缓存。这里,我们可以使用插件来实现对一级缓存的操作(禁用等等)。
ParameterHandler是Mybatis实现Sql入参设置的对象。插件可以改变我们Sql的参数默认设置。
ResultSetHandler是Mybatis把ResultSet集合映射成POJO的接口对象。我们可以定义插件对Mybatis的结果集自动映射进行修改。
插件Interceptor
Mybatis的插件实现要实现Interceptor接口,我们看下这个接口定义的方法。
public interface Interceptor {
Object intercept(Invocation invocation) throws T
Object plugin(Object target);
void setProperties(Properties properties);
这个接口只声明了三个方法。
setProperties方法是在Mybatis进行配置插件的时候可以配置自定义相关属性,即:接口实现对象的参数配置
plugin方法是插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:return Plugin.wrap(target, this);
intercept方法就是要进行拦截的时候要执行的方法
理解这个接口的定义,先要知道java动态代理机制。plugin接口即返回参数target对象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理对象。在调用对应对象的接口的时候,可以进行拦截并处理。
Mybatis四大接口对象创建方法
Mybatis的插件是采用对四大接口的对象生成动态代理对象的方法来实现的。那么现在我们看下Mybatis是怎么创建这四大接口对象的。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//确保ExecutorType不为空(defaultExecutorType有可能为空)
executorType = executorType == null ? defaultExecutorType : executorT
executorType = executorType == null ? ExecutorType.SIMPLE : executorT
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
executor = new SimpleExecutor(this, transaction);
if (cacheEnabled) {
executor = new CachingExecutor(executor);
executor = (Executor) interceptorChain.pluginAll(executor);
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 statementH
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterH
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetH
查看源码可以发现, Mybatis框架在创建好这四大接口对象的实例后,都会调用InterceptorChain.pluginAll()方法。InterceptorChain对象是插件执行链对象,看源码就知道里面维护了Mybatis配置的所有插件(Interceptor)对象。
--& Executor/ParameterHandler/ResultSetHander/StatementHandler
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
其实就是安顺序执行我们插件的plugin方法,一层一层返回我们原对象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理对象。当我们调用四大接口对象的方法时候,实际上是调用代理对象的响应方法,代理对象又会调用十大接口对象的实例。
Plugin对象
我们知道,官方推荐插件实现plugin方法为:Plugin.wrap(target, this);
public static Object wrap(Object target, Interceptor interceptor) {
// 获取插件的Intercepts注解
Map&Class&?&, Set&Method&& signatureMap = getSignatureMap(interceptor);
Class&?& type = target.getClass();
Class&?&[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length & 0) {
return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));
这个方法其实是Mybatis简化我们插件实现的工具方法。其实就是根据当前拦截的对象创建了一个动态代理对象。代理对象的InvocationHandler处理器为新建的Plugin对象。
插件配置注解@Intercepts
Mybatis的插件都要有Intercepts注解来指定要拦截哪个对象的哪个方法。我们知道,Plugin.warp方法会返回四大接口对象的代理对象(通过new Plugin()创建的IvocationHandler处理器),会拦截所有的执行方法。在代理对象执行对应方法的时候,会调用InvocationHandler处理器的invoke方法。Mybatis中利用了注解的方式配置指定拦截哪些方法。具体如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Set&Method& methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
可以看到,只有通过Intercepts注解指定的方法才会执行我们自定义插件的intercept方法。未通过Intercepts注解指定的将不会执行我们的intercept方法。
官方插件开发方式
@Intercepts({@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class TestInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget(); //被代理对象
Method method = invocation.getMethod(); //代理方法
Object[] args = invocation.getArgs(); //方法参数
// do something ...... 方法拦截前执行代码块
Object result = invocation.proceed();
// do something .......方法拦截后执行代码块
public Object plugin(Object target) {
return Plugin.wrap(target, this);
以上就是Mybatis官方推荐的插件实现的方法,通过Plugin对象创建被代理对象的动态代理对象。可以发现,Mybatis的插件开发还是很简单的。
自定义开发方式
Mybatis的插件开发通过内部提供的Plugin对象可以很简单的开发。只有理解了插件实现原理,对应不采用Plugin对象我们一样可以自己实现插件的开发。下面是我个人理解之后的自己实现的一种方式。
public class TestInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget(); //被代理对象
Method method = invocation.getMethod(); //代理方法
Object[] args = invocation.getArgs(); //方法参数
// do something ...... 方法拦截前执行代码块
Object result = invocation.proceed();
// do something .......方法拦截后执行代码块
public Object plugin(final Object target) {
return Proxy.newProxyInstance(Interceptor.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return intercept(new Invocation(target, method, args));
public void setProperties(Properties properties) {
当然,Mybatis插件的那这个时候Intercepts的注解起不到作用了。
我们在MyBatis配置了一个插件,在运行发生了什么
所有可能被拦截的处理类都会生成一个代理
处理类代理在执行对应方法时,判断要不要执行插件中的拦截方法
执行插接中的拦截方法后,推进目标的执行
如果有N个插件,就有N个代理,每个代理都要执行上面的逻辑。这里面的层层代理要多次生成动态代理,是比较影响性能的。虽然能指定插件拦截的位置,但这个是在执行方法时动态判断,初始化的时候就是简单的把插件包装到了所有可以拦截的地方。
因此,在编写插件时需注意以下几个原则:
不编写不必要的插件;
实现plugin方法时判断一下目标类型,是本插件要拦截的对象才执行Plugin.wrap方法,否者直接返回目标本省,这样可以减少目标被代理的次数。// 假如我们只要拦截Executor对象,那么我们应该这么做
public Object plugin(final Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
Mybatis插件很强大,可以对Mybatis框架进行很大的扩展。当然,如果你不理解Mybatis插件的原理,开发起来只能是模拟两可。在实际开发过程中,我们可以参考别人写的插件。下面是一个Mybatis分页的插件,可以为以后开发做参考。
* Mybatis - 通用分页插件(如果开启二级缓存需要注意)
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class PageHelper implements Interceptor {
public static final ThreadLocal&Page& localPage = new ThreadLocal&Page&();
* 开始分页
* @param pageNum
* @param pageSize
public static void startPage(int pageNum, int pageSize) {
localPage.set(new Page(pageNum, pageSize));
* 结束分页并返回结果,该方法必须被调用,否则localPage会一直保存下去,直到下一次startPage
public static Page endPage() {
Page page = localPage.get();
localPage.remove();
public Object intercept(Invocation invocation) throws Throwable {
if (localPage.get() == null) {
return invocation.proceed();
if (invocation.getTarget() instanceof StatementHandler) {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
// 分离代理对象链(由于目标类可能被多个插件拦截,从而形成多次代理,通过下面的两次循环
// 可以分离出最原始的的目标类)
while (metaStatementHandler.hasGetter("h")) {
Object object = metaStatementHandler.getValue("h");
metaStatementHandler = SystemMetaObject.forObject(object);
// 分离最后一个代理对象的目标类
while (metaStatementHandler.hasGetter("target")) {
Object object = metaStatementHandler.getValue("target");
metaStatementHandler = SystemMetaObject.forObject(object);
MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
//分页信息if (localPage.get() != null) {
Page page = localPage.get();
BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
// 分页参数作为参数对象parameterObject的一个属性
String sql = boundSql.getSql();
// 重写sql
String pageSql = buildPageSql(sql, page);
//重写分页sql
metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
Connection connection = (Connection) invocation.getArgs()[0];
// 重设分页参数里的总页数等
setPageParameter(sql, connection, mappedStatement, boundSql, page);
// 将执行权交给下一个插件
return invocation.proceed();
} else if (invocation.getTarget() instanceof ResultSetHandler) {
Object result = invocation.proceed();
Page page = localPage.get();
page.setResult((List) result);
* 只拦截这两种类型的
* &br&StatementHandler
* &br&ResultSetHandler
* @param target
public Object plugin(Object target) {
if (target instanceof StatementHandler || target instanceof ResultSetHandler) {
return Plugin.wrap(target, this);
public void setProperties(Properties properties) {
* 修改原SQL为分页SQL
* @param sql
* @param page
private String buildPageSql(String sql, Page page) {
StringBuilder pageSql = new StringBuilder(200);
pageSql.append("select * from (");
pageSql.append(sql);
pageSql.append(" ) temp limit ").append(page.getStartRow());
pageSql.append(" , ").append(page.getPageSize());
return pageSql.toString();
* 获取总记录数
* @param sql
* @param connection
* @param mappedStatement
* @param boundSql
* @param page
private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement,
BoundSql boundSql, Page page) {
// 记录总记录数
String countSql = "select count(0) from (" + sql + ") temp";
PreparedStatement countStmt =
ResultSet rs =
countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
boundSql.getParameterMappings(), boundSql.getParameterObject());
setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());
rs = countStmt.executeQuery();
int totalCount = 0;
if (rs.next()) {
totalCount = rs.getInt(1);
page.setTotal(totalCount);
int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1);
page.setPages(totalPage);
} catch (SQLException e) {
log.error("Ignore this exception", e);
} finally {
rs.close();
} catch (SQLException e) {
log.error("Ignore this exception", e);
countStmt.close();
} catch (SQLException e) {
log.error("Ignore this exception", e);
* 代入参数值
* @param ps
* @param mappedStatement
* @param boundSql
* @param parameterObject
* @throws SQLException
private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
Object parameterObject) throws SQLException {
ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler.setParameters(ps);
@Data //采用lombok插件编译
public static class Page&E& {
private int pageN
private int pageS
private int startR
private int endR
private List&E&
public Page(int pageNum, int pageSize) {
this.pageNum = pageN
this.pageSize = pageS
this.startRow = pageNum & 0 ? (pageNum - 1) * pageSize : 0;
this.endRow = pageNum * pageS
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
如果你是程序员,或者有一颗喜欢写程序的心,喜欢分享技术干货、项目经验、程序员日常囧事等等,欢迎投稿《程序员》专题。
专题主编:小...
· 279936人关注
javaWeb学习
· 11人关注
· 5人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:2955人阅读
JAVA(27)
数据库(30)
Mybatis(13)
Mybatis 系列之配置上一篇文章,我们简单了解了Mybatis,现在我们就来了解一下她的核心配置文件。注:以下内容部分转载自Mybatis官方文档。XML 映射配置文件MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置(settings)和属性(properties)信息。属性(properties)这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:&properties resource=&org/mybatis/example/config.properties&&
&property name=&username& value=&dev_user&/&
&property name=&password& value=&F2Fa3!33TYyg&/&
&/properties&其中的属性就可以在整个配置文件中使用来替换需要动态配置的属性值。比如:&dataSource type=&POOLED&&
&property name=&driver& value=&${driver}&/&
&property name=&url& value=&${url}&/&
&property name=&username& value=&${username}&/&
&property name=&password& value=&${password}&/&
&/dataSource&这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。属性也可以被传递到 SqlSessionBuilder.build()方法中。例如:SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, props);
// ... or ...
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, props);
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:在 properties 元素体内指定的属性首先被读取。然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。设置(settings)这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。设置参数描述有效值默认值cacheEnabled该配置影响的所有映射器中配置的缓存的全局开关。true | falsetruelazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。true | falsefalseaggressiveLazyLoading当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载。true | falsetruemultipleResultSetsEnabled是否允许单一语句返回多结果集(需要兼容驱动)。true | falsetrueuseColumnLabel使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。true | falsetrueuseGeneratedKeys允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。true | falseFalseautoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIALdefaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。SIMPLE REUSE BATCHSIMPLEdefaultStatementTimeout设置超时时间,它决定驱动等待数据库响应的秒数。Any positive integerNot Set (null)defaultFetchSizeSets the driver a hint as to control fetching size for return results. This parameter value can be override by a query setting.Any positive integerNot Set (null)safeRowBoundsEnabled允许在嵌套语句中使用分页(RowBounds)。true | falseFalsemapUnderscoreToCamelCase是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。true | falseFalselocalCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。SESSION | STATEMENTSESSIONjdbcTypeForNull当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType enumeration. Most common are: NULL, VARCHAR and OTHEROTHERlazyLoadTriggerMethods指定哪个对象的方法触发一次延迟加载。A method name list separated by commasequals,clone,hashCode,toStringdefaultScriptingLanguage指定动态 SQL 生成的默认语言。A type alias or fully qualified class name.org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDrivercallSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。true | falsefalselogPrefix指定 MyBatis 增加到日志名称的前缀。Any StringNot setlogImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGINGNot setproxyFactory指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。CGLIB | JAVASSISTJAVASSIST (MyBatis 3.3 or above)vfsImplSpecifies VFS implementationsFully qualified class names of custom VFS implementation separated by commas.Not set一个配置完整的 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=&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&类型别名(typeAliases)类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:&typeAliases&
&typeAlias alias=&Author& type=&domain.blog.Author&/&
&typeAlias alias=&Blog& type=&domain.blog.Blog&/&
&typeAlias alias=&Comment& type=&ment&/&
&typeAlias alias=&Post& type=&domain.blog.Post&/&
&typeAlias alias=&Section& type=&domain.blog.Section&/&
&typeAlias alias=&Tag& type=&domain.blog.Tag&/&
&/typeAliases&当这样配置时,Blog可以用在任何使用domain.blog.Blog的地方。也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:&typeAliases&
&package name=&domain.blog&/&
&/typeAliases&每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。看下面的例子:@Alias(&author&)
public class Author {
}配置环境(environments)MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者共享相同 Schema 的多个生产数据库, 想使用相同的 SQL 映射。许多类似的用例。不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:每个数据库对应一个 SqlSessionFactory 实例为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties);如果忽略了环境参数,那么默认环境将会被加载,如下所示:SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties);环境元素定义了如何配置环境。&environments default=&development&&
&environment id=&development&&
&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&注意这里的关键点:默认的环境 ID(比如:default=”development”)。每个 environment 元素定义的环境 ID(比如:id=”development”)。事务管理器的配置(比如:type=”JDBC”)。数据源的配置(比如:type=”POOLED”)。默认的环境和环境 ID 是一目了然的。随你怎么命名,只要保证默认环境要匹配其中一个环境ID。事务管理器(transactionManager)在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。数据源(dataSource)dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。许多 MyBatis 的应用程序将会按示例中的例子来配置数据源。然而它并不是必须的。要知道为了方便使用延迟加载,数据源才是必须的。有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):UNPOOLED– 这个数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择。 不同的数据库在这方面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理想的。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是JDBC驱动中可能包含的数据源类)。url – 这是数据库的 JDBC URL 地址。username – 登录数据库的用户名。password – 登录数据库的密码。defaultTransactionIsolationLevel – 默认的连接事务隔离级别。作为可选项,你也可以传递属性给数据库驱动。要这样做,属性的前缀为“driver.”,例如:driver.encoding=UTF8这将通过DriverManager.getConnection(url,driverProperties)方法传递值为 UTF8 的 encoding 属性给数据库驱动。POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。除了上述提到 UNPOOLED 下的属性外,会有更多属性用来配置 POOLED 的数据源:poolMaximumActiveConnections – 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)poolTimeToWait – 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。poolPingEnabled – 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。JNDI– 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性:initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。data_source – 这是引用数据源实例位置的上下文的路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给初始上下文。比如:env.encoding=UTF8这就会在初始上下文(InitialContext)实例化时往它的构造方法传递值为 UTF8 的 encoding 属性。映射器(mappers)既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:&!-- Using classpath relative resources --&
&mapper resource=&org/mybatis/builder/AuthorMapper.xml&/&
&mapper resource=&org/mybatis/builder/BlogMapper.xml&/&
&mapper resource=&org/mybatis/builder/PostMapper.xml&/&
&/mappers&
&!-- Using url fully qualified paths --&
&mapper url=&file:///var/mappers/AuthorMapper.xml&/&
&mapper url=&file:///var/mappers/BlogMapper.xml&/&
&mapper url=&file:///var/mappers/PostMapper.xml&/&
&/mappers&
&!-- Using mapper interface classes --&
&mapper class=&org.mybatis.builder.AuthorMapper&/&
&mapper class=&org.mybatis.builder.BlogMapper&/&
&mapper class=&org.mybatis.builder.PostMapper&/&
&/mappers&
&!-- Register all interfaces in a package as mappers --&
&package name=&org.mybatis.builder&/&
&/mappers&以上使用了四种方法来指定映射文件的位置:使用相对路径指定是映射文件存放位置,这里是相对根路径(src)使用绝对路径使用映射接口类使用包名称这些配置会告诉了 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要学习讨论的。下面给出一个用来学习的实际核心配置文件的例子,方便大家理解上述内容。&?xml version=&1.0& encoding=&UTF-8&?&
&!DOCTYPE configuration PUBLIC &-//mybatis.org//DTD Config 3.0//EN&
&http://mybatis.org/dtd/mybatis-3-config.dtd&&
&configuration&
使用了两种指定属性方法:
1.使用resource关键字包含外部属性文件
2.直接声明属性
如果在properties中专用明的属性与resource引用的外部属性文件中有重名的属性,
本处声明的属性优先级相对较高,会覆盖掉外部文件中的属性值
&properties resource=&mybatis-mysql.properties&&
&property name=&driver-mysql& value=&com.mysql.jdbc.Driver&&&/property&
&property name=&url& value=&jdbc:mysql://127.0.0.1:3306/etao& &&/property&
&property name=&username& value=&root&&&/property&
&property name=&password& value=&cope9020&&&/property&
&/properties&
&!-- 将数据库字段命名规则A_COLUMN转换为Java使用的驼峰式命名规则aCloumn --&
&settings&
&setting name=&mapUnderscoreToCamelCase& value=&true& /&
&/settings&
&!-- 定义短别名,方便在其他地方引用 --&
&typeAliases&
&typeAlias alias=&User& type=&com.emerson.learning.pojo.User& /&
&/typeAliases&
&!-- 配置多环境信息,由default指定默认使用的环境 --&
&environments default=&development&&
&environment id=&development&&
&transactionManager type=&JDBC& /&
&dataSource type=&POOLED&&
&property name=&driver& value=&${driver-mysql}& /&
&property name=&url& value=&${url}& /&
&property name=&username& value=&${username}& /&
&property name=&password& value=&${password}& /&
&/dataSource&
&/environment&
&environment id=&product&&
&transactionManager type=&JDBC& /&
&dataSource type=&POOLED&&
&property name=&driver& value=&com.mysql.jdbc.Driver& /&
&property name=&url& value=&jdbc:mysql://127.0.0.1:3306/etao& /&
&property name=&username& value=&root& /&
&property name=&password& value=&cope9020& /&
&/dataSource&
&/environment&
&/environments&
指定数据表映射文件位置,有以下四种方式:
1.使用相关资源
&mapper resource=&XXX/YYY/ZZZ/WWW.xml& /&
&/mappers&
2.使用全路径
&mapper url=&file:///XXX.xml& /&
&/mappers&
3.使用映射接口名称
&mapper class=&XXX.YYY.ZZZ.ClassName& /&
&/mappers&
4.使用包名称
&package name=&XXX/YYY/ZZZ& /&
&/mappers&
&mapper resource=&com/emerson/learning/mapping/User.xml& /&
&mapper class=&com.emerson.learning.dao.ICommunicatorDao& /&
&/mappers&
&/configuration&&附录《》《》《》《》《》《》《》《》&
一共有1位博乐进行推荐
多使用包名映射
[code=html]
&package name=&XXX/YYY/ZZZ& /&
&/mappers&
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:365245次
积分:4490
积分:4490
排名:第5185名
原创:111篇
转载:19篇
评论:126条
(1)(2)(3)(1)(2)(13)(7)(1)(6)(3)(2)(1)(1)(6)(2)(1)(2)(2)(1)(1)(2)(1)(3)(2)(2)(7)(2)(2)(2)(1)(4)(2)(6)(2)(16)(20)}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信