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

add MapValue annotation #2863

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/main/java/org/apache/ibatis/annotations/MapValue.java
@@ -0,0 +1,27 @@
/*
* Copyright 2009-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.annotations;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MapValue {

String value();

}
23 changes: 21 additions & 2 deletions src/main/java/org/apache/ibatis/binding/MapperMethod.java
Expand Up @@ -26,6 +26,7 @@

import org.apache.ibatis.annotations.Flush;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.MapValue;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
Expand Down Expand Up @@ -193,9 +194,9 @@ private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), method.getMapValue(), rowBounds);
} else {
result = sqlSession.selectMap(command.getName(), param, method.getMapKey());
result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), method.getMapValue());
}
return result;
}
Expand Down Expand Up @@ -277,6 +278,7 @@ public static class MethodSignature {
private final boolean returnsOptional;
private final Class<?> returnType;
private final String mapKey;
private final String mapValue;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;
Expand All @@ -295,6 +297,7 @@ public MethodSignature(Configuration configuration, Class<?> mapperInterface, Me
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.mapValue = getMapValue(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
Expand Down Expand Up @@ -371,6 +374,10 @@ public String getMapKey() {
return mapKey;
}

public String getMapValue() {
return mapValue;
}

private String getMapKey(Method method) {
String mapKey = null;
if (Map.class.isAssignableFrom(method.getReturnType())) {
Expand All @@ -381,6 +388,18 @@ private String getMapKey(Method method) {
}
return mapKey;
}

private String getMapValue(Method method) {
String mapValue = null;
if (Map.class.isAssignableFrom(method.getReturnType())) {
final MapValue annotation = method.getAnnotation(MapValue.class);
if (annotation != null) {
mapValue = annotation.value();
}
}
return mapValue;
}

}

}
Expand Up @@ -31,18 +31,20 @@ public class DefaultMapResultHandler<K, V> implements ResultHandler<V> {

private final Map<K, V> mappedResults;
private final String mapKey;
private final String mapValue;
private final ObjectFactory objectFactory;
private final ObjectWrapperFactory objectWrapperFactory;
private final ReflectorFactory reflectorFactory;

@SuppressWarnings("unchecked")
public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory,
ReflectorFactory reflectorFactory) {
public DefaultMapResultHandler(String mapKey, String mapValue, ObjectFactory objectFactory,
ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
this.mappedResults = objectFactory.create(Map.class);
this.mapKey = mapKey;
this.mapValue = mapValue;
}

@Override
Expand All @@ -51,7 +53,7 @@ public void handleResult(ResultContext<? extends V> context) {
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
// TODO is that assignment always true?
final K key = (K) mo.getValue(mapKey);
mappedResults.put(key, value);
mappedResults.put(key, ((V) (mapValue == null ? value : mo.getValue(mapValue))));
}

public Map<K, V> getMappedResults() {
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/org/apache/ibatis/session/SqlSession.java
Expand Up @@ -156,6 +156,50 @@ public interface SqlSession extends Closeable {
*/
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

/**
* The selectMap is a special case in that it is designed to convert a list of results into a Map based on one of the
* properties in the resulting objects.
*
* @param <K>
* the returned Map keys type
* @param <V>
* the returned Map values type
* @param statement
* Unique identifier matching the statement to use.
* @param parameter
* A parameter object to pass to the statement.
* @param mapKey
* The property to use as key for each value in the list.
* @param mapValue
* The property to use as value for each value in the list.
*
* @return Map containing key pair data.
*/
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, String mapValue);

/**
* The selectMap is a special case in that it is designed to convert a list of results into a Map based on one of the
* properties in the resulting objects.
*
* @param <K>
* the returned Map keys type
* @param <V>
* the returned Map values type
* @param statement
* Unique identifier matching the statement to use.
* @param parameter
* A parameter object to pass to the statement.
* @param mapKey
* The property to use as key for each value in the list.
* @param mapValue
* The property to use as value for each value in the list.
* @param rowBounds
* Bounds to limit object retrieval
*
* @return Map containing key pair data.
*/
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, String mapValue, RowBounds rowBounds);

/**
* A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
*
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/apache/ibatis/session/SqlSessionManager.java
Expand Up @@ -179,6 +179,16 @@ public <K, V> Map<K, V> selectMap(String statement, Object parameter, String map
return sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds);
}

public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, String mapValue) {
return sqlSessionProxy.selectMap(statement, parameter, mapKey, mapValue);
}

@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, String mapValue,
RowBounds rowBounds) {
return sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds);
}

@Override
public <T> Cursor<T> selectCursor(String statement) {
return sqlSessionProxy.selectCursor(statement);
Expand Down
Expand Up @@ -96,8 +96,19 @@ public <K, V> Map<K, V> selectMap(String statement, Object parameter, String map

@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return this.selectMap(statement, parameter, mapKey, null, rowBounds);
}

@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, String mapValue) {
return this.selectMap(statement, parameter, mapKey, mapValue, RowBounds.DEFAULT);
}

@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, String mapValue,
RowBounds rowBounds) {
final List<? extends V> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey,
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey, mapValue,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
final DefaultResultContext<V> context = new DefaultResultContext<>();
for (V o : list) {
Expand Down
13 changes: 13 additions & 0 deletions src/test/java/org/apache/ibatis/session/SqlSessionTest.java
Expand Up @@ -53,6 +53,7 @@
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.ibatis.submitted.mapkey_value.NoticeMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -180,6 +181,18 @@ void shouldSelectAllAuthorsAsMap() {
}
}

@Test
public void shouldGroupStatusAsMap() {
try (SqlSession sqlSession = sqlMapper.openSession()) {
NoticeMapper mapper = sqlSession.getMapper(NoticeMapper.class);
Map<Integer, Integer> statusCount = mapper.groupStatus();
assertEquals(2, statusCount.size());
assertEquals(statusCount.get(1), 3);
assertEquals(statusCount.get(2), 4);

}
}

@Test
void shouldSelectCountOfPosts() {
try (SqlSession session = sqlMapper.openSession()) {
Expand Down
@@ -0,0 +1,28 @@
/*
* Copyright 2009-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.submitted.mapkey_value;

import java.util.Map;

import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.MapValue;

public interface NoticeMapper {

@MapKey("status")
@MapValue("count")
Map<Integer, Integer> groupStatus();
}
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--

Copyright 2009-2022 the original author or authors.
Copyright 2009-2023 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 @@ -77,6 +77,7 @@
<mapper resource="org/apache/ibatis/builder/CachedAuthorMapper.xml"/>
<mapper resource="org/apache/ibatis/builder/PostMapper.xml"/>
<mapper resource="org/apache/ibatis/builder/NestedBlogMapper.xml"/>
<mapper resource="org/apache/ibatis/builder/NoticeMapper.xml"/>
</mappers>

</configuration>
38 changes: 38 additions & 0 deletions src/test/resources/org/apache/ibatis/builder/NoticeMapper.xml
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--

Copyright 2009-2023 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.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.apache.ibatis.submitted.mapkey_value.NoticeMapper">


<resultMap id="groupStatusMap" type="hashmap">
<result column="count" javaType="Integer" property="count"/>
<result column="status" javaType="Integer" property="status"/>
</resultMap>


<select id="groupStatus" resultMap="groupStatusMap">
select count(*) as count , status
from notice group by status
</select>


</mapper>
@@ -1,5 +1,5 @@
--
-- Copyright 2009-2022 the original author or authors.
-- Copyright 2009-2023 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 @@ -49,10 +49,18 @@ INSERT INTO comment (id,post_id,name,comment) VALUES (3,3,'rider','I prefer moto
-- 4 5 6 7

INSERT INTO node (id, parent_id) VALUES (1,null);
INSERT INTO node (id, parent_id) VALUES (2,1);
INSERT INTO node (id, parent_id) VALUES (2,1);
INSERT INTO node (id, parent_id) VALUES (3,1);
INSERT INTO node (id, parent_id) VALUES (4,2);
INSERT INTO node (id, parent_id) VALUES (5,2);
INSERT INTO node (id, parent_id) VALUES (6,3);
INSERT INTO node (id, parent_id) VALUES (7,3);


INSERT INTO notice (id, status) VALUES (1,1);
INSERT INTO notice (id, status) VALUES (2,2);
INSERT INTO notice (id, status) VALUES (3,1);
INSERT INTO notice (id, status) VALUES (4,2);
INSERT INTO notice (id, status) VALUES (5,1);
INSERT INTO notice (id, status) VALUES (6,2);
INSERT INTO notice (id, status) VALUES (7,2);
@@ -1,5 +1,5 @@
--
-- Copyright 2009-2022 the original author or authors.
-- Copyright 2009-2023 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 @@ -80,6 +80,12 @@ parent_id INT,
PRIMARY KEY(id)
);

CREATE TABLE notice (
id INT NOT NULL,
status INT NOT NULL,
PRIMARY KEY(id)
);

CREATE PROCEDURE selectTwoSetsOfAuthors(DP1 INTEGER, DP2 INTEGER)
PARAMETER STYLE JAVA
LANGUAGE JAVA
Expand Down