如何在mybatis中select文本数组类型的结果?
How to select results of text array type in mybatis?
例如有一个table,它有一个text[]
类型的列,在PostgreSQL中:
CREATE TABLE t
(
id integer,
name text,
tags text[],
PRIMARY KEY (id)
)
现在,我想 select tags
有两种方式:
- Select
tags
使用主键id
,结果应该是List<String>
类型
- Select
tags
使用 name
,结果应该是类型 List<List<String>>
我应该如何编写 MyBatis 映射器来实现这个?
你不是第一个遇到这个问题的人。
项目common-mybatis has a type handler exactly for this use case: StringArrayTypeHandler
在MyBatis配置中添加即可:
<typeHandlers>
<typeHandler handler="org.gbif.mybatis.type.StringArrayTypeHandler"/>
</typeHandlers>
...然后对于映射,它很简单:
<select id="getTagsById" resultType="java.util.List">
SELECT tags FROM t WHERE id = #{id}
</select>
<select id="getTagsByName" resultType="java.util.List">
SELECT tags FROM t WHERE name = #{name}
</select>
...在代码中:
try (SqlSession session = sessionFactory.openSession()) {
List<String> tags = session.selectOne("[...].getTagsById", 1);
System.out.println("Tags: " + tags);
List<List<String>> multiTags = session.selectList("[...].getTagsByName", "test");
System.out.println("Tags: " + multiTags);
}
使用 JDBC 驱动程序版本 42.2.5 和以下测试数据针对 PostgreSQL 11 进行了测试:
select * from t;
id | name | tags
----+---------+------------------------------------
1 | test | {Thriller,Drama}
2 | my name | {Science-Fiction,Adventure,Horror}
3 | test | {Comedy,Adventure}
(3 rows)
...产生:
Tags: [Thriller, Drama]
Tags: [[Thriller, Drama], [Comedy, Adventure]]
你仍然可以使用 Java 映射器,然而,当 return 类型是 List
时,MyBatis 内部调用 SqlSession#selectList
而这不是你想要的。
因此,您需要使用 Object
作为 return 类型。
@Select("select tags from t where id = #{id}")
Object getTagById(Integer id);
@Select("select tags from t where name = #{name}")
List<Object> getTagByName(String name);
并在配置中全局注册您的类型处理程序。即
<typeHandlers>
<typeHandler handler="xxx.yyy.ListArrayTypeHandler" />
</typeHandlers>
或
configuration.getTypeHandlerRegistry()
.register(ListArrayTypeHandler.class);
为了完整起见,这里有一个示例类型处理程序实现。
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
@MappedJdbcTypes({ JdbcType.ARRAY })
@MappedTypes({ Object.class })
public class ListArrayTypeHandler extends BaseTypeHandler<List<?>> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
List<?> parameter, JdbcType jdbcType) throws SQLException {
// JDBC type is required
Array array = ps.getConnection().createArrayOf("TEXT", parameter.toArray());
try {
ps.setArray(i, array);
} finally {
array.free();
}
}
@Override
public List<?> getNullableResult(ResultSet rs, String columnName) throws SQLException {
return extractArray(rs.getArray(columnName));
}
@Override
public List<?> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return extractArray(rs.getArray(columnIndex));
}
@Override
public List<?> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return extractArray(cs.getArray(columnIndex));
}
protected List<?> extractArray(Array array) throws SQLException {
if (array == null) {
return null;
}
Object javaArray = array.getArray();
array.free();
return new ArrayList<>(Arrays.asList((Object[])javaArray));
}
}
仅供参考,要将 List
存储到 tags
列中,您可能必须明确指定类型处理程序。
insert into t (...) values (#{id}, #{name},
#{tags,typeHandler=xxx.yyy.ListArrayTypeHandler})
例如有一个table,它有一个text[]
类型的列,在PostgreSQL中:
CREATE TABLE t
(
id integer,
name text,
tags text[],
PRIMARY KEY (id)
)
现在,我想 select tags
有两种方式:
- Select
tags
使用主键id
,结果应该是List<String>
类型
- Select
tags
使用name
,结果应该是类型List<List<String>>
我应该如何编写 MyBatis 映射器来实现这个?
你不是第一个遇到这个问题的人。
项目common-mybatis has a type handler exactly for this use case: StringArrayTypeHandler
在MyBatis配置中添加即可:
<typeHandlers>
<typeHandler handler="org.gbif.mybatis.type.StringArrayTypeHandler"/>
</typeHandlers>
...然后对于映射,它很简单:
<select id="getTagsById" resultType="java.util.List">
SELECT tags FROM t WHERE id = #{id}
</select>
<select id="getTagsByName" resultType="java.util.List">
SELECT tags FROM t WHERE name = #{name}
</select>
...在代码中:
try (SqlSession session = sessionFactory.openSession()) {
List<String> tags = session.selectOne("[...].getTagsById", 1);
System.out.println("Tags: " + tags);
List<List<String>> multiTags = session.selectList("[...].getTagsByName", "test");
System.out.println("Tags: " + multiTags);
}
使用 JDBC 驱动程序版本 42.2.5 和以下测试数据针对 PostgreSQL 11 进行了测试:
select * from t;
id | name | tags
----+---------+------------------------------------
1 | test | {Thriller,Drama}
2 | my name | {Science-Fiction,Adventure,Horror}
3 | test | {Comedy,Adventure}
(3 rows)
...产生:
Tags: [Thriller, Drama]
Tags: [[Thriller, Drama], [Comedy, Adventure]]
你仍然可以使用 Java 映射器,然而,当 return 类型是 List
时,MyBatis 内部调用 SqlSession#selectList
而这不是你想要的。
因此,您需要使用 Object
作为 return 类型。
@Select("select tags from t where id = #{id}")
Object getTagById(Integer id);
@Select("select tags from t where name = #{name}")
List<Object> getTagByName(String name);
并在配置中全局注册您的类型处理程序。即
<typeHandlers>
<typeHandler handler="xxx.yyy.ListArrayTypeHandler" />
</typeHandlers>
或
configuration.getTypeHandlerRegistry()
.register(ListArrayTypeHandler.class);
为了完整起见,这里有一个示例类型处理程序实现。
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
@MappedJdbcTypes({ JdbcType.ARRAY })
@MappedTypes({ Object.class })
public class ListArrayTypeHandler extends BaseTypeHandler<List<?>> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
List<?> parameter, JdbcType jdbcType) throws SQLException {
// JDBC type is required
Array array = ps.getConnection().createArrayOf("TEXT", parameter.toArray());
try {
ps.setArray(i, array);
} finally {
array.free();
}
}
@Override
public List<?> getNullableResult(ResultSet rs, String columnName) throws SQLException {
return extractArray(rs.getArray(columnName));
}
@Override
public List<?> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return extractArray(rs.getArray(columnIndex));
}
@Override
public List<?> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return extractArray(cs.getArray(columnIndex));
}
protected List<?> extractArray(Array array) throws SQLException {
if (array == null) {
return null;
}
Object javaArray = array.getArray();
array.free();
return new ArrayList<>(Arrays.asList((Object[])javaArray));
}
}
仅供参考,要将 List
存储到 tags
列中,您可能必须明确指定类型处理程序。
insert into t (...) values (#{id}, #{name},
#{tags,typeHandler=xxx.yyy.ListArrayTypeHandler})