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

Association javaType property not automatically resolved #1381

Closed
berniegp opened this Issue Oct 30, 2018 · 3 comments

Comments

Projects
None yet
2 participants
@berniegp
Copy link
Contributor

berniegp commented Oct 30, 2018

MyBatis version

3.4.6

Test case or example project

// Java types (simplified)

public class AuthInfo {
	private Account account;
	private String password;
	// getters + setters
}

public class Account {
	private String username;
	// getters + setters
}

// Mapper (with auto-mapping)

<resultMap id="authInfoResult" type="AuthInfo">
  <association property="account"></association>
  <!-- <association property="account" javaType="Account"> <-- fixes the problem -->
</resultMap>

<select id="selectAuthInfo" resultMap="authInfoResult">
  SELECT username, password FROM accounts WHERE username = #{username}
</select>

Steps to reproduce

Execute selectAuthInfo(username)

Expected result

No exception :)

Actual result

### Cause: java.lang.NullPointerException
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:150)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:77)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
	... 37 more
Caused by: java.lang.NullPointerException
	at java.lang.Class.isAssignableFrom(Native Method)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createRowKey(DefaultResultSetHandler.java:1024)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyNestedResultMappings(DefaultResultSetHandler.java:957)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:918)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForNestedResultMap(DefaultResultSetHandler.java:881)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:328)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:303)
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:196)
	at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
	at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
	at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
	at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:326)
	at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
	at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
	at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:83)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
	... 44 more

Discussion

The documentation states, regarding the javaType property of association, that "MyBatis can usually figure out the type if you're mapping to a JavaBean." Looking at the code however, type detection does not occur when the result map is built:

// from XMLMapperBuilder
  private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
	// snip
	String type = resultMapNode.getStringAttribute("type",
		resultMapNode.getStringAttribute("ofType",
			resultMapNode.getStringAttribute("resultType",
				resultMapNode.getStringAttribute("javaType"))));
	// snip
	Class<?> typeClass = resolveClass(type);
	// snip
  }

// from BaseBuilder
  protected Class<?> resolveClass(String alias) {
	if (alias == null) {
	  return null;
	}
	try {
	  return resolveAlias(alias);
	} catch (Exception e) {
	  throw new BuilderException("Error resolving class. Cause: " + e, e);
	}
  }

Either the documentation for the javaType property should be changed (easiest) or type detection should be implemented (harder).

Sidenote: is there a better way to accomplish this use-case? I want to sometimes select only Account and sometimes AuthInfo that contains an Account. Both operations should occur in a single select clause. AuthInfo is just an Account with added fields.

@harawata

This comment has been minimized.

Copy link
Member

harawata commented Oct 31, 2018

Thank you, @berniegp .
I'll look into it.

Both operations should occur in a single select clause.

As you need two unrelated objects, you may need two statements (i.e. selectAccount and selectAuthInfo).
You can reuse Account's result map, though.

  <resultMap type="test.Account" id="accountResult">
    <id property="id" column="id" />
    <result property="username" column="username" />
  </resultMap>

  <resultMap type="test.AuthInfo" id="authInfoResult">
    <id column="id" />
    <result property="password" column="password" />
    <association property="account"
      resultMap="accountResult" />
  </resultMap>
@berniegp

This comment has been minimized.

Copy link
Contributor Author

berniegp commented Oct 31, 2018

Thanks so much for taking the time to provide this guidance.

If you decide which option to go with, I'll be happy to help if I can with a pull request. I can either update the doc (option 1) or provide a failing test case (option 2) at the very least.

@harawata

This comment has been minimized.

Copy link
Member

harawata commented Oct 31, 2018

I think I have a fix, so a failing test case would be nice.
Thanks in advance!

FYI, there is another possibly related issue and I plan to take a look at it before committing the fix.

@harawata harawata self-assigned this Nov 4, 2018

@harawata harawata added the bug label Nov 4, 2018

@harawata harawata added this to the 3.5.0 milestone Nov 4, 2018

harawata added a commit that referenced this issue Nov 5, 2018

@harawata harawata closed this in c797058 Nov 5, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment