Skip to content

Commit

Permalink
Merge pull request mybatis#1698 from kazuki43zoo/support-repeatable-a…
Browse files Browse the repository at this point in the history
…nnotation-on-result-and-arg

Support repeatable annotation on `@Arg` and `@Result`
  • Loading branch information
kazuki43zoo committed Oct 31, 2019
2 parents 128d24e + 4bc8e95 commit 5a4560a
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 15 deletions.
5 changes: 4 additions & 1 deletion src/main/java/org/apache/ibatis/annotations/Arg.java
Expand Up @@ -16,6 +16,8 @@
package org.apache.ibatis.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand All @@ -32,7 +34,8 @@
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@Target(ElementType.METHOD)
@Repeatable(ConstructorArgs.class)
public @interface Arg {

/**
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/org/apache/ibatis/annotations/Result.java
Expand Up @@ -16,6 +16,8 @@
package org.apache.ibatis.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand All @@ -32,7 +34,8 @@
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@Target(ElementType.METHOD)
@Repeatable(Results.class)
public @interface Result {
/**
* Returns whether id column or not.
Expand Down
Expand Up @@ -39,7 +39,6 @@
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.CacheNamespaceRef;
import org.apache.ibatis.annotations.Case;
import org.apache.ibatis.annotations.ConstructorArgs;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.Insert;
Expand Down Expand Up @@ -228,11 +227,11 @@ private void parseCacheRef() {

private String parseResultMap(Method method) {
Class<?> returnType = getReturnType(method);
ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
Results results = method.getAnnotation(Results.class);
Arg[] args = method.getAnnotationsByType(Arg.class);
Result[] results = method.getAnnotationsByType(Result.class);
TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
String resultMapId = generateResultMapName(method);
applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
applyResultMap(resultMapId, returnType, args, results, typeDiscriminator);
return resultMapId;
}

Expand Down Expand Up @@ -626,14 +625,6 @@ private String nullOrEmpty(String value) {
return value == null || value.trim().length() == 0 ? null : value;
}

private Result[] resultsIf(Results results) {
return results == null ? new Result[0] : results.value();
}

private Arg[] argsIf(ConstructorArgs args) {
return args == null ? new Arg[0] : args.value();
}

private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
Class<?> resultTypeClass = selectKeyAnnotation.resultType();
Expand Down
91 changes: 91 additions & 0 deletions src/test/java/org/apache/ibatis/binding/BindingTest.java
Expand Up @@ -36,6 +36,7 @@
import net.sf.cglib.proxy.Factory;

import org.apache.ibatis.BaseDataTest;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.domain.blog.Author;
import org.apache.ibatis.domain.blog.Blog;
Expand Down Expand Up @@ -688,4 +689,94 @@ void registeredMappers() {
assertTrue(mapperClasses.contains(BoundAuthorMapper.class));
}

@Test
void shouldMapPropertiesUsingRepeatableAnnotation() {
try (SqlSession session = sqlSessionFactory.openSession()) {
BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
mapper.insertAuthor(author);
Author author2 = mapper.selectAuthorMapToPropertiesUsingRepeatable(author.getId());
assertNotNull(author2);
assertEquals(author.getId(), author2.getId());
assertEquals(author.getUsername(), author2.getUsername());
assertEquals(author.getPassword(), author2.getPassword());
assertEquals(author.getBio(), author2.getBio());
assertEquals(author.getEmail(), author2.getEmail());
session.rollback();
}
}

@Test
void shouldMapConstructorUsingRepeatableAnnotation() {
try (SqlSession session = sqlSessionFactory.openSession()) {
BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
mapper.insertAuthor(author);
Author author2 = mapper.selectAuthorMapToConstructorUsingRepeatable(author.getId());
assertNotNull(author2);
assertEquals(author.getId(), author2.getId());
assertEquals(author.getUsername(), author2.getUsername());
assertEquals(author.getPassword(), author2.getPassword());
assertEquals(author.getBio(), author2.getBio());
assertEquals(author.getEmail(), author2.getEmail());
assertEquals(author.getFavouriteSection(), author2.getFavouriteSection());
session.rollback();
}
}

@Test
void shouldMapUsingSingleRepeatableAnnotation() {
try (SqlSession session = sqlSessionFactory.openSession()) {
BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
mapper.insertAuthor(author);
Author author2 = mapper.selectAuthorUsingSingleRepeatable(author.getId());
assertNotNull(author2);
assertEquals(author.getId(), author2.getId());
assertEquals(author.getUsername(), author2.getUsername());
assertNull(author2.getPassword());
assertNull(author2.getBio());
assertNull(author2.getEmail());
assertNull(author2.getFavouriteSection());
session.rollback();
}
}

@Test
void shouldMapWhenSpecifyBothArgAndConstructorArgs() {
try (SqlSession session = sqlSessionFactory.openSession()) {
BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
mapper.insertAuthor(author);
Author author2 = mapper.selectAuthorUsingBothArgAndConstructorArgs(author.getId());
assertNotNull(author2);
assertEquals(author.getId(), author2.getId());
assertEquals(author.getUsername(), author2.getUsername());
assertEquals(author.getPassword(), author2.getPassword());
assertEquals(author.getBio(), author2.getBio());
assertEquals(author.getEmail(), author2.getEmail());
assertEquals(author.getFavouriteSection(), author2.getFavouriteSection());
session.rollback();
}
}

@Test
void shouldMapWhenSpecifyBothResultAndResults() {
try (SqlSession session = sqlSessionFactory.openSession()) {
BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
mapper.insertAuthor(author);
Author author2 = mapper.selectAuthorUsingBothResultAndResults(author.getId());
assertNotNull(author2);
assertEquals(author.getId(), author2.getId());
assertEquals(author.getUsername(), author2.getUsername());
assertNull(author2.getPassword());
assertNull(author2.getBio());
assertNull(author2.getEmail());
assertNull(author2.getFavouriteSection());
session.rollback();
}
}

}

86 changes: 85 additions & 1 deletion src/test/java/org/apache/ibatis/binding/BoundAuthorMapper.java
@@ -1,5 +1,5 @@
/**
* Copyright 2009-2018 the original author or authors.
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -68,6 +68,23 @@ public interface BoundAuthorMapper {

//======================================================

@Result(property = "id", column = "AUTHOR_ID", id = true)
@Result(property = "username", column = "AUTHOR_USERNAME")
@Result(property = "password", column = "AUTHOR_PASSWORD")
@Result(property = "email", column = "AUTHOR_EMAIL")
@Result(property = "bio", column = "AUTHOR_BIO")
@Select({
"SELECT ",
" ID as AUTHOR_ID,",
" USERNAME as AUTHOR_USERNAME,",
" PASSWORD as AUTHOR_PASSWORD,",
" EMAIL as AUTHOR_EMAIL,",
" BIO as AUTHOR_BIO",
"FROM AUTHOR WHERE ID = #{id}"})
Author selectAuthorMapToPropertiesUsingRepeatable(int id);

//======================================================

@ConstructorArgs({
@Arg(column = "AUTHOR_ID", javaType = Integer.class),
@Arg(column = "AUTHOR_USERNAME", javaType = String.class),
Expand All @@ -89,6 +106,73 @@ public interface BoundAuthorMapper {

//======================================================

@Arg(column = "AUTHOR_ID", javaType = Integer.class, id = true)
@Arg(column = "AUTHOR_USERNAME", javaType = String.class)
@Arg(column = "AUTHOR_PASSWORD", javaType = String.class)
@Arg(column = "AUTHOR_EMAIL", javaType = String.class)
@Arg(column = "AUTHOR_BIO", javaType = String.class)
@Arg(column = "AUTHOR_SECTION", javaType = Section.class)
@Select({
"SELECT ",
" ID as AUTHOR_ID,",
" USERNAME as AUTHOR_USERNAME,",
" PASSWORD as AUTHOR_PASSWORD,",
" EMAIL as AUTHOR_EMAIL,",
" BIO as AUTHOR_BIO," +
" FAVOURITE_SECTION as AUTHOR_SECTION",
"FROM AUTHOR WHERE ID = #{id}"})
Author selectAuthorMapToConstructorUsingRepeatable(int id);

//======================================================

@Arg(column = "AUTHOR_ID", javaType = int.class)
@Result(property = "username", column = "AUTHOR_USERNAME")
@Select({
"SELECT ",
" ID as AUTHOR_ID,",
" USERNAME as AUTHOR_USERNAME,",
" PASSWORD as AUTHOR_PASSWORD,",
" EMAIL as AUTHOR_EMAIL,",
" BIO as AUTHOR_BIO",
"FROM AUTHOR WHERE ID = #{id}"})
Author selectAuthorUsingSingleRepeatable(int id);

//======================================================

@ConstructorArgs({
@Arg(column = "AUTHOR_ID", javaType = Integer.class),
@Arg(column = "AUTHOR_USERNAME", javaType = String.class),
@Arg(column = "AUTHOR_PASSWORD", javaType = String.class),
@Arg(column = "AUTHOR_EMAIL", javaType = String.class),
@Arg(column = "AUTHOR_BIO", javaType = String.class)
})
@Arg(column = "AUTHOR_SECTION", javaType = Section.class)
@Select({
"SELECT ",
" ID as AUTHOR_ID,",
" USERNAME as AUTHOR_USERNAME,",
" PASSWORD as AUTHOR_PASSWORD,",
" EMAIL as AUTHOR_EMAIL,",
" BIO as AUTHOR_BIO," +
" FAVOURITE_SECTION as AUTHOR_SECTION",
"FROM AUTHOR WHERE ID = #{id}"})
Author selectAuthorUsingBothArgAndConstructorArgs(int id);

//======================================================

@Results(
@Result(property = "id", column = "AUTHOR_ID")
)
@Result(property = "username", column = "AUTHOR_USERNAME")
@Select({
"SELECT ",
" ID as AUTHOR_ID,",
" USERNAME as AUTHOR_USERNAME",
"FROM AUTHOR WHERE ID = #{id}"})
Author selectAuthorUsingBothResultAndResults(int id);

//======================================================

List<Post> findThreeSpecificPosts(@Param("one") int one,
RowBounds rowBounds,
@Param("two") int two,
Expand Down

0 comments on commit 5a4560a

Please sign in to comment.