It is currently not possible to reuse a resultmap created using the @ConstructorArgs and/or @Results annotation.
If would be nice if you could name and reference @Results, e.g.:
@Select("select * from users")
@Results(id = "User", value = {
@Result(property = "id", column = "id", id = "true"),
@Result(property = "firstName", column = "first_name")
@Result(property = "lastName", column = "last_name")
})
List<User> getUsers();
@Select("select * from users where id = #{value}")
@ResultMap("User")
User getUserById(int id);
@Select("select * from users where first_name = #{value}")
@ResultMap("User")
List<User> getUsersByFirstName(String firstName);
Note: Please also consider implementing issue #81
Note: You can already reference the resultmap generated from @ConstructorArgs and/or @Results, but the generated name in non-trivial:
Example:
@Select("select * from users")
@Results({
@Result(property = "id", column = "id", id = "true"),
@Result(property = "firstName", column = "first_name")
@Result(property = "lastName", column = "last_name")
})
List<User> getUsers();
@Select("select * from users where id = #{value}")
@ResultMap("getUsers-void")
User getUserById(int id);
@Select("select * from users where first_name = #{value}")
@ResultMap("getUsers-void")
List<User> getUsersByFirstName(String firstName);
Looking at the MyBatis code this would be real easy to implements. Just add an id property to @ConstructorArgs, @Results, @TypeDiscriminator and @Case and in org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parseResultMap and createDiscriminatorResultMaps use the id from the annotation as resultMapId or default to generateResultMapName when not specified.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ConstructorArgs {
+++ String id() default "";
Arg[] value() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Results {
+++ String id() default "";
Result[] value() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TypeDiscriminator {
+++ String id();
String column();
Class<?> javaType() default void.class;
JdbcType jdbcType() default JdbcType.UNDEFINED;
Class<? extends TypeHandler<?>> typeHandler() default UnknownTypeHandler.class;
Case[] cases();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Case {
+++ String id();
String value();
Class<?> type();
Result[] results() default {};
Arg[] constructArgs() default {};
}
private String parseResultMap(final Method method) {
Class<?> returnType = getReturnType(method);
ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
Results results = method.getAnnotation(Results.class);
TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
--- String resultMapId = generateResultMapName(method);
+++ String resultMapId;
+++ if (args != null && args.id().length() > 0) {
+++ resultMapId = args.id();
+++ } else if (results != null && results.id().length() > 0) {
+++ resultMapId = results.id();
+++ } else if (typeDiscriminator != null && typeDiscriminator.id().length() > 0) {
+++ resultMapId = typeDiscriminator.id();
+++ } else {
+++ resultMapId = generateResultMapName(method);
+++ }
applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
return resultMapId;
}
private void createDiscriminatorResultMaps(final String resultMapId, final Class<?> resultType, final TypeDiscriminator discriminator) {
if (discriminator != null) {
for (Case c : discriminator.cases()) {
--- String caseResultMapId = resultMapId + "-" + c.value();
+++ String caseResultMapId;
+++ if (c.id().length() > 0) {
+++ caseResultMapId = c.id();
+++ } else {
+++ caseResultMapId = resultMapId + "-" + c.value();
+++ }
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
applyConstructorArgs(c.constructArgs(), resultType, resultMappings); // issue #136
applyResults(c.results(), resultType, resultMappings);
this.assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null); // TODO add AutoMappingBehaviour
}
}
}
+1
+1
+1
+1
As a single mapper method can have both @Results and @ConstructorArgs, adding id property to each annotation would be confusing at least (there should be only one id per result map (i.e. per method)).
I think we need to add a new annotation (e.g. @ResultId) just to name the result map defined using these annotations.
@Select("select * from users")
@ResultsId("User")
@Results({
@Result(property = "id", column = "id", id = "true"),
@Result(property = "firstName", column = "first_name")
@Result(property = "lastName", column = "last_name")
})
List<User> getUsers();
What do you think?
And a better name for the new annotation?
Any plan to implement this feature ?
@vlcheong
Please read my previous comment and let me know what you think!
@harawata
Adding new annotation making the code more verbose. Personally, I prefer Condor70's idea by adding the id attribute to the @Results annotation.
I think you only consider the simplest case.
To define a result map, you can use any combination of @Results, @ConstructorArgs or @TypeDiscriminator.
Please see some examples below.
// No @Results annotation
@Select("select * from users")
@ConstructorArgs({
@Arg(property = "id", column = "id", id = "true"),
@Arg(property = "firstName", column = "first_name")
@Arg(property = "lastName", column = "last_name")
})
List<User> getUsers();
// Both @ConstructorArgs and @Results
@Select("select * from users")
@ConstructorArgs({
@Args(property = "id", column = "id", id = "true"),
})
@Results({
@Result(property = "firstName", column = "first_name")
@Result(property = "lastName", column = "last_name")
})
List<User> getUsers();
Adding a new annotation is simpler and clearer in cases like above, in my opinion.
@harawata
Yes, I just think of the @Results use case. Since @Results, @ConstructorArgs and @TypeDiscriminator are different annotation, using a more conscience name like @MapperId would be relatively sensible compare to @ResultsId.
It's now too late to change, but I would have preferred it if the annotations followed the XML more closely, e.g.
@Results(id = "User", value = {
@ConstructorArgs({
@Args(property = "id", column = "id", id = "true")
}),
@Result(property = "firstName", column = "first_name"),
@Result(property = "lastName", column = "last_name")
})
Thank you guys for your comment!
@Condor70 's example made me rethink about reusing the existing annotation.
Here is a new idea:
@Results annotation.The advantages are:
@Results to name your result map. No confusing alternatives.Here are the modified version of the examples from my previous comment:
1) Using @Results just for naming the result map.
@Select("select * from users")
@Results(id = "userResult") // no value attribute
@ConstructorArgs({
@Arg(property = "id", column = "id", id = "true"),
@Arg(property = "firstName", column = "first_name")
@Arg(property = "lastName", column = "last_name")
})
List<User> getUsers();
2) Combination of @Results and @ConstructorArgs, but name must be specified in @Results.
@Select("select * from users")
@ConstructorArgs({
@Args(property = "id", column = "id", id = "true"),
})
@Results(id = "userResult", value = {
@Result(property = "firstName", column = "first_name")
@Result(property = "lastName", column = "last_name")})
List<User> getUsers();
Does it look reasonable?
@harawata look good to me
@harawata Thanks for your effort
Thank you everyone for the ideas and comments!
I have committed the change and the feature is available in the latest 3.4.0-SNAPSHOT.
Although it is implemented as I explained in my previous comment, it still is snapshot, so feel free to add a comment if you have a better idea.
nice
@harawata it is not working in the specific scenario.
Lets say we have
@Select("select * from users")
@Results(id = "userResult", value = {
@Result(property = "id", column = "id")
@Result(property = "firstName", column = "first_name")
@Result(property = "lastName", column = "last_name")})
List
and we want to use the same result mapping on different method. For example:
@Select("select * from users where last_name like %abc% ")
@ResultMap("userResult")
List
The test case pass successfully but it doesn't return anything. Am I missing something or is it really a problem ?
Hi @luqmanahmad
Thank you for testing!
The test case pass successfully but it doesn't return anything.
Which test case are you referring to?
This test case looks the same as your example and it verifies the returned result.
@luqmanahmad
Actually the Result Id feature is working fine. From the example you attached
@Select("select * from users where last_name like %abc%")
@ResultMap("userResult")
List<User> getUserByLastName()
Your SQL is literal variable. Thus, you need to add single quote like '%abc%', this got nothing to do with Result Id
Hi @harawata,
Sorry I was out of town for a while couldn't get back to you.
It is working now. I am really not sure why it was not working before. I ran all the tests but thanks for coming back to me.
@vicheong
The query I put in my example was just an example I didn't have it in my real example but thanks for spotting it. :+1:
@luqmanahmad OK. Thanks for the follow-up!
So has it been suppoted?
@daikaixian Yes. Milestone=3.3.1 means it works in version 3.3.1 and later.
it's work . Thanks !
ibatis.xml.txt
hi sir, I hit more complicated case. I have convert from ibatis for mybatis using annotation.
below is the ibatis sqlmap ,there is one cursor from storedprocedure out .
need to map to one java class. how to do ?
please find attached
thank you very much!
hkronaldo
Hi @hkronaldo, this is not the right place to ask question, please post your question at Stack Overflow instead. Anyway, you may checkout this @Results to map your object.
hi @vlcheong, understand but cannot access the link
Hi @hkronaldo, the link fixed.
thanks, but annotation can help for plsql cursor case ?
only xml can help only ?
Most helpful comment
As a single mapper method can have both
@Resultsand@ConstructorArgs, adding id property to each annotation would be confusing at least (there should be only one id per result map (i.e. per method)).I think we need to add a new annotation (e.g.
@ResultId) just to name the result map defined using these annotations.What do you think?
And a better name for the new annotation?