gain insight into MyBatis return values
To understand the return value, we need to understand the Resulttype,resultmap and the return value defined in the interface method.
Let's see Resulttype and Resultmap Resulttype and Resultmap .
As you should know, there are two ways to set the return value in the <select> tab of MyBatis, namely Resultmap and Resulttype.
The code for handling Resultmap and Resulttype is as follows:
private void Setstatementresultmap (String resultmap, class<?> resulttype, ResultsetType Res
Ultsettype, 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); }
You can see that the resultmap is treated preferentially here, but Resulttype is also used.
Next look at MyBatis after getting the data, if you work with a single row of results (for example, in simple data, regardless of the nesting situation):
Private Object Getrowvalue (Resultsetwrapper rsw, Resultmap resultmap) throws SQLException {final Resultloadermap lazy
Loader = new Resultloadermap ();
Object resultobject = Createresultobject (RSW, Resultmap, lazyloader, NULL); if (resultobject! = null &&!typehandlerregistry.hastypehandler (Resultmap.gettype ())) {final MetaObject m
Etaobject = Configuration.newmetaobject (Resultobject);
Boolean foundvalues = Resultmap.getconstructorresultmappings (). Size () > 0; if (Shouldapplyautomaticmappings (Resultmap,! AutoMappingBehavior.NONE.equals (Configuration.getautomappingbehavior ()))) {foundvalues = Applyautomaticmappin GS (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 it is determined whether the current support Auto-mapping (can be configured), it is very important, if not supported , then can not use the Resulttype method, must be used in Resultmap mode, if supported , Resulttype mode and Resultmap mode can be used simultaneously .
The basic logic here is to first assign a value to an attribute that has no resultmap, which is implemented by Applyautomaticmappings.
If the object has Resultmap, then the Applypropertymappings method is also performed.
That is, the fields that are automatically mapped in Resulttype are processed first, and the fields that are configured in the Resultmap can be used simultaneously.
Here are two ways to say it in order. Resulttype Way
If auto-mapping is supported, applyautomaticmappings is executed, which has the MetaObject parameter.
Final MetaObject metaobject = Configuration.newmetaobject (Resultobject);
Let's look at one of the most critical places to create MetaObject in the Reflector class:
for (String propname:readablepropertynames) {
caseinsensitivepropertymap.put (propname.touppercase ( Locale.english), propname);
}
Here, the attribute names in the entity are mapped, and the corresponding actual property names are capitalized. such as Id:id.
In the first row in Applyautomaticmappings, first get the column names that are not mapped:
Final list<string> unmappedcolumnnames = Rsw.getunmappedcolumnnames (Resultmap, Columnprefix);
When you get 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 names are converted to uppercase , while the mappedcolumnnames mapped columns and unmappedcolumnnames columns are saved.
because both the property name and the query column are uppercase, the column names and property names are matched as long as they are capitalized.
So when we write SQL, we don't need to convert the case of the query columns, and the auto-match is case-insensitive. Resultmap Way
This method is also very simple, the above mentioned mappedcolumnnames, in determining whether the mapping column, the use of Mappedcolumns.contains (uppercolumnname) to judge, Mappedcolumns is the mapped column we configured, so we have to capitalize when we configure it.
Actually not, here is also not case-sensitive, in <result column= "xxx". /> 's column is also not case-sensitive, see the following code:
For (resultmapping compositeResultMapping:resultMapping.getComposites ()) {
final String Compositecolumn = Compositeresultmapping.getcolumn ();
if (compositecolumn! = null) {
ResultMap.mappedColumns.add (compositecolumn.touppercase (Locale.english));}
}
Here also converted to uppercase.
Here about the Resulttypt and Resultmap is over, but there is a simple question, many people do not understand, what is. Look at the next headline. MyBatis Interface return value
The interface return value is usually a result, or a list and array. MyBatis How to know if I want to return a result or multiple results.
Some of the code in Mappermethod 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);
}
You can see that the query results are in 4 cases, void,list (and Array), Map,one.
The important thing here is if the judging condition, this judgment condition calculation method:
This.returntype = Method.getreturntype ();
This.returnsvoid = Void.class.equals (this.returntype);
This.returnsmany = (Configuration.getobjectfactory (). IsCollection (this.returntype) | | This.returnType.isArray ());
As you can see, these conditions are entirely determined by the return value of the method. So if you write a return value that is an array or a collection, the result is multiple.
If the return value itself has more than one, but the return value writes a Pojo, not a collection or an array.
The answer is an error toomanyresultsexception ("Expected one result (or null) to being returned by SelectOne (), but found:" + list.size ()).
Whether it is returning a result or multiple results, MyBatis is installing multiple results for querying, SelectOne is querying one, selectlist is querying multiple, let's see SelectOne 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 being returned by SelectOne (), but found:" + list.size ());
} else {
return null;
}
}
Take a look:
list<t> list = this.<t>selectlist (statement, parameter);
In fact, regardless of whether you query one or more results, MyBatis is queried by multiple results first. After getting the list results in judgment.
If you are querying a result, the list can have at most one return value. By the above code the IF else if Esle can be understood very clearly. How much does Resulttyp,resultmap have to do with the return value?
There is no relationship.
With the contents of the preceding Resulttype and resultmap, we should know that this property is to configure how the JDBC query results are mapped to an object.
Regardless of what the return value is or a few, the results are returned according to Resulttype and Resultmap.
The type of the returned result is determined by Resulttype and Resultmap. returns the type of the result
The type of return result is determined by Resulttype and Resultmap, is it not surprising ...
This is actually the case.
For example, there is an entity country and Country2.
Interface list<country> SelectAll (), XML <select id= "SelectAll" resulttype= "Country2";.
When you call through the interface, the return value is what. You think the object type in your list is country, but they're actually Country2.
If the interface method is country Selectbyid (Integer ID), the XML is <select id= "Selectbyid" resulttype= "Country2", because the type is inconsistent, When the query will be error: JAVA.LANG.CLASSCASTEXCEPTION:XX. Country2 cannot is cast to XX. why would country do that?
this is because the interface invocation method is the encapsulation of the namespace-mode invocation .
when you call through a namespace, what type of result is returned.
Is the type determined by Resulttype and Resultmap, which is easy to understand. But instead of the interface, it feels different.
this is because the interface method has more return values, so we would think that the return must be of this type. is actually wrong. Special Cases
When using pure annotations, the return value type of an interface can be useful, and if you do not specify a return value type using the @resulttype annotation, the return value type written here is used as the resulttype.