【緊急避難所】mybatisはXMLと注記を併用できます
21878 ワード
【緊急避難所】mybatisはXMLと注記を併用できます 結論 の使い方 方式一 方式2: 方式3: 注意:
原因分析 方式が成立した原因 方式2の限界 方式三成立の原因 注意
結論を先に述べる.
MyBatisはXMLと注釈を併用して構成できます.
使用方法
プロジェクトにMapper:com.inaction.webmybatisinaction.UserMapperと彼のXMLプロファイルがresourceディレクトリの下に配置されていると仮定します:UserMapper.xml
方式一
XMLのresourceパス(またはURLパス)のみ明記
方式2:
注釈Mapperのクラスフルパス名のみを明記します(この方法は注釈のみを含む構成に適しています).
方式3:
同時に明記しますが、クラスのフルパス名はxmlの前に書かなければなりません.
注意:
XMLと注釈の2つの方式を同時に採用して構成することができますが、同じ方法に対して同時に注釈とXMLの構成を行うことはできません.そうしないと、エラーが発生します.
原因分析
方式が成立した原因
SqlSessionFactoryの作成中にコンフィギュレーションオブジェクトが先に作成され、SqlMapConfig.xmlのノードが先に解析され、最後に解析されるのがノードで、XML MapperBuilderのparse()メソッド解析が呼び出され、XMLのメソッドノードが解析されると、XMLファイルがコンフィギュレーションに設定されていることを解析した後、名前空間バインドの操作が行われます:bindMapperForNamespace()
この操作はまずコンフィギュレーションが事前にMapperオブジェクトを解析したかどうかを判断し,事前に解析した場合は処理をせずにそのまま終了し,解析しなかった場合はXMLファイルに配置されたネーミングスペースを介して対応するMapperクラスに反射し,一連の反射操作により注釈を解析する.したがって,XMLファイルパスのみを明記すると,依然としてMapper注釈に解析できる.
方式二の限界
この方式では、コンフィギュレーションのMapperRegistryに直接Mapperを登録しますが、MapperオブジェクトがXMLの位置を知らないため、XMLの構成は解析されません.だからこの方法は安全ではありません.
方式三が成立した原因
方式三一は必ずクラスの配置をxmlの配置の前に書いて、先にmapperを解析した後に、引き続きxmlを解析することができると言って、xmlを解析する時にmapperが解析したことを判断した後に、繰り返し解析しても間違いがないと判断して、しかし先にxmlを解析するならば、Configurationの中でMapperを登録することができて、その後、Mapperの解析中にロードが検出されると例外が放出され、SqlSessionFactoryの作成が終了します.
に注意
XMLと注釈の2つの方式を同時に採用して構成することができますが、同じ方法に対して同時に注釈とXMLの構成を行うことはできません.そうしないと、エラーが発生します.各sqlmapを解析するときに一意のIDが生成され、MapperRegistryに格納されるため、この登録センターは本質的にHashMapであり、既存のkey値を挿入することは許されず、挿入操作をするときに同名のIDが検出されるとエラーで解析が終了する.したがって、1つのメソッドの注釈とXML構成は許可されません.
結論を先に述べる.
MyBatisはXMLと注釈を併用して構成できます.
使用方法
プロジェクトにMapper:com.inaction.webmybatisinaction.UserMapperと彼のXMLプロファイルがresourceディレクトリの下に配置されていると仮定します:UserMapper.xml
方式一
XMLのresourceパス(またはURLパス)のみ明記
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
方式2:
注釈Mapperのクラスフルパス名のみを明記します(この方法は注釈のみを含む構成に適しています).
<mappers>
<mapper class="com.inaction.webmybatisinaction.UserMapper"/>
mappers>
方式3:
同時に明記しますが、クラスのフルパス名はxmlの前に書かなければなりません.
<mappers>
<mapper class="com.inaction.webmybatisinaction.UserMapper"/>
<mapper resource="UserMapper.xml"/>
mappers>
注意:
XMLと注釈の2つの方式を同時に採用して構成することができますが、同じ方法に対して同時に注釈とXMLの構成を行うことはできません.そうしないと、エラーが発生します.
原因分析
方式が成立した原因
SqlSessionFactoryの作成中にコンフィギュレーションオブジェクトが先に作成され、SqlMapConfig.xmlのノードが先に解析され、最後に解析されるのがノードで、XML MapperBuilderのparse()メソッド解析が呼び出され、XMLのメソッドノードが解析されると、XMLファイルがコンフィギュレーションに設定されていることを解析した後、名前空間バインドの操作が行われます:bindMapperForNamespace()
//java XMLMapperBuilder
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Mapper
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
この操作はまずコンフィギュレーションが事前にMapperオブジェクトを解析したかどうかを判断し,事前に解析した場合は処理をせずにそのまま終了し,解析しなかった場合はXMLファイルに配置されたネーミングスペースを介して対応するMapperクラスに反射し,一連の反射操作により注釈を解析する.したがって,XMLファイルパスのみを明記すると,依然としてMapper注釈に解析できる.
方式二の限界
この方式では、コンフィギュレーションのMapperRegistryに直接Mapperを登録しますが、MapperオブジェクトがXMLの位置を知らないため、XMLの構成は解析されません.だからこの方法は安全ではありません.
// MapperRegistry , Mapper XML
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
方式三が成立した原因
方式三一は必ずクラスの配置をxmlの配置の前に書いて、先にmapperを解析した後に、引き続きxmlを解析することができると言って、xmlを解析する時にmapperが解析したことを判断した後に、繰り返し解析しても間違いがないと判断して、しかし先にxmlを解析するならば、Configurationの中でMapperを登録することができて、その後、Mapperの解析中にロードが検出されると例外が放出され、SqlSessionFactoryの作成が終了します.
// , hasMapper(type) Mapper, 。
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
に注意
XMLと注釈の2つの方式を同時に採用して構成することができますが、同じ方法に対して同時に注釈とXMLの構成を行うことはできません.そうしないと、エラーが発生します.各sqlmapを解析するときに一意のIDが生成され、MapperRegistryに格納されるため、この登録センターは本質的にHashMapであり、既存のkey値を挿入することは許されず、挿入操作をするときに同名のIDが検出されるとエラーで解析が終了する.したがって、1つのメソッドの注釈とXML構成は許可されません.
// Configuration ,
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
// mappedStatements StictMap ,
@Override
@SuppressWarnings("unchecked")
public V put(String key, V value) {
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key
+ (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
}
if (key.contains(".")) {
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
super.put(shortKey, value);
} else {
super.put(shortKey, (V) new Ambiguity(shortKey));
}
}
return super.put(key, value);
}