一次在做一个项目的过程中,发现mybatis加载xml文件错误,导致了后面所有的sql都报了这个sql语句的错误,一时没有摸到头脑,在此分析下。
在mybatis加载xml文件的时候,会解析所有的文件,同时把statement错误的文件放到一个集合中去,代码如下:
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);
}
}
}
configuration.addIncompleteStatement(statementParser);
}
}
}
重点看catch中的语句:
public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
incompleteStatements.add(incompleteStatement);
}
incompleteStatements是加载错误的Statements集合。而当每次执行任何sql语句的时候,都会检查incompleteStatements是否为空,如果为空的话,就一直报那个加载错误的xml文件。代码如下:
我们从MapperProxy类的invoke方法一直往下走:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
final Class<?> declaringInterface = findDeclaringInterface(proxy, method);
final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
final Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
final Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
public MapperMethod(Class<?> declaringInterface, Method method, SqlSession sqlSession) {
paramNames = new ArrayList<String>();
paramPositions = new ArrayList<Integer>();
this.sqlSession = sqlSession;
this.method = method;
this.config = sqlSession.getConfiguration();
this.hasNamedParameters = false;
this.declaringInterface = declaringInterface;
this.objectFactory = config.getObjectFactory();
setupFields();//设置字段
setupMethodSignature();//设置方法验证
setupCommandType();//命令类型:select,update,delete,insert
validateStatement();//验证statement
}
setupCommandType();//命令类型:select,update,delete,insert
validateStatement();//验证statement
}
private void setupCommandType() {
MappedStatement ms = config.getMappedStatement(commandName);
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + commandName);
}
}
MappedStatement ms = config.getMappedStatement(commandName);
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + commandName);
}
}
在获取命令类型前,先获取到MappedStatement,同时在获取MappedStatement前会有一个验证,是否有未完成的Statement:
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.get(id);
}
buildAllStatements();
}
return mappedStatements.get(id);
}
protected void buildAllStatements() {
if (!incompleteResultMaps.isEmpty()) {
synchronized (incompleteResultMaps) {
// This always throws a BuilderException.
incompleteResultMaps.iterator().next().resolve();
}
}
if (!incompleteCacheRefs.isEmpty()) {
synchronized (incompleteCacheRefs) {
// This always throws a BuilderException.
incompleteCacheRefs.iterator().next().resolveCacheRef();
}
}
if (!incompleteStatements.isEmpty()) {
synchronized (incompleteStatements) {
// This always throws a BuilderException.
incompleteStatements.iterator().next().parseStatementNode();
}
}
}
if (!incompleteStatements.isEmpty()) {
synchronized (incompleteStatements) {
// This always throws a BuilderException.
incompleteStatements.iterator().next().parseStatementNode();
}
}
}
如果有未完成是MappedStatement的话就会,继续完成转化MappedStatement,如果转化出错,则会直接抛出异常。