|
@@ -49,8 +49,9 @@ import org.apache.ibatis.session.Configuration;
|
|
|
import org.apache.ibatis.type.JdbcType;
|
|
|
import org.apache.ibatis.type.TypeHandler;
|
|
|
|
|
|
-import com.baomidou.mybatisplus.mapper.AutoSqlInjector;
|
|
|
+import com.baomidou.mybatisplus.entity.GlobalConfiguration;
|
|
|
import com.baomidou.mybatisplus.mapper.BaseMapper;
|
|
|
+import com.baomidou.mybatisplus.toolkit.StringUtils;
|
|
|
|
|
|
/**
|
|
|
* <p>
|
|
@@ -65,367 +66,384 @@ import com.baomidou.mybatisplus.mapper.BaseMapper;
|
|
|
*/
|
|
|
public class MybatisXMLMapperBuilder extends BaseBuilder {
|
|
|
|
|
|
- private XPathParser parser;
|
|
|
- private MapperBuilderAssistant builderAssistant;
|
|
|
- private Map<String, XNode> sqlFragments;
|
|
|
- private String resource;
|
|
|
-
|
|
|
- @Deprecated
|
|
|
- public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace) {
|
|
|
- this(reader, configuration, resource, sqlFragments);
|
|
|
- this.builderAssistant.setCurrentNamespace(namespace);
|
|
|
- }
|
|
|
-
|
|
|
- @Deprecated
|
|
|
- public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
|
|
|
- this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),
|
|
|
- configuration, resource, sqlFragments);
|
|
|
- }
|
|
|
-
|
|
|
- public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace) {
|
|
|
- this(inputStream, configuration, resource, sqlFragments);
|
|
|
- this.builderAssistant.setCurrentNamespace(namespace);
|
|
|
- }
|
|
|
-
|
|
|
- public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
|
|
|
- this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
|
|
|
- configuration, resource, sqlFragments);
|
|
|
- }
|
|
|
-
|
|
|
- private MybatisXMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
|
|
|
- super(configuration);
|
|
|
- this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
|
|
|
- this.parser = parser;
|
|
|
- this.sqlFragments = sqlFragments;
|
|
|
- this.resource = resource;
|
|
|
- }
|
|
|
-
|
|
|
- public void parse() {
|
|
|
- if (!configuration.isResourceLoaded(resource)) {
|
|
|
- configurationElement(parser.evalNode("/mapper"));
|
|
|
- configuration.addLoadedResource(resource);
|
|
|
- bindMapperForNamespace();
|
|
|
- }
|
|
|
-
|
|
|
- parsePendingResultMaps();
|
|
|
- parsePendingChacheRefs();
|
|
|
- parsePendingStatements();
|
|
|
- }
|
|
|
-
|
|
|
- public XNode getSqlFragment(String refid) {
|
|
|
- return sqlFragments.get(refid);
|
|
|
- }
|
|
|
-
|
|
|
- private void configurationElement(XNode context) {
|
|
|
- try {
|
|
|
- String namespace = context.getStringAttribute("namespace");
|
|
|
- if (namespace == null || namespace.equals("")) {
|
|
|
- throw new BuilderException("Mapper's namespace cannot be empty");
|
|
|
- }
|
|
|
- builderAssistant.setCurrentNamespace(namespace);
|
|
|
- cacheRefElement(context.evalNode("cache-ref"));
|
|
|
- cacheElement(context.evalNode("cache"));
|
|
|
- parameterMapElement(context.evalNodes("/mapper/parameterMap"));
|
|
|
- resultMapElements(context.evalNodes("/mapper/resultMap"));
|
|
|
- sqlElement(context.evalNodes("/mapper/sql"));
|
|
|
- buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
|
|
|
- } catch (Exception e) {
|
|
|
- throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void buildStatementFromContext(List<XNode> list) {
|
|
|
- if (configuration.getDatabaseId() != null) {
|
|
|
- buildStatementFromContext(list, configuration.getDatabaseId());
|
|
|
- }
|
|
|
- buildStatementFromContext(list, null);
|
|
|
- }
|
|
|
-
|
|
|
- private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
|
|
|
- for (XNode context : list) {
|
|
|
- final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
|
|
|
- try {
|
|
|
- statementParser.parseStatementNode();
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- configuration.addIncompleteStatement(statementParser);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void parsePendingResultMaps() {
|
|
|
- Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
|
|
|
- synchronized (incompleteResultMaps) {
|
|
|
- Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
|
|
|
- while (iter.hasNext()) {
|
|
|
- try {
|
|
|
- iter.next().resolve();
|
|
|
- iter.remove();
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- // ResultMap is still missing a resource...
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void parsePendingChacheRefs() {
|
|
|
- Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
|
|
|
- synchronized (incompleteCacheRefs) {
|
|
|
- Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
|
|
|
- while (iter.hasNext()) {
|
|
|
- try {
|
|
|
- iter.next().resolveCacheRef();
|
|
|
- iter.remove();
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- // Cache ref is still missing a resource...
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void parsePendingStatements() {
|
|
|
- Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
|
|
|
- synchronized (incompleteStatements) {
|
|
|
- Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
|
|
|
- while (iter.hasNext()) {
|
|
|
- try {
|
|
|
- iter.next().parseStatementNode();
|
|
|
- iter.remove();
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- // Statement is still missing a resource...
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void cacheRefElement(XNode context) {
|
|
|
- if (context != null) {
|
|
|
- configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
|
|
|
- CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
|
|
|
- try {
|
|
|
- cacheRefResolver.resolveCacheRef();
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- configuration.addIncompleteCacheRef(cacheRefResolver);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void cacheElement(XNode context) throws Exception {
|
|
|
- if (context != null) {
|
|
|
- String type = context.getStringAttribute("type", "PERPETUAL");
|
|
|
- Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
|
|
|
- String eviction = context.getStringAttribute("eviction", "LRU");
|
|
|
- Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
|
|
|
- Long flushInterval = context.getLongAttribute("flushInterval");
|
|
|
- Integer size = context.getIntAttribute("size");
|
|
|
- boolean readWrite = !context.getBooleanAttribute("readOnly", false);
|
|
|
- boolean blocking = context.getBooleanAttribute("blocking", false);
|
|
|
- Properties props = context.getChildrenAsProperties();
|
|
|
- builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void parameterMapElement(List<XNode> list) throws Exception {
|
|
|
- for (XNode parameterMapNode : list) {
|
|
|
- String id = parameterMapNode.getStringAttribute("id");
|
|
|
- String type = parameterMapNode.getStringAttribute("type");
|
|
|
- Class<?> parameterClass = resolveClass(type);
|
|
|
- List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
|
|
|
- List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
|
|
|
- for (XNode parameterNode : parameterNodes) {
|
|
|
- String property = parameterNode.getStringAttribute("property");
|
|
|
- String javaType = parameterNode.getStringAttribute("javaType");
|
|
|
- String jdbcType = parameterNode.getStringAttribute("jdbcType");
|
|
|
- String resultMap = parameterNode.getStringAttribute("resultMap");
|
|
|
- String mode = parameterNode.getStringAttribute("mode");
|
|
|
- String typeHandler = parameterNode.getStringAttribute("typeHandler");
|
|
|
- Integer numericScale = parameterNode.getIntAttribute("numericScale");
|
|
|
- ParameterMode modeEnum = resolveParameterMode(mode);
|
|
|
- Class<?> javaTypeClass = resolveClass(javaType);
|
|
|
- JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
|
|
|
- ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
|
|
|
- parameterMappings.add(parameterMapping);
|
|
|
- }
|
|
|
- builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void resultMapElements(List<XNode> list) throws Exception {
|
|
|
- for (XNode resultMapNode : list) {
|
|
|
- try {
|
|
|
- resultMapElement(resultMapNode);
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- // ignore, it will be retried
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
|
|
|
- return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
|
|
|
- }
|
|
|
-
|
|
|
- private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
|
|
|
- ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
|
|
|
- String id = resultMapNode.getStringAttribute("id",
|
|
|
- resultMapNode.getValueBasedIdentifier());
|
|
|
- String type = resultMapNode.getStringAttribute("type",
|
|
|
- resultMapNode.getStringAttribute("ofType",
|
|
|
- resultMapNode.getStringAttribute("resultType",
|
|
|
- resultMapNode.getStringAttribute("javaType"))));
|
|
|
- String extend = resultMapNode.getStringAttribute("extends");
|
|
|
- Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
|
|
|
- Class<?> typeClass = resolveClass(type);
|
|
|
- Discriminator discriminator = null;
|
|
|
- List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
|
|
|
- resultMappings.addAll(additionalResultMappings);
|
|
|
- List<XNode> resultChildren = resultMapNode.getChildren();
|
|
|
- for (XNode resultChild : resultChildren) {
|
|
|
- if ("constructor".equals(resultChild.getName())) {
|
|
|
- processConstructorElement(resultChild, typeClass, resultMappings);
|
|
|
- } else if ("discriminator".equals(resultChild.getName())) {
|
|
|
- discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
|
|
|
- } else {
|
|
|
- List<ResultFlag> flags = new ArrayList<ResultFlag>();
|
|
|
- if ("id".equals(resultChild.getName())) {
|
|
|
- flags.add(ResultFlag.ID);
|
|
|
- }
|
|
|
- resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
|
|
|
- }
|
|
|
- }
|
|
|
- ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
|
|
|
- try {
|
|
|
- return resultMapResolver.resolve();
|
|
|
- } catch (IncompleteElementException e) {
|
|
|
- configuration.addIncompleteResultMap(resultMapResolver);
|
|
|
- throw e;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
|
|
|
- List<XNode> argChildren = resultChild.getChildren();
|
|
|
- for (XNode argChild : argChildren) {
|
|
|
- List<ResultFlag> flags = new ArrayList<ResultFlag>();
|
|
|
- flags.add(ResultFlag.CONSTRUCTOR);
|
|
|
- if ("idArg".equals(argChild.getName())) {
|
|
|
- flags.add(ResultFlag.ID);
|
|
|
- }
|
|
|
- resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
|
|
|
- String column = context.getStringAttribute("column");
|
|
|
- String javaType = context.getStringAttribute("javaType");
|
|
|
- String jdbcType = context.getStringAttribute("jdbcType");
|
|
|
- String typeHandler = context.getStringAttribute("typeHandler");
|
|
|
- Class<?> javaTypeClass = resolveClass(javaType);
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
|
|
|
- JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
|
|
|
- Map<String, String> discriminatorMap = new HashMap<String, String>();
|
|
|
- for (XNode caseChild : context.getChildren()) {
|
|
|
- String value = caseChild.getStringAttribute("value");
|
|
|
- String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings));
|
|
|
- discriminatorMap.put(value, resultMap);
|
|
|
- }
|
|
|
- return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
|
|
|
- }
|
|
|
-
|
|
|
- private void sqlElement(List<XNode> list) throws Exception {
|
|
|
- if (configuration.getDatabaseId() != null) {
|
|
|
- sqlElement(list, configuration.getDatabaseId());
|
|
|
- }
|
|
|
- sqlElement(list, null);
|
|
|
- }
|
|
|
-
|
|
|
- private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
|
|
|
- for (XNode context : list) {
|
|
|
- String databaseId = context.getStringAttribute("databaseId");
|
|
|
- String id = context.getStringAttribute("id");
|
|
|
- id = builderAssistant.applyCurrentNamespace(id, false);
|
|
|
- if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
|
|
|
- sqlFragments.put(id, context);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
|
|
|
- if (requiredDatabaseId != null) {
|
|
|
- if (!requiredDatabaseId.equals(databaseId)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (databaseId != null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- // skip this fragment if there is a previous one with a not null databaseId
|
|
|
- if (this.sqlFragments.containsKey(id)) {
|
|
|
- XNode context = this.sqlFragments.get(id);
|
|
|
- if (context.getStringAttribute("databaseId") != null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
|
|
|
- String property = context.getStringAttribute("property");
|
|
|
- String column = context.getStringAttribute("column");
|
|
|
- String javaType = context.getStringAttribute("javaType");
|
|
|
- String jdbcType = context.getStringAttribute("jdbcType");
|
|
|
- String nestedSelect = context.getStringAttribute("select");
|
|
|
- String nestedResultMap = context.getStringAttribute("resultMap",
|
|
|
- processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
|
|
|
- String notNullColumn = context.getStringAttribute("notNullColumn");
|
|
|
- String columnPrefix = context.getStringAttribute("columnPrefix");
|
|
|
- String typeHandler = context.getStringAttribute("typeHandler");
|
|
|
- String resultSet = context.getStringAttribute("resultSet");
|
|
|
- String foreignColumn = context.getStringAttribute("foreignColumn");
|
|
|
- boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
|
|
|
- Class<?> javaTypeClass = resolveClass(javaType);
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
|
|
|
- JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
|
|
|
- return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
|
|
|
- }
|
|
|
-
|
|
|
- private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings) throws Exception {
|
|
|
- if ("association".equals(context.getName())
|
|
|
- || "collection".equals(context.getName())
|
|
|
- || "case".equals(context.getName())) {
|
|
|
- if (context.getStringAttribute("select") == null) {
|
|
|
- ResultMap resultMap = resultMapElement(context, resultMappings);
|
|
|
- return resultMap.getId();
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- 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)) {
|
|
|
- // 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);
|
|
|
- }
|
|
|
- //TODO 注入 CURD 动态 SQL
|
|
|
- if (BaseMapper.class.isAssignableFrom(boundType)) {
|
|
|
- new AutoSqlInjector(configuration, MybatisConfiguration.DB_TYPE).inject(builderAssistant, boundType);
|
|
|
+ private XPathParser parser;
|
|
|
+ private MapperBuilderAssistant builderAssistant;
|
|
|
+ private Map<String, XNode> sqlFragments;
|
|
|
+ private String resource;
|
|
|
+
|
|
|
+ @Deprecated
|
|
|
+ public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource,
|
|
|
+ Map<String, XNode> sqlFragments, String namespace) {
|
|
|
+ this(reader, configuration, resource, sqlFragments);
|
|
|
+ this.builderAssistant.setCurrentNamespace(namespace);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Deprecated
|
|
|
+ public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource,
|
|
|
+ Map<String, XNode> sqlFragments) {
|
|
|
+ this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration,
|
|
|
+ resource, sqlFragments);
|
|
|
+ }
|
|
|
+
|
|
|
+ public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource,
|
|
|
+ Map<String, XNode> sqlFragments, String namespace) {
|
|
|
+ this(inputStream, configuration, resource, sqlFragments);
|
|
|
+ this.builderAssistant.setCurrentNamespace(namespace);
|
|
|
+ }
|
|
|
+
|
|
|
+ public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource,
|
|
|
+ Map<String, XNode> sqlFragments) {
|
|
|
+ this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
|
|
|
+ configuration, resource, sqlFragments);
|
|
|
+ }
|
|
|
+
|
|
|
+ private MybatisXMLMapperBuilder(XPathParser parser, Configuration configuration, String resource,
|
|
|
+ Map<String, XNode> sqlFragments) {
|
|
|
+ super(configuration);
|
|
|
+ this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
|
|
|
+ this.parser = parser;
|
|
|
+ this.sqlFragments = sqlFragments;
|
|
|
+ this.resource = resource;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void parse() {
|
|
|
+ if (!configuration.isResourceLoaded(resource)) {
|
|
|
+ configurationElement(parser.evalNode("/mapper"));
|
|
|
+ configuration.addLoadedResource(resource);
|
|
|
+ bindMapperForNamespace();
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ parsePendingResultMaps();
|
|
|
+ parsePendingChacheRefs();
|
|
|
+ parsePendingStatements();
|
|
|
+ }
|
|
|
+
|
|
|
+ public XNode getSqlFragment(String refid) {
|
|
|
+ return sqlFragments.get(refid);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void configurationElement(XNode context) {
|
|
|
+ try {
|
|
|
+ String namespace = context.getStringAttribute("namespace");
|
|
|
+ if (StringUtils.isEmpty(namespace)) {
|
|
|
+ throw new BuilderException("Mapper's namespace cannot be empty");
|
|
|
+ }
|
|
|
+ builderAssistant.setCurrentNamespace(namespace);
|
|
|
+ cacheRefElement(context.evalNode("cache-ref"));
|
|
|
+ cacheElement(context.evalNode("cache"));
|
|
|
+ parameterMapElement(context.evalNodes("/mapper/parameterMap"));
|
|
|
+ resultMapElements(context.evalNodes("/mapper/resultMap"));
|
|
|
+ sqlElement(context.evalNodes("/mapper/sql"));
|
|
|
+ buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void buildStatementFromContext(List<XNode> list) {
|
|
|
+ if (configuration.getDatabaseId() != null) {
|
|
|
+ buildStatementFromContext(list, configuration.getDatabaseId());
|
|
|
+ }
|
|
|
+ buildStatementFromContext(list, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
|
|
|
+ for (XNode context : list) {
|
|
|
+ final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant,
|
|
|
+ context, requiredDatabaseId);
|
|
|
+ try {
|
|
|
+ statementParser.parseStatementNode();
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ configuration.addIncompleteStatement(statementParser);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parsePendingResultMaps() {
|
|
|
+ Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
|
|
|
+ synchronized (incompleteResultMaps) {
|
|
|
+ Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ try {
|
|
|
+ iter.next().resolve();
|
|
|
+ iter.remove();
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ // ResultMap is still missing a resource...
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parsePendingChacheRefs() {
|
|
|
+ Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
|
|
|
+ synchronized (incompleteCacheRefs) {
|
|
|
+ Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ try {
|
|
|
+ iter.next().resolveCacheRef();
|
|
|
+ iter.remove();
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ // Cache ref is still missing a resource...
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parsePendingStatements() {
|
|
|
+ Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
|
|
|
+ synchronized (incompleteStatements) {
|
|
|
+ Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ try {
|
|
|
+ iter.next().parseStatementNode();
|
|
|
+ iter.remove();
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ // Statement is still missing a resource...
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void cacheRefElement(XNode context) {
|
|
|
+ if (context != null) {
|
|
|
+ configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
|
|
|
+ CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant,
|
|
|
+ context.getStringAttribute("namespace"));
|
|
|
+ try {
|
|
|
+ cacheRefResolver.resolveCacheRef();
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ configuration.addIncompleteCacheRef(cacheRefResolver);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void cacheElement(XNode context) throws Exception {
|
|
|
+ if (context != null) {
|
|
|
+ String type = context.getStringAttribute("type", "PERPETUAL");
|
|
|
+ Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
|
|
|
+ String eviction = context.getStringAttribute("eviction", "LRU");
|
|
|
+ Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
|
|
|
+ Long flushInterval = context.getLongAttribute("flushInterval");
|
|
|
+ Integer size = context.getIntAttribute("size");
|
|
|
+ boolean readWrite = !context.getBooleanAttribute("readOnly", false);
|
|
|
+ boolean blocking = context.getBooleanAttribute("blocking", false);
|
|
|
+ Properties props = context.getChildrenAsProperties();
|
|
|
+ builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parameterMapElement(List<XNode> list) throws Exception {
|
|
|
+ for (XNode parameterMapNode : list) {
|
|
|
+ String id = parameterMapNode.getStringAttribute("id");
|
|
|
+ String type = parameterMapNode.getStringAttribute("type");
|
|
|
+ Class<?> parameterClass = resolveClass(type);
|
|
|
+ List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
|
|
|
+ List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
|
|
|
+ for (XNode parameterNode : parameterNodes) {
|
|
|
+ String property = parameterNode.getStringAttribute("property");
|
|
|
+ String javaType = parameterNode.getStringAttribute("javaType");
|
|
|
+ String jdbcType = parameterNode.getStringAttribute("jdbcType");
|
|
|
+ String resultMap = parameterNode.getStringAttribute("resultMap");
|
|
|
+ String mode = parameterNode.getStringAttribute("mode");
|
|
|
+ String typeHandler = parameterNode.getStringAttribute("typeHandler");
|
|
|
+ Integer numericScale = parameterNode.getIntAttribute("numericScale");
|
|
|
+ ParameterMode modeEnum = resolveParameterMode(mode);
|
|
|
+ Class<?> javaTypeClass = resolveClass(javaType);
|
|
|
+ JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(
|
|
|
+ typeHandler);
|
|
|
+ ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property,
|
|
|
+ javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
|
|
|
+ parameterMappings.add(parameterMapping);
|
|
|
+ }
|
|
|
+ builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void resultMapElements(List<XNode> list) throws Exception {
|
|
|
+ for (XNode resultMapNode : list) {
|
|
|
+ try {
|
|
|
+ resultMapElement(resultMapNode);
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ // ignore, it will be retried
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
|
|
|
+ return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
|
|
|
+ }
|
|
|
+
|
|
|
+ private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings)
|
|
|
+ throws Exception {
|
|
|
+ ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
|
|
|
+ String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
|
|
|
+ String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType",
|
|
|
+ resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType"))));
|
|
|
+ String extend = resultMapNode.getStringAttribute("extends");
|
|
|
+ Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
|
|
|
+ Class<?> typeClass = resolveClass(type);
|
|
|
+ Discriminator discriminator = null;
|
|
|
+ List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
|
|
|
+ resultMappings.addAll(additionalResultMappings);
|
|
|
+ List<XNode> resultChildren = resultMapNode.getChildren();
|
|
|
+ for (XNode resultChild : resultChildren) {
|
|
|
+ if ("constructor".equals(resultChild.getName())) {
|
|
|
+ processConstructorElement(resultChild, typeClass, resultMappings);
|
|
|
+ } else if ("discriminator".equals(resultChild.getName())) {
|
|
|
+ discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
|
|
|
+ } else {
|
|
|
+ List<ResultFlag> flags = new ArrayList<ResultFlag>();
|
|
|
+ if ("id".equals(resultChild.getName())) {
|
|
|
+ flags.add(ResultFlag.ID);
|
|
|
+ }
|
|
|
+ resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend,
|
|
|
+ discriminator, resultMappings, autoMapping);
|
|
|
+ try {
|
|
|
+ return resultMapResolver.resolve();
|
|
|
+ } catch (IncompleteElementException e) {
|
|
|
+ configuration.addIncompleteResultMap(resultMapResolver);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings)
|
|
|
+ throws Exception {
|
|
|
+ List<XNode> argChildren = resultChild.getChildren();
|
|
|
+ for (XNode argChild : argChildren) {
|
|
|
+ List<ResultFlag> flags = new ArrayList<ResultFlag>();
|
|
|
+ flags.add(ResultFlag.CONSTRUCTOR);
|
|
|
+ if ("idArg".equals(argChild.getName())) {
|
|
|
+ flags.add(ResultFlag.ID);
|
|
|
+ }
|
|
|
+ resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType,
|
|
|
+ List<ResultMapping> resultMappings) throws Exception {
|
|
|
+ String column = context.getStringAttribute("column");
|
|
|
+ String javaType = context.getStringAttribute("javaType");
|
|
|
+ String jdbcType = context.getStringAttribute("jdbcType");
|
|
|
+ String typeHandler = context.getStringAttribute("typeHandler");
|
|
|
+ Class<?> javaTypeClass = resolveClass(javaType);
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
|
|
|
+ JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
|
|
|
+ Map<String, String> discriminatorMap = new HashMap<String, String>();
|
|
|
+ for (XNode caseChild : context.getChildren()) {
|
|
|
+ String value = caseChild.getStringAttribute("value");
|
|
|
+ String resultMap = caseChild.getStringAttribute("resultMap",
|
|
|
+ processNestedResultMappings(caseChild, resultMappings));
|
|
|
+ discriminatorMap.put(value, resultMap);
|
|
|
+ }
|
|
|
+ return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass,
|
|
|
+ discriminatorMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void sqlElement(List<XNode> list) throws Exception {
|
|
|
+ if (configuration.getDatabaseId() != null) {
|
|
|
+ sqlElement(list, configuration.getDatabaseId());
|
|
|
+ }
|
|
|
+ sqlElement(list, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
|
|
|
+ for (XNode context : list) {
|
|
|
+ String databaseId = context.getStringAttribute("databaseId");
|
|
|
+ String id = context.getStringAttribute("id");
|
|
|
+ id = builderAssistant.applyCurrentNamespace(id, false);
|
|
|
+ if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
|
|
|
+ sqlFragments.put(id, context);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
|
|
|
+ if (requiredDatabaseId != null) {
|
|
|
+ if (!requiredDatabaseId.equals(databaseId)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (databaseId != null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // skip this fragment if there is a previous one with a not null
|
|
|
+ // databaseId
|
|
|
+ if (this.sqlFragments.containsKey(id)) {
|
|
|
+ XNode context = this.sqlFragments.get(id);
|
|
|
+ if (context.getStringAttribute("databaseId") != null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags)
|
|
|
+ throws Exception {
|
|
|
+ String property = context.getStringAttribute("property");
|
|
|
+ String column = context.getStringAttribute("column");
|
|
|
+ String javaType = context.getStringAttribute("javaType");
|
|
|
+ String jdbcType = context.getStringAttribute("jdbcType");
|
|
|
+ String nestedSelect = context.getStringAttribute("select");
|
|
|
+ String nestedResultMap = context.getStringAttribute("resultMap",
|
|
|
+ processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
|
|
|
+ String notNullColumn = context.getStringAttribute("notNullColumn");
|
|
|
+ String columnPrefix = context.getStringAttribute("columnPrefix");
|
|
|
+ String typeHandler = context.getStringAttribute("typeHandler");
|
|
|
+ String resultSet = context.getStringAttribute("resultSet");
|
|
|
+ String foreignColumn = context.getStringAttribute("foreignColumn");
|
|
|
+ boolean lazy = "lazy".equals(
|
|
|
+ context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
|
|
|
+ Class<?> javaTypeClass = resolveClass(javaType);
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
|
|
|
+ JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
|
|
|
+ return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum,
|
|
|
+ nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet,
|
|
|
+ foreignColumn, lazy);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings) throws Exception {
|
|
|
+ if ("association".equals(context.getName()) || "collection".equals(context.getName())
|
|
|
+ || "case".equals(context.getName())) {
|
|
|
+ if (context.getStringAttribute("select") == null) {
|
|
|
+ ResultMap resultMap = resultMapElement(context, resultMappings);
|
|
|
+ return resultMap.getId();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)) {
|
|
|
+ // 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);
|
|
|
+ }
|
|
|
+ //TODO 注入 CURD 动态 SQL
|
|
|
+ if (BaseMapper.class.isAssignableFrom(boundType)) {
|
|
|
+ GlobalConfiguration.getSqlInjector(configuration).inspectInject(builderAssistant, boundType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
}
|