JDBI3 使用 BeanMapper.factory(Class, prefix) 时,要求 SQL 查询结果列名必须以 prefix_(下划线)开头,而非 prefix.(点号);若未显式别名化字段,数据库返回的原始列名(如 t.id)不会被自动转换为 t_id,导致字段匹配失败。
jdbi3 使用 `beanmapper.factory(class, prefix)` 时,要求 sql 查询结果列名**必须以 `prefix_`(下划线)开头**,而非 `prefix.`(点号);若未显式别名化字段,数据库返回的原始列名(如 `t.id`)不会被自动转换为 `t_id`,导致字段匹配失败。
在 JDBI3 中,BeanMapper.factory(Ticket.class, "t") 并非通过 SQL 别名 t. 动态解析字段,而是严格依赖 ResultSet 中实际列名(getColumnName())是否以 "t_" 开头。例如:
- ✅ 正确列名(可匹配):t_id、t_ticket_int_1、t_dateFiled
- ❌ 错误列名(无法匹配):t.id、id、ticket_int_1(即使来自 t.*)
因此,你原查询:
SELECT t.*, i.* FROM issue AS i JOIN ticket_issues AS ti ON i.id = ti.issue_id JOIN ticket AS t ON ti.ticket_id = t.id
虽然语法合法,但多数 JDBC 驱动(如 PostgreSQL、MySQL)对 t.* 展开后返回的列名是原始字段名(如 id, ticket_int_1),不带表别名前缀,更不会自动转为 t_id 形式 —— 这正是 IllegalArgumentException: Mapping bean class Ticket didn't find any matching columns 的根本原因。
✅ 正确解决方案:显式声明带 _ 前缀的列别名
你需要手动为每个需映射的字段指定别名,格式为
_<field_name>:<pre class="brush:php;toolbar:false;">String selectSql = """
SELECT
t.id AS t_id,
t.ticket_int_1 AS t_ticket_int_1,
t.ticket_int_2 AS t_ticket_int_2,
t.ticket_int_3 AS t_ticket_int_3,
t.ticket_varchar_1 AS t_ticket_varchar_1,
t.dateFiled AS t_dateFiled,
t.dateLastUpdated AS t_dateLastUpdated,
t.dateResolved AS t_dateResolved,
i.id AS i_id,
i.issue_bigInt_1 AS i_issue_bigInt_1,
i.issue_bigInt_2 AS i_issue_bigInt_2,
i.issue_int_1 AS i_issue_int_1,
i.issue_bigInt_3 AS i_issue_bigInt_3,
i.issue_int_2 AS i_issue_int_2,
i.issue_int_3 AS i_issue_int_3,
i.issue_varchar_1 AS i_issue_varchar_1,
i.issue_varchar_2 AS i_issue_varchar_2,
i.issue_varchar_3 AS i_issue_varchar_3,
i.issue_int_4 AS i_issue_int_4,
i.dateResolved AS i_dateResolved,
i.dateCreated AS i_dateCreated,
i.dateUpdate AS i_dateUpdate
FROM issue AS i
JOIN ticket_issues AS ti ON i.id = ti.issue_id
JOIN ticket AS t ON ti.ticket_id = t.id
""";
jdbi.useHandle(handle -> {
handle.registerRowMapper(BeanMapper.factory(Ticket.class, "t"));
handle.registerRowMapper(BeanMapper.factory(Issue.class, "i"));
handle.registerRowMapper(JoinRowMapper.forTypes(Ticket.class, Issue.class));
List<joinrow issue>> results = handle.createQuery(selectSql)
.mapTo(JoinRow.class)
.list();
return results;
});</joinrow>
? 提示:JoinRow
是 JDBI3 提供的泛型容器,用于承载一对关联对象。确保 Ticket 和 Issue 类字段命名与别名严格一致(如 ticket_int_1 ↔ t_ticket_int_1),且类型兼容。
⚠️ 注意事项与最佳实践
- 不要依赖 t.* 或 i.* 的“魔法展开”:JDBI3 的 BeanMapper 不解析 SQL AST,也不修改 ResultSet 元数据,它只做字符串前缀匹配。
- 大小写敏感性:列别名大小写需与 Java 字段名完全一致(如 t_dateFiled → dateFiled,而非 datefiled)。
- 避免列名冲突:若 Ticket 和 Issue 含同名字段(如 dateResolved),必须用不同前缀别名区分(t_dateResolved / i_dateResolved),否则 JoinRowMapper 无法正确分发。
-
考虑使用 @SqlQuery + @RegisterBeanMapper(推荐):更简洁、类型安全:
@SqlQuery("SELECT ... ") // 同上完整别名SQL @RegisterBeanMappers({ @RegisterBeanMapper(value = Ticket.class, prefix = "t"), @RegisterBeanMapper(value = Issue.class, prefix = "i") }) List<joinrow issue>> findTicketIssues();</joinrow> -
调试技巧:临时打印 ResultSet 列名验证:
ResultSet rs = ...; ResultSetMetaData meta = rs.getMetaData(); for (int i = 1; i
遵循此规范,即可彻底解决 Mapping bean class didn't find any matching columns 异常,让 JDBI3 的 Bean 映射稳定可靠地工作。










