MyBatisフレームのデザインパターン
10275 ワード
前言:
前のいくつかの文章はソースの角度からMybatisフレームワークを分析しました。もちろん、優れたフレームワークとして、デザインモデルの使用も不可欠です。
この論文では、著者が簡単にMybatisデザインの使用シーンを紹介します。
デザインモードに関する知識は、読者が自分でネット上の関連資料を調べてもいいです。おすすめは初心者コースです。 http://www.runoob.com/design-pattern/design-pattern-tutorial.html
1.工場モード
ネットではSql Session Factoryは工場モードだとよく言われていますが、工場モードとはちょっと違うような気がします。個人的には以下のパターンが似ていると思います。
Configrations.newExector()
2.単例モード
シングルモデルはみんながよく知っているデザインです。
3.建築モード
まず、次の優雅なコードを見てみます。
XMLConfigBuider.environments Element()
4.飾り器モード
JDKのInputStreamと同様に、デコレーションモードにより入力ストリームの機能を動的に増加させることができます。
org.apache.ibatis.cache.acheには多くの実現類があります。org.apacthe.ibatis.cache.decorators.Transation cacheを見てみます。
5.テンプレートモード
テンプレートモードは、より広範なモデルを使用する必要があります。私たちは親のクラスでアルゴリズムの骨格を定義し、サブクラスによって具体的に実現します。
Mybatisのorg.apache.ibatis.extor.BaseExectorは標準的なテンプレートパターンです。
6.ダイナミックエージェントモード
動的エージェントは、Mybatisの中で最も重要な設計モデルといえる。動的エージェントを使用して優雅に多くの機能を実現している。
次のコードを分析します。
IUserはインターフェースだけで、一連の方法を定義しています。それ自体は実現されていません。どうやって呼び出すべきですか?私たちは引き続きMapperProxy類を見ます。
主にここで一括処理を行いたいです。Mapperインターフェースに関する操作は全部MapperProxyで処理します。MapperProxyも結局はsql Sessionで処理します。
参考: http://xpenxpen.iteye.com/blog/1508749
前のいくつかの文章はソースの角度からMybatisフレームワークを分析しました。もちろん、優れたフレームワークとして、デザインモデルの使用も不可欠です。
この論文では、著者が簡単にMybatisデザインの使用シーンを紹介します。
デザインモードに関する知識は、読者が自分でネット上の関連資料を調べてもいいです。おすすめは初心者コースです。 http://www.runoob.com/design-pattern/design-pattern-tutorial.html
1.工場モード
ネットではSql Session Factoryは工場モードだとよく言われていますが、工場モードとはちょっと違うような気がします。個人的には以下のパターンが似ていると思います。
Configrations.newExector()
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;
}
まとめ:ユーザーが入力したexectorTypeに基づいて、具体的なExectorを生成し、工場モードに適合する。2.単例モード
シングルモデルはみんながよく知っているデザインです。
public final class LogFactory {
...
private LogFactory() {
// disable construction
}
public static Log getLog(Class> aClass) {
return getLog(aClass.getName());
}
まとめ:古典的な一例モードとはちょっと違っています。LogFactoryは自分自身を獲得する方式を実現していません。ただ一つのツールとして使っています。3.建築モード
まず、次の優雅なコードを見てみます。
XMLConfigBuider.environments Element()
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)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//
// Builder Environment static class ,
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// environmentBuilder.build() Environment
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
Evironmentはどのように構築されているかを見てみましょう。public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
...
public static class Builder {
private String id;
private TransactionFactory transactionFactory;
private DataSource dataSource;
public Builder(String id) {
this.id = id;
}
public Builder transactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
return this;
}
public Builder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public Environment build() {
return new Environment(this.id, this.transactionFactory, this.dataSource);
}
}
...
まとめ:実際のコードの中でこのようにオブジェクトを作ることができます。コードはとても優雅に見えます。4.飾り器モード
JDKのInputStreamと同様に、デコレーションモードにより入力ストリームの機能を動的に増加させることができます。
org.apache.ibatis.cache.acheには多くの実現類があります。org.apacthe.ibatis.cache.decorators.Transation cacheを見てみます。
public class TransactionalCache implements Cache {
private Cache delegate;//
private boolean clearOnCommit;
private Map
まとめ:delegate属性に対する代理を通じて、delegateの機能を実現する前に、自分の需要を増加します。5.テンプレートモード
テンプレートモードは、より広範なモデルを使用する必要があります。私たちは親のクラスでアルゴリズムの骨格を定義し、サブクラスによって具体的に実現します。
Mybatisのorg.apache.ibatis.extor.BaseExectorは標準的なテンプレートパターンです。
public abstract class BaseExecutor implements Executor {
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) throw new ExecutorException("Executor was closed.");
clearLocalCache();
return doUpdate(ms, parameter);
}
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
...
例えば、udate()方法では、udateの骨格方法を定義していますが、本当にdoUpdate()を実行するには、サブクラス(SimpleExector)によって実現されます。// SimpleExecutor.doUpdate
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
まとめ:様々な実現方法を書く時、このパターンを参考にします。6.ダイナミックエージェントモード
動的エージェントは、Mybatisの中で最も重要な設計モデルといえる。動的エージェントを使用して優雅に多くの機能を実現している。
次のコードを分析します。
IUser mapper = session.getMapper(IUser.class);
User user = mapper.getUser(3);
これは私たちがよく使う方法です。ソースコードを追跡してみます。どうやって実現したのかを見てみます。どうやってMapperインターフェースを取得しますか?// DefaultSqlSession.getMapper()
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
// Configuration.getMapper()
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry.getMapper()
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
//
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
// mapperProxyFactory.newInstance()
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
私たちが最終的に入手したのは、MapperInterface(つまり、この例のIUserインターフェース)の代理種であるMapperProxyです。なぜこのようなMapperProxyを手に入れたのですか?IUserはインターフェースだけで、一連の方法を定義しています。それ自体は実現されていません。どうやって呼び出すべきですか?私たちは引き続きMapperProxy類を見ます。
public class MapperProxy implements InvocationHandler, Serializable {
...
// MapperProxy invoke ,
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
//
return mapperMethod.execute(sqlSession, args);
}
// mapperMethod.execute()
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
// sqlSession
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
なぜMapperProxyを使うのですか?主にここで一括処理を行いたいです。Mapperインターフェースに関する操作は全部MapperProxyで処理します。MapperProxyも結局はsql Sessionで処理します。
参考: http://xpenxpen.iteye.com/blog/1508749