Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve POJO constructor arguments by name rather than position #721

Closed
usethe4ce opened this issue Jun 29, 2016 · 8 comments
Closed

Resolve POJO constructor arguments by name rather than position #721

usethe4ce opened this issue Jun 29, 2016 · 8 comments
Assignees
Labels
enhancement Improve a feature or add a new feature
Milestone

Comments

@usethe4ce
Copy link

The docs say:

In order to inject the results into the constructor, MyBatis needs to identify the constructor by the type of its parameters. Java has no way to introspect (or reflect) on parameter names. So when creating a constructor element, ensure that the arguments are in order, and that the data types are specified.

But actually there are few ways commonly used to get at the constructor's parameter names. Spring can wire up immutable beans this way, for example. There's compiling with debug info (very common nowadays), the older @ConstructorProperties annotation, explicit annotations on each constructor parameter (e.g. MyBatis's own @Param), and in JDK8 the compilation option -parameters.

It's a huge advantage, of course, when dealing with immutables to be able to match constructor parameters to fields not positionally, which is quite fragile, but by name.

@harawata harawata added the enhancement Improve a feature or add a new feature label Jul 8, 2016
@harawata harawata self-assigned this Jan 24, 2017
@harawata harawata added this to the 3.4.3 milestone Jan 24, 2017
harawata added a commit that referenced this issue Jan 24, 2017
@harawata
Copy link
Member

harawata commented Jan 24, 2017

I have committed the changes.
You can specify the parameter name in name attribute of @Arg annotation or <idArg /> and <arg /> elements.

<constructor>
  <arg column="col2" name="secondArg" javaType="string" />
  <arg column="col1" name="firstArg" javaType="int" />
</constructor>
@ConstructorArgs({
  @Arg(column = "col2", name = "secondArg", javaType = String.class),
  @Arg(column = "col1", name = "firstArg", javaType = Integer.class)
})
  • To reference constructor parameters by name, you can either 1) annotate parameters with @Param annotation or 2) compile with -parameters option.
  • javaType can be omitted if there is a property with the same name and type.
  • When MyBatis could not find a matching constructor, it throws an exception during build-phase.

@usethe4ce , @reddyalready
It would be great if you guys could try the latest 3.4.3-SNAPSHOT.

harawata added a commit to mybatis/mybatis.github.io that referenced this issue Jan 25, 2017
@reddyalready
Copy link

reddyalready commented Jul 13, 2017

@harawata Just wanted to thank you again for doing this, I finally got to play around with it - using Kotlin/immutability no less, and it works like a charm 😄

I believe it is not possible yet to create complex immutable objects (resulting from joins, for example) using constructor args, e.g.:

User {
  id: Int = 1,
  address: Address = {
    houseNum: Int = 1,
    line1: String = "Blah Blah"
  }
}

I believe this is related to #101

Can you please confirm?

@reddyalready
Copy link

After some more digging, I found #968, where you mention a test that uses columnPrefix inside a constructor. This is exactly what I'm looking for, but I clone the mybatis code and was unable to find such a test. Could you please point me to this test?

@harawata
Copy link
Member

harawata commented Jul 13, 2017

Thank you for the feedback, @reddyalready !

The test case I mentioned in #968 was this one .
My understanding was that the issue was about a result map referenced from an <association /> element, though.

Regarding your question, it is possible with some limitations [1] if I understand correctly.
Please see this test case and the corresponding mapper statement.

[1] Collections are not supported as you found in #101 and it has low reusability because there is no columnPrefix in <arg /> and <idArg />.

p.s.
Not sure if this works with Kotlin's immutable object, but with Java, you can use <association /> to map results to a private property.

public class User {
  private Integer id;
  private Address address;
  public User(Integer id) {
    this.id = id;
  }
  // getters only
}
<select id="selectUser" resultMap="userRM">
  select
    u.id,
    a.house_num addr_house_num,
    a.line1 addr_line1
  from user u, address a
  where ...
</select>

<resultMap type="User" id="userRM">
  <constructor>
    <idArg column="id" javaType="int" />
  </constructor>
  <association property="address" resultMap="addressRM" columnPrefix="addr_" />
</resultMap>

<resultMap type="Address" id="addressRM">
  <constructor>
    <arg column="house_num" javaType="int" />
    <arg column="line1" javaType="string" />
  </constructor>
</resultMap>

In this case, you can use columnPrefix.

@reddyalready
Copy link

Thank you very much @harawata, you've been a great help!

I tried complex objects in constructor args shortly after and they seem to be working fine so far, I'll switch to your suggested method should things get more complex.

@dant3
Copy link

dant3 commented Aug 18, 2017

It would be nice if it supported somewhat more standard approach with @java.beans.ConstructorProperties as well. It's in standard library (not on Android unfortunately, but still), things like lombok can annotate your ctors then you use @lombok.Value and you get Spring resolution of constructor args as well with one-stop-shop annotation.

@harawata
Copy link
Member

harawata commented Sep 5, 2017

@dant3 ,
I'm not sure what you meant by 'support', but if the constructor generated by Lombok has the right parameter names, you just need to add -parameters compiler option to reference them from MyBatis mapper, I think.

@GoldSubmarine
Copy link

Hi @harawata

I have add a test about this issue in this fork. Can u check it? I want MyBatis to map automatically with the constructor by argument names without defining <constructor>...</constructor> or @ConstructorArgs.

Is this a feature that MyBatis intends to support or has already supported?

I am using kotlin's data class, I want mybatis to be automatically mapped, just like I did through the empty constructor and getter setter in java.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improve a feature or add a new feature
Projects
None yet
Development

No branches or pull requests

5 participants