1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.builder.xml;
17
18 import java.util.List;
19 import java.util.Locale;
20
21 import org.apache.ibatis.builder.BaseBuilder;
22 import org.apache.ibatis.builder.MapperBuilderAssistant;
23 import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
24 import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
25 import org.apache.ibatis.executor.keygen.KeyGenerator;
26 import org.apache.ibatis.executor.keygen.NoKeyGenerator;
27 import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
28 import org.apache.ibatis.mapping.MappedStatement;
29 import org.apache.ibatis.mapping.ResultSetType;
30 import org.apache.ibatis.mapping.SqlCommandType;
31 import org.apache.ibatis.mapping.SqlSource;
32 import org.apache.ibatis.mapping.StatementType;
33 import org.apache.ibatis.parsing.XNode;
34 import org.apache.ibatis.scripting.LanguageDriver;
35 import org.apache.ibatis.session.Configuration;
36
37
38
39
40 public class XMLStatementBuilder extends BaseBuilder {
41
42 private final MapperBuilderAssistant builderAssistant;
43 private final XNode context;
44 private final String requiredDatabaseId;
45
46 public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context) {
47 this(configuration, builderAssistant, context, null);
48 }
49
50 public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context,
51 String databaseId) {
52 super(configuration);
53 this.builderAssistant = builderAssistant;
54 this.context = context;
55 this.requiredDatabaseId = databaseId;
56 }
57
58 public void parseStatementNode() {
59 String id = context.getStringAttribute("id");
60 String databaseId = context.getStringAttribute("databaseId");
61
62 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
63 return;
64 }
65
66 String nodeName = context.getNode().getNodeName();
67 SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
68 boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
69 boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
70 boolean useCache = context.getBooleanAttribute("useCache", isSelect);
71 boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
72
73
74 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
75 includeParser.applyIncludes(context.getNode());
76
77 String parameterType = context.getStringAttribute("parameterType");
78 Class<?> parameterTypeClass = resolveClass(parameterType);
79
80 String lang = context.getStringAttribute("lang");
81 LanguageDriver langDriver = getLanguageDriver(lang);
82
83
84 processSelectKeyNodes(id, parameterTypeClass, langDriver);
85
86
87 KeyGenerator keyGenerator;
88 String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
89 keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
90 if (configuration.hasKeyGenerator(keyStatementId)) {
91 keyGenerator = configuration.getKeyGenerator(keyStatementId);
92 } else {
93 keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
94 configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
95 ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
96 }
97
98 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
99 StatementType statementType = StatementType
100 .valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
101 Integer fetchSize = context.getIntAttribute("fetchSize");
102 Integer timeout = context.getIntAttribute("timeout");
103 String parameterMap = context.getStringAttribute("parameterMap");
104 String resultType = context.getStringAttribute("resultType");
105 Class<?> resultTypeClass = resolveClass(resultType);
106 String resultMap = context.getStringAttribute("resultMap");
107 if (resultTypeClass == null && resultMap == null) {
108 resultTypeClass = MapperAnnotationBuilder.getMethodReturnType(builderAssistant.getCurrentNamespace(), id);
109 }
110 String resultSetType = context.getStringAttribute("resultSetType");
111 ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
112 if (resultSetTypeEnum == null) {
113 resultSetTypeEnum = configuration.getDefaultResultSetType();
114 }
115 String keyProperty = context.getStringAttribute("keyProperty");
116 String keyColumn = context.getStringAttribute("keyColumn");
117 String resultSets = context.getStringAttribute("resultSets");
118 boolean dirtySelect = context.getBooleanAttribute("affectData", Boolean.FALSE);
119
120 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
121 parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
122 keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect);
123 }
124
125 private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
126 List<XNode> selectKeyNodes = context.evalNodes("selectKey");
127 if (configuration.getDatabaseId() != null) {
128 parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
129 }
130 parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
131 removeSelectKeyNodes(selectKeyNodes);
132 }
133
134 private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass,
135 LanguageDriver langDriver, String skRequiredDatabaseId) {
136 for (XNode nodeToHandle : list) {
137 String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
138 String databaseId = nodeToHandle.getStringAttribute("databaseId");
139 if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
140 parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
141 }
142 }
143 }
144
145 private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver,
146 String databaseId) {
147 String resultType = nodeToHandle.getStringAttribute("resultType");
148 Class<?> resultTypeClass = resolveClass(resultType);
149 StatementType statementType = StatementType
150 .valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
151 String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
152 String keyColumn = nodeToHandle.getStringAttribute("keyColumn");
153 boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));
154
155
156 boolean useCache = false;
157 boolean resultOrdered = false;
158 KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
159 Integer fetchSize = null;
160 Integer timeout = null;
161 boolean flushCache = false;
162 String parameterMap = null;
163 String resultMap = null;
164 ResultSetType resultSetTypeEnum = null;
165
166 SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
167 SqlCommandType sqlCommandType = SqlCommandType.SELECT;
168
169 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
170 parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
171 keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null, false);
172
173 id = builderAssistant.applyCurrentNamespace(id, false);
174
175 MappedStatement keyStatement = configuration.getMappedStatement(id, false);
176 configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
177 }
178
179 private void removeSelectKeyNodes(List<XNode> selectKeyNodes) {
180 for (XNode nodeToHandle : selectKeyNodes) {
181 nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode());
182 }
183 }
184
185 private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
186 if (requiredDatabaseId != null) {
187 return requiredDatabaseId.equals(databaseId);
188 }
189 if (databaseId != null) {
190 return false;
191 }
192 id = builderAssistant.applyCurrentNamespace(id, false);
193 if (!this.configuration.hasStatement(id, false)) {
194 return true;
195 }
196
197 MappedStatement previous = this.configuration.getMappedStatement(id, false);
198 return previous.getDatabaseId() == null;
199 }
200
201 private LanguageDriver getLanguageDriver(String lang) {
202 Class<? extends LanguageDriver> langClass = null;
203 if (lang != null) {
204 langClass = resolveClass(lang);
205 }
206 return configuration.getLanguageDriver(langClass);
207 }
208
209 }