使用MyBatis

时间:2017-01-20 11:38:16

标签: postgresql mybatis

我有一个超级简单的表test,例如

create table test (
    id serial primary key,
    status varchar (10)
);

insert into test (status) 
     values ('ready'), ('ready'), 
            ('steady'), 
            ('go'), ('go'), ('go'), 
            ('new');

要获得聚合计数,我可以运行: -

1)使用group by

的简单多行结果
select status, 
       count(id) as count        
  from test
 group by status

...返回......

-------+-------
status | counts
-------+-------
go     |      3
ready  |      2
new    |      1
steady |      1
-------+-------

2)使用jsonb_object_agg

的单行结果
    with stats as (
       select status, 
              count(id) as count        
         from test
     group by status
    )
    select jsonb_object_agg (status, count) as status_counts from stats

...返回......

--------------------------------------------------
status_counts
--------------------------------------------------
{ "go" : 3, "new" : 1, "ready" : 2, "steady" : 1 }
--------------------------------------------------

Mybatis接口方法。

在我的Java代码中(通过MyBatis)我有一个方法: -

public Map<String, Integer> selectStatusCounts();

我想知道的是如何通过MyBatis将查询映射到Map<String, Integer> Java对象?

更新(1)

关于a_horse_with_no_name建议和this stackover article我提出了这个问题: -

3)使用hstore

的单行结果
select hstore(array_agg(hs_key), array_agg(hs_value::text))
from (
    select 
        status, 
        count(id) as count        
    from test
    group by status    
) x(hs_key,hs_value)

...返回......

--------------------------------------------------
status_counts
--------------------------------------------------
"go"=>"3", "new"=>"1", "ready"=>"2", "steady"=>"1"
--------------------------------------------------

使用这样的东西可能有用: -

https://github.com/gbif/checklistbank/blob/master/checklistbank-mybatis-service/src/main/java/org/gbif/checklistbank/service/mybatis/postgres/HstoreCountTypeHandler.java

现在测试吧! : - )

更新(2)

再次感谢a_horse_with_no_name您的贡献 - 我现在非常接近但与MyBatis仍然很奇怪。这是我创建的类型处理程序(所以我可以在其他地方重用聚合): -

@MappedTypes(LinkedHashMap.class)
@MappedJdbcTypes(JdbcType.OTHER)
public class MyBatisMapHstoreToStringIntegerMap implements TypeHandler<Map<String, Integer>> {

    public MyBatisMapHstoreToStringIntegerMap() {}

    public void setParameter(PreparedStatement ps, int i, Map<String, Integer> map, JdbcType jdbcType) throws SQLException {
        ps.setString(i, HStoreConverter.toString(map));
    }

    public Map<String, Integer> getResult(ResultSet rs, String columnName) throws SQLException {
        return readMap(rs.getString(columnName));
    }

    public Map<String, Integer> getResult(ResultSet rs, int columnIndex) throws SQLException {
        return readMap(rs.getString(columnIndex));
    }

    public Map<String, Integer> getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return readMap(cs.getString(columnIndex));
    }

    private Map<String, Integer> readMap(String hstring) throws SQLException {
        if (hstring != null) {
            Map<String, Integer> map = new LinkedHashMap<String, Integer>();
            Map<String, String> rawMap = HStoreConverter.fromString(hstring);
            for (Map.Entry<String, String> entry : rawMap.entrySet()) {
                map.put(entry.getKey(), Integer.parseInt(entry.getValue())); // convert from <String, String> to <String,Integer>
            }

            return map;
        }
        return null;
    }

}

...这里是mapper界面...

public interface TestMapper {

    public Map<String, Integer> selectStatusCounts();

}

...这里是XML映射文件中的<select> ...

<select id="selectStatusCounts" resultType="java.util.LinkedHashMap">
    select hstore(array_agg(hs_key), array_agg(hs_value::text)) as status_counts
    from (
        select 
            status, 
            count(id) as count        
        from test
        group by status    
    ) x(hs_key,hs_value)
</select>

但是,它返回Map,其中一个名为status_counts的条目,其值是我想要的实际地图,即{status_counts={new=1, ready=2, go=3, steady=1}}

以下是关于PostgreSQL / MyBatis的maven依赖关系: -

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.2.2</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>9.4-1201-jdbc41</version>
    </dependency>

2 个答案:

答案 0 :(得分:0)

最简单的方法是定义hstore_agg()函数:

CREATE AGGREGATE hstore_agg(hstore) 
(
    SFUNC = hs_concat(hstore, hstore),
    STYPE = hstore
);

然后你可以这样做:

select hstore_agg(hstore(status, cnt::text))
from (
  select status, count(*) cnt
  from test
  group by status
) t;

使用当前的JDBC驱动程序Statement.getObject()将返回Map<String, String>

由于hstore仅存储字符串,因此无法返回Map<String, Integer>

答案 1 :(得分:0)

发布答案(这不完美),但又想知道是否有其他人有更好的解决方案。

我的回答是基于这个stackoverflow答案: -

Return HashMap in mybatis and use it as ModelAttribute in spring MVC

我创建了一个名为KeyValue的POJO类: -

public class KeyValue<K, V> {

    private K key;
    private V value;

    public KeyValue() {
    }

    public KeyValue(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }

}

...并将测试映射器方法更改为...

@MapKey("key")
public Map<String, KeyValue<String, Integer>> selectStatusCounts();
  

请注意使用@MapKey参数

我正在使用“1)简单的多行结果,使用” SQL原始问题“,并将结果列更改为key + value(所以它映射到新的KeyValue对象),如下所示: -

<select id="selectStatusCounts" resultType="test.KeyValue">
   select status    as key, 
          count(id) as value        
     from bulk_upload
 group by status
</select>

使用Java访问它实现如下: -

Map<String, KeyValue<String, Integer>> statusCounts = mapper.selectStatusCounts();

并检索例如我们所做的new项目地图中的值: -

int numberOfstatusCounts = statusCounts.get("new").getValue();

我对这个解决方案非常满意,但我仍然更喜欢Map<String, Integer>而不是Map<String, KeyValue<String, Integer>>,所以不接受我的解决方案 - 这只是为了表明我是如何得到的工作的东西(现在)。