Mybatis之工厂模式
文章目录
- Mybatis之工厂模式
- 一、趣说工厂模式
- 二、Mybatis中如何运用的
- 三、我在项目中真实运用工厂模式的案例
一、趣说工厂模式
工厂模式是Java中最常用的设计模式之一。
工厂模式就是提供一个工厂类,当有客户端需要调用的时候,只调用这个工厂类就可以得到自己想要的结果,从而无需关注某类的具体实现过程。
就好比,你是个富二代,你可以饭来张口
,衣来伸手
;你只负责拿钱去买买买
,而不用关心
你买的东西是怎么做
的,也不必关心钱是谁赚的
,从哪里来了。
二、Mybatis中如何运用的
在Mybatis中运用工厂模式最典型的就是SqlSessionFactory。
SqlSession是Mybatis中最最最核心的一个模块了。
可以简单的理解,Mybatis中所有的sql都是通过SqlSession来最终执行的。
可以执行jdbc的操作(增删改查)。
SqlSessionFactory就是构建SqlSession对象的一个工厂类。
工厂模式用一句话来说就是用来帮你创建对象的。
SqlSessionFactory中有一个openSession(…)方法。
如下所示:
代码语言:javascript复制public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
可以看到有很多种创建SqlSession的方式。
其中SqlSession openSession(ExecutorType execType);就是一个很典型的应用了工厂模式来达到目的的。
点进去这个SqlSession openSession(ExecutorType execType);到DefaultSqlSessionFactory类中,我们发现又调用了一个:
代码语言:javascript复制 @Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
继续点进去:
代码语言:javascript复制可以看到,真的是见名之意了。功能就是打开会话的
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//Executor:SQL语句的执行器
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " e, e);
} finally {
ErrorContext.instance().reset();
}
}
其中我们要讲的最核心的就是:“创建SQL语句的执行器”
代码语言:javascript复制//Executor:SQL语句的执行器
final Executor executor = configuration.newExecutor(tx, execType);
代码如下:
代码语言:javascript复制可以看到,在创建Executor对象的时候,是根据不同的类型,创建不同的对象的。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
三、我在项目中真实运用工厂模式的案例
我在使用ES的时候,就运用了工厂模式。
ES支持模糊匹配和精确匹配。
如果没接触过ES的话,可以把模糊匹配想象成是sql的like,可以把精确匹配想象成sql中的=号。
在项目中的需求是:
返回匹配数据结果的前100条数据。
返回的数据 <= 100
旧的实现步骤:
1、一步到位,直接模糊匹配,然后返回前100条。
不足之处:例如:我搜索一个“食堂相关制度未在食堂公示”。 ES会进行切词,可能会切成:“食堂”,“相关制度”,“未在食堂公示”。 那么就会匹配这三个短语,从而返回这三个中的数据,最终的前100条数据中,并没有把“食堂相关制度未在食堂公示”这条数据显示在第一个位置。 这样用户的体验就会很不好了。
解决的办法:
最终目的:返回100条数据。
1、先进行精确匹配;
2、如果精确匹配到了说明这些数据肯定要展示在前面的;例如:此时已经匹配了10条数据。
3、然后对这10条数据进行排序,谁的长度最小,谁在前面。(进一步提高了准确度)
4、再进行模糊匹配;
5、此时应该拿的数据是100-10=90条。
6、返回90 10=100条数据。
此时的重点是:
在写代码的时候,我们要创建两套代码,这两套代码的相似度在99%,只有一个调用的方法变了,其余的参数都没变,显得很冗余。
旧:
代码语言:javascript复制//模糊构造模糊查询的SearchSourceBuilder对象
private SearchSourceBuilder builderMatchQuery(参数......) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
QueryBuilder queryBuilder = null;
.....此处省略N行代码
//核心代码就是这一句, QueryBuilders.matchQuery返回的QueryBuilder对象就是一个可以构造一个模糊查询的条件
queryBuilder = QueryBuilders.matchQuery(searchType,content);
.....此处省略N行代码
return searchSourceBuilder;
}
//构造精确查询的SearchSourceBuilder对象
private SearchSourceBuilder builderMatchPhraseQuery(参数......) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
QueryBuilder queryBuilder = null;
......
//精确查询 QueryBuilders.matchPhraseQuery返回的QueryBuilder,就是用来构建精确查询的条件
queryBuilder = QueryBuilders.matchPhraseQuery(searchType,content);
.......
return searchSourceBuilder;
}
我们可以看到两句核心的语句是:
代码语言:javascript复制QueryBuilders.matchPhraseQuery和QueryBuilders.matchQuery都可以返回QueryBuilder对象。那么可不可以简单一些呢?
QueryBuilder queryBuilder = null;
//QueryBuilders.matchPhraseQuery返回的QueryBuilder,就是用来构建精确查询的条件
queryBuilder = QueryBuilders.matchPhraseQuery(searchType,content);
// QueryBuilders.matchQuery返回的QueryBuilder对象就是一个可以构造一个模糊查询的条件
queryBuilder = QueryBuilders.matchQuery(searchType,content);
答案是可以的,利用工厂模式就可以很好的解决。
运用工厂模式:
代码语言:javascript复制可以看到下面这段代码就可以把上面的代码减少了50%的量。
我根据传递的buildQueryType参数,决定返回的QueryBuilder对象是哪个。
//模糊构造模糊查询的SearchSourceBuilder对象
private SearchSourceBuilder builderMatchQueryAndMatchPhraseQuery(其他参数......,Type buildQueryType) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
QueryBuilder queryBuilder = null;
.....此处省略N行代码
//模糊查询
if(buildQueryType==Type.BOOLEAN){
queryBuilder = QueryBuilders.matchQuery(searchType,content);
//精确查询
}else if(buildQueryType==Type.PHRASE){
queryBuilder = QueryBuilders.matchPhraseQuery(searchType,content);
}
.....此处省略N行代码
return searchSourceBuilder;
}
我写的上面这个代码,并不是真正的模仿工厂模式来写的,而且运用了工厂模式的思想。
简单吧。