如何使用 mybatis 接口而不是 sqlSession 处理地图结果(两列结果)
How to handle map result(two column results) using mybatis interface not sqlSession
我在 java 网络应用程序中使用 Spring 引导和 Mybatis(界面和 mapper.xml)。
我的目的是将包含两列 'name' 和 'count' 的结果列表转换为地图。第一列应用作该映射中的键,第二列应用作该映射中的值。
我知道我应该重写ResultHandler,但是使用接口怎么会生效呢?
已更新。我通过添加一个拦截器来处理这些情况并在 mybatis-config.xml 中添加插件以启用它来实现此方法。
首先,创建一个注解,然后你可以通过添加注解来处理这种情况,覆盖它并将第一列设置为键,将第二列设置为值。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
public @interface ResultMap {
String value();
}
其次,你应该实现一个拦截器来处理带有 @ResultMap
注解的 mybatis resultSet
public class ResultMapInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(ResultMapInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
DefaultResultSetHandler defaultResultSetHandler = (DefaultResultSetHandler) target;
Field field = ReflectionUtils.findField(DefaultResultSetHandler.class, "mappedStatement");
ReflectionUtils.makeAccessible(field);
MappedStatement mappedStatement = (MappedStatement) field.get(defaultResultSetHandler);
String className = StringUtils.substringBeforeLast(mappedStatement.getId(), ".");
String methodName = StringUtils.substringAfterLast(mappedStatement.getId(), ".");
Method[] methods = Class.forName(className).getDeclaredMethods();
if (methods == null) {
return invocation.proceed();
}
// get method "mappedStatement"
for (Method method : methods) {
if (methodName.equalsIgnoreCase(method.getName())) {
// get annotation ResultMap, if null, then proceed
ResultMap resultMap = method.getAnnotation(ResultMap.class);
if (resultMap == null) {
return invocation.proceed();
}
// convert result map to specify result
Statement statement = (Statement) invocation.getArgs()[0];
return convert2Map(statement);
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof ResultSetHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
}
private Object convert2Map(Statement statement) throws Throwable{
ResultSet resultSet = statement.getResultSet();
if (resultSet == null) {
return null;
}
List<Object> resultList = new ArrayList(4);
Map<Object, Object> map = new HashMap(8);
while (resultSet.next()) {
if (resultSet.getObject(1) != null) {
map.put(resultSet.getObject(1), resultSet.getObject(2));
}
}
resultList.add(map);
LOGGER.debug("map result from DB, {}", resultList);
return resultList;
}
然后在mybatis中添加插件开启这个拦截器-config.xml
</configuration>
<plugins>
<plugin interceptor="com.handler.ResultMapInterceptor"/>
</plugins>
</configuration>
最后在mybatis dao接口中使用这个注解
@ResultMap("")
Map getCountByAccType(SearchFilter searchFilter);
mapper.xml
<select id="getCountByAccType" resultMap="resultMap">
select
id,
count(ACCOUNTTYPE) count
from table t
</select>
最简单的方法是使用默认接口方法进行转换:
interfact MyMapper {
@Select("SELECT name, count FROM whatever")
List<Map<String, Object>> findCountsList();
default Map<String, Integer> findCounts() {
Map<String, Integer> result = new HashMap<>;
for(Map<String, Object> entry:findCountsList()) {
result.put(entry.get("name"), ((Number)entry.get("count")).intValue());
}
return result;
}
}
我在 java 网络应用程序中使用 Spring 引导和 Mybatis(界面和 mapper.xml)。
我的目的是将包含两列 'name' 和 'count' 的结果列表转换为地图。第一列应用作该映射中的键,第二列应用作该映射中的值。
我知道我应该重写ResultHandler,但是使用接口怎么会生效呢?
已更新。我通过添加一个拦截器来处理这些情况并在 mybatis-config.xml 中添加插件以启用它来实现此方法。
首先,创建一个注解,然后你可以通过添加注解来处理这种情况,覆盖它并将第一列设置为键,将第二列设置为值。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
public @interface ResultMap {
String value();
}
其次,你应该实现一个拦截器来处理带有 @ResultMap
注解的 mybatis resultSet
public class ResultMapInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(ResultMapInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
DefaultResultSetHandler defaultResultSetHandler = (DefaultResultSetHandler) target;
Field field = ReflectionUtils.findField(DefaultResultSetHandler.class, "mappedStatement");
ReflectionUtils.makeAccessible(field);
MappedStatement mappedStatement = (MappedStatement) field.get(defaultResultSetHandler);
String className = StringUtils.substringBeforeLast(mappedStatement.getId(), ".");
String methodName = StringUtils.substringAfterLast(mappedStatement.getId(), ".");
Method[] methods = Class.forName(className).getDeclaredMethods();
if (methods == null) {
return invocation.proceed();
}
// get method "mappedStatement"
for (Method method : methods) {
if (methodName.equalsIgnoreCase(method.getName())) {
// get annotation ResultMap, if null, then proceed
ResultMap resultMap = method.getAnnotation(ResultMap.class);
if (resultMap == null) {
return invocation.proceed();
}
// convert result map to specify result
Statement statement = (Statement) invocation.getArgs()[0];
return convert2Map(statement);
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof ResultSetHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
}
private Object convert2Map(Statement statement) throws Throwable{
ResultSet resultSet = statement.getResultSet();
if (resultSet == null) {
return null;
}
List<Object> resultList = new ArrayList(4);
Map<Object, Object> map = new HashMap(8);
while (resultSet.next()) {
if (resultSet.getObject(1) != null) {
map.put(resultSet.getObject(1), resultSet.getObject(2));
}
}
resultList.add(map);
LOGGER.debug("map result from DB, {}", resultList);
return resultList;
}
然后在mybatis中添加插件开启这个拦截器-config.xml
</configuration>
<plugins>
<plugin interceptor="com.handler.ResultMapInterceptor"/>
</plugins>
</configuration>
最后在mybatis dao接口中使用这个注解
@ResultMap("")
Map getCountByAccType(SearchFilter searchFilter);
mapper.xml
<select id="getCountByAccType" resultMap="resultMap">
select
id,
count(ACCOUNTTYPE) count
from table t
</select>
最简单的方法是使用默认接口方法进行转换:
interfact MyMapper {
@Select("SELECT name, count FROM whatever")
List<Map<String, Object>> findCountsList();
default Map<String, Integer> findCounts() {
Map<String, Integer> result = new HashMap<>;
for(Map<String, Object> entry:findCountsList()) {
result.put(entry.get("name"), ((Number)entry.get("count")).intValue());
}
return result;
}
}