[emergency shelter] mybatis can use XML and annotation at the same time
Put the conclusion first
MyBatis can be configured using both XML and annotations.
How do you use it?
Suppose there is one in the project Mapper:com.inaction.webmybatisinaction.UserMapper And his XML configuration file are placed in the resource directory: UserMapper.xml
Mode 1Write only the resource path (or URL path) of XML
<mappers> <mapper resource="UserMapper.xml"/> </mappers>Mode 2:
Only annotate Mapper's class full pathname (this method is only suitable for annotation only configurations)
<mappers> <mapper/> </mappers>Mode 3:
It is indicated at the same time, but the class full pathname must be written in front of xml
<mappers> <mapper/> <mapper resource="UserMapper.xml"/> </mappers>be careful:
Although XML and annotation can be used for configuration at the same time, you cannot annotate and configure the same method at the same time, or an error will be reported.
Cause analysis
Reason for the establishment of mode 1In the process of creating SqlSessionFactory, the Configuration object will be created first, and it will be resolved first SqlMapConfig.xml The last node to be parsed is the node, in which the parse() method of XMLMapperBuilder will be called for parsing. When the XML mode node is parsed, a namespace binding operation will be performed after parsing the XML file Configuration into Configuration: bindMapperForNamespace();
//XMLMapperBuilder class of java 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)) { //If Mapper has been registered before, there will be no duplicate registration or error // 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); } } } }
This operation first determines whether the Configuration has parsed the Mapper object in advance. If it has parsed the Mapper object in advance, it will exit without processing. If it has not parsed it, it will reflect to the corresponding Mapper class through the namespace configured in the XML file, and then parse the annotation through a series of anti reflection operations. Therefore, only the path of XML file can be parsed to Mapper annotation.
Limitations of mode 2In this way, Mapper will be registered directly with MapperRegistry of Configuration, but the Mapper object will not parse the Configuration in XML because it does not know the location of XML. So this way is not safe.
//This method is located in the MapperRegistry class. It can only parse Mapper annotations, but not 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); } } } }Reasons for the establishment of mode 3
Method 3: the Configuration of the class must be written before the Configuration of xml. It is said that after the mapper is parsed, the xml can be parsed continuously. If it is judged that the mapper is parsed, it will not be parsed repeatedly or thrown wrongly. However, if the xml is parsed first, the mapper will be registered in the Configuration. If the mapper is parsed later, an exception will be thrown and the program creation will be terminated Build SqlSessionFactory.
//This code refers to the second point of analysis. Here, hasMapper(type) will be called to check whether Mapper has been registered. If it has been parsed, it will be thrown in error. public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); }be careful
Although XML and annotation can be used for configuration at the same time, you cannot annotate and configure the same method at the same time, or an error will be reported. Because when parsing each sqlmap, a unique ID will be generated and stored in MapperRegistry. This registry is essentially a HashMap, and it is not allowed to insert the existing key value. When inserting, if an ID with the same name is detected, an error will be reported to terminate the analysis. Therefore, it is not allowed to annotate and configure a method with XML.
//This is a method in the Configuration class, which is used to register statements public void addMappedStatement(MappedStatement ms) { mappedStatements.put(ms.getId(), ms); }
//This is the implementation method of StictMap, the internal implementation class of mappedStatements. The first step is to check the repetition and report the error @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); }