Learn more about MyBatis return values and mybatis return values
Learn more about MyBatis return values
To learn about the returned values, we need to knowresultType
,resultMap
And the return value defined in the interface method.
Let's look at it first.resultType
AndresultMap
ResultType and resultMap
Everyone should know that<select>
There are two ways to set the return value for a tag:resultMap
AndresultType
.
ProcessingresultMap
AndresultType
The Code is as follows:
private void setStatementResultMap( String resultMap, Class<?> resultType, ResultSetType resultSetType, MappedStatement.Builder statementBuilder) { resultMap = applyCurrentNamespace(resultMap, true); List<ResultMap> resultMaps = new ArrayList<ResultMap>(); if (resultMap != null) { String[] resultMapNames = resultMap.split(","); for (String resultMapName : resultMapNames) { try { resultMaps.add(configuration.getResultMap(resultMapName.trim())); } catch (IllegalArgumentException e) { throw new IncompleteElementException("Could not find result map " + resultMapName, e); } } } else if (resultType != null) { ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder( configuration, statementBuilder.id() + "-Inline", resultType, new ArrayList<ResultMapping>(), null); resultMaps.add(inlineResultMapBuilder.build()); } statementBuilder.resultMaps(resultMaps); statementBuilder.resultSetType(resultSetType);}
We can see that priority is given here.resultMap
,HoweverresultType
.
Next, after MyBatis obtains data, if a row of results is processed (taking simple data as an example, nesting is not considered ):
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null); if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) { final MetaObject metaObject = configuration.newMetaObject(resultObject); boolean foundValues = resultMap.getConstructorResultMappings().size() > 0; if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) { foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; resultObject = foundValues ? resultObject : null; return resultObject; } return resultObject;}
The important code in the above Code is as follows:
if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) { foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;}foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
If is used to determine whether or not it currently supportsAutomatic ing(Configurable). This is important ifNot Supported, So it cannot be used.resultType
Method, requiredresultMap
Method, ifSupported,resultType
Method andresultMap
MethodCan be used at the same time.
The basic logic here isresultMap
Automatically map and assign values to attributesapplyAutomaticMappings
.
If the object hasresultMap
, Then proceedapplyPropertyMappings
Method.
That is, first processingresultType
Fields automatically mapped inresultMap
The configuration field in,Both can be used at the same time!
The following two methods are described in order.
resultType
Method
If Automatic ing is supportedapplyAutomaticMappings
, Which includesmetaObject
Parameters.
final MetaObject metaObject = configuration.newMetaObject(resultObject);
Let's see how to createmetaObject
The most important thing is thatReflector
Class:
for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}
Here, the attribute names in the object are mapped to the actual attribute names in uppercase. For exampleID:id
.
InapplyAutomaticMappings
In the first line, first obtain the column name without ing:
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
When obtaining the column Name:
for (String columnName : columnNames) { final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH); if (mappedColumns.contains(upperColumnName)) { mappedColumnNames.add(upperColumnName); } else { unmappedColumnNames.add(columnName); }}
Note that the column name isConvert to uppercaseAnd savesmappedColumnNames
Ing column andunmappedColumnNames
Columns not mapped.
Because both attribute names and query columns are in uppercase, the column name matches the attribute name as long as the column name is the same as the attribute name.
Therefore, when writing SQL statements, you do not need to convert the case sensitivity of the query column. automatic matching is case insensitive.
resultMap
Method
This method is also very simple, as mentioned abovemappedColumnNames
When determining whether a ing column is usedmappedColumns.contains(upperColumnName)
To judge,mappedColumns
Is the ing column we configured,Do we have to write the configuration in uppercase?
Actually, this is not case sensitive.<result column="xxx" ../>
Ofcolumn
It is case insensitive. See the following code:
for (ResultMapping compositeResultMapping : resultMapping.getComposites()) { final String compositeColumn = compositeResultMapping.getColumn(); if (compositeColumn != null) { resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH)); }}
It is also converted to uppercase.
Here we will discussresultTypt
AndresultMap
It is over, but there is a simple question. Many people don't understand. What is it? Next title.
MyBatis interface Return Value
The interface return value is usually a result, orList
And array.
How does MyBatis know if I want to return one or more results?
InMapperMethod
Part of the Code in is as follows:
if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null;} else if (method.returnsMany()) { result = executeForMany(sqlSession, args);} else if (method.returnsMap()) { result = executeForMap(sqlSession, args);} else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param);}
The query result contains 4 conditions,void
,list
(Andarray
),map
,one
.
Here it is important to determine the condition of if. This method is used to calculate the condition:
this.returnType = method.getReturnType();this.returnsVoid = void.class.equals(this.returnType);this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
We can see that these conditions are completely determined by the return value of the method. Therefore, if the returned value is an array or a set, multiple returned results are returned.
If there are multiple return values, but a POJO is written in the return value, what will happen if it is not a set or array?
The answer is: an error is reported.TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size())
.
Whether one or more results are returned, MyBatis installs multiple results for query,selectOne
Is to query one,selectList
Is to query multiple, let's lookselectOne
Code:
public <T> T selectOne(String statement, Object parameter) { List<T> list = this.<T>selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; }}
Note:
List<T> list = this.<T>selectList(statement, parameter);
In fact, no matter whether one or more results are queried, MyBatis queries by multiple results first. Getlist
The result is then judged.
If you query a resultlist
A maximum of one return value is allowed. Through the code aboveif else if esle
It is understandable.
resultTyp
,resultMap
Is it related to the number of returned values?
It does not matter.
Pass the precedingresultType
AndresultMap
We should know that this attribute is used to configure how JDBC query results are mapped to an object.
No matter what the return value is or how many values areresultType
AndresultMap
Generate returned results.
The type of the returned result is determinedresultType
AndresultMap
Decide.
Type of the returned result
The type of the returned result is determinedresultType
AndresultMap
Are you surprised to decide ???
This is actually the case ..
For example, there is an entity.Country
AndCountry2
.
InterfaceList<Country> selectAll()
, In xml<select id="selectAll" resultType="Country2">
.
What is the returned value when you call an interface? What you think is yoursList
The object type in isCountry
But they areCountry2
If the interface method isCountry selectById(Integer id)
, Which is<select id="selectById" resultType="Country2">
, Because the types are inconsistent, an error is returned during query:java.lang.ClassCastException: xx.Country2 cannot be cast to xx.Country
Why?
This is because the interface call method is the encapsulation of the namespace call method..
What is the type of the returned result when you call it using the namespace method?
Is fromresultType
AndresultMap
This is easy to understand. But I think it is different when I change to an interface.
This is because the interface method has more return values, so we will think that the returned value must be of this type. It is actually wrong.
Special Cases
When the pure annotation method is used, the return value type of the interface can be used.@ResultType
The annotation specifies the type of the return value, then the return value type written here will be usedresultType
.