When referencing id of another resultMap
in association/collection, unessesery ResultMap is created and stored in configuration. For example:
- When using nested collection:
<resultMap id="BlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" name="id"/>
<arg column="blog_title" name="title"/>
</constructor>
<collection property="posts" ofType="Post">
<constructor>
<idArg column="post_id" name="id"/>
<arg column="post_body" name="body"/>
</constructor>
</collection>
</resultMap>
Two ResultMaps are added to configuration:
- BlogResultMap
based on provided id of resultMap
- mapper_resultMap[BlogResultMap]_collection[posts]
for nested unnamed resultMap
This works as expected so far.
- When referencing collection by id (different approach to map same result):
<resultMap id="BlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" name="id"/>
<arg column="blog_title" name="title"/>
</constructor>
<collection property="posts" resultMap="PostResultMap"/>
</resultMap>
<resultMap id="PostResultMap" type="Post">
<constructor>
<idArg column="post_id" name="id"/>
<arg column="post_body" name="body"/>
</constructor>
</resultMap>
Three ResultMaps are added to configuration:
- BlogResultMap
based on provided id of 1st resultMap
- PostResultMap
based on provided id of 2nd resultMap
- mapper_resultMap[BlogResultMap]_collection[posts]
this one should not be created as it is not needed and doesn't even contain any mapped columns
Fix implemented in https://github.com/mybatis/mybatis-3/compare/master...piotrponikowski:remove-orphan-result-maps?expand=1#diff-c066585c40ce9a447125a1ba15b3a5a2
Method processNestedResultMappings
should be evaluated only when attribute resultMap
is empty.
Comment From: harawata
This seems like a good optimization. Thank you, @piotrponikowski !
How about using function to achieve lazy evaluation?
If we add a new method taking Supplier
in XNode
...
public String getStringAttribute(String name, Supplier<String> defSupplier) {
String value = attributes.getProperty(name);
return value == null ? defSupplier.get() : value;
}
... we can pass lambda which is evaluated only when the value
above is null
.
String nestedResultMap = context.getStringAttribute("resultMap", () ->
processNestedResultMappings(context, Collections.emptyList(), resultType));
Comment From: piotrponikowski
I like idea of Supplier
, hovewer throws
declaration on processNestedResultMappings
make it harder to use it with lambda expressions:
private String processNestedResultMappings(...)
throws Exception
We could add new functional interface like the one below, however this apporach requires more code.
@FunctionalInterface
public interface ThrowingSupplier<T> {
T get() throws Exception;
}
public String getStringAttribute(String name, ThrowingSupplier<String> defSupplier) throws Exception {
String value = attributes.getProperty(name);
return value == null ? defSupplier.get() : value;
}
There is also option to use simply try catch
, but it doesn't look clean to me:
String nestedResultMap = context.getStringAttribute("resultMap", () ->
{
try {
return processNestedResultMappings(context, Collections.emptyList(), resultType);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
I am not sure if I am familiar with functional interfaces enought to solve this in cleaner way. What do you think?
Comment From: harawata
I forgot to mention the exception, sorry.
It seems that processNestedResultMappings()
does not throw any checked exception and I have just removed it from the method declarations, so it shouldn't be a problem anymore.
Could you rebase the branch and force-push with the new changes?
(I'm sorry for the trouble, but the change I made was not directly related to this PR and I wanted to make it clear)
Comment From: piotrponikowski
Looks like I made some mistakes during rebase - will fix it tomorrow.
Comment From: piotrponikowski
Fixed PR to contain only related commits, hope it is ok now.
Comment From: harawata
LGTM. Thank you for the quick update, @piotrponikowski ! The change should be included in the latest 3.5.4-SNAPSHOT.