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
When updating to version 3.38.1, an error message "No row mapper registered for map key class java.lang.String" was prompted. #2342
Comments
Hi @buzzxu , can you share a bit more detail about how you set up Jdbi? We didn't run into this problem when upgrading. |
Sorry, I just saw your reply。 private void customizeJdbi(Jdbi jdbi){
jdbi.registerColumnMapper(new GenericType<List<IdName>>() {
}, (r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? Collections.emptyList() : Jackson.json2List(val, IdName.class);
});
jdbi.registerColumnMapper(new GenericType<List<Map<String, Object>>>() {
}, (r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? null : Jackson.json2Object(val, Jackson.buildCollectionType(List.class, Map.class));
});
jdbi.registerColumnMapper(new GenericType<Map<String, Object>>() {
}, (r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? Collections.emptyMap() : Jackson.json2Map(val);
});
jdbi.registerColumnMapper(new GenericType<List<String>>() {
}, (r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? Collections.emptyList() : Jackson.isJSON(val) ? Jackson.json2Object(val, new TypeReference<List<String>>() {
}) : Splitter.on(",").splitToList(val);
});
jdbi.registerColumnMapper(new GenericType<List<Integer>>() {
}, (r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? Collections.emptyList() : Jackson.isJSON(val) ? Jackson.json2Object(val, new TypeReference<List<Integer>>() {
}) : Splitter.on(",").splitToStream(val).map(Integer::valueOf).toList();
});
jdbi.registerColumnMapper(new GenericType<List<Long>>() {
}, (r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? Collections.emptyList() : Jackson.isJSON(val) ? Jackson.json2Object(val, new TypeReference<List<Long>>() {
}): Splitter.on(",").splitToStream(val).map(Long::valueOf).toList();
});
jdbi.registerColumnMapper(new GenericType<String[]>(){}, (r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? null : Jackson.isJSON(val) ? Jackson.json2Object(val,String[].class) : Splitter.on(",").splitToStream(val).toArray(String[]::new );
});
jdbi.registerColumnMapper(new GenericType<Integer[]>(){},(r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? null : Jackson.isJSON(val) ? Jackson.json2Object(val,Integer[].class) :Splitter.on(",").splitToStream(val).map(Integer::valueOf).toArray(Integer[]::new);
});
jdbi.registerColumnMapper(new GenericType<Long[]>(){},(r, columnNumber, ctx) -> {
String val = r.getString(columnNumber);
return Strings.isNullOrEmpty(val) ? null : Jackson.isJSON(val) ? Jackson.json2Object(val,Long[].class) :Splitter.on(",").splitToStream(val).map(Long::valueOf).toArray(Long[]::new);
});
jdbi.registerArgument(new AbstractArgumentFactory<List<IdName>>(Types.VARCHAR) {
@Override
protected Argument build(List<IdName> val, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position, Jackson.object2Json(val));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<List<Map<String, Object>>>(Types.VARCHAR) {
@Override
protected Argument build(List<Map<String, Object>> val, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position, Jackson.object2Json(val));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<Map<String, Object>>(Types.VARCHAR) {
@Override
protected Argument build(Map<String, Object> val, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position, Jackson.object2Json(val));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<int[]>(Types.VARCHAR) {
@Override
protected Argument build(int[] value, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position, Jackson.object2Json(value));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<Integer[]>(Types.VARCHAR) {
@Override
protected Argument build(Integer[] value, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position,Jackson.object2Json(value));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<Long[]>(Types.VARCHAR) {
@Override
protected Argument build(Long[] value, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position,Jackson.object2Json(value));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<String[]>(Types.VARCHAR) {
@Override
protected Argument build(String[] value, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position, Jackson.object2Json(value));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<List<Integer>>(Types.VARCHAR) {
@Override
protected Argument build(List<Integer> value, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position,Jackson.object2Json(value));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<List<Long>>(Types.VARCHAR) {
@Override
protected Argument build(List<Long> value, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position, Jackson.object2Json(value));
}
});
jdbi.registerArgument(new AbstractArgumentFactory<List<String>>(Types.VARCHAR) {
@Override
protected Argument build(List<String> value, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setString(position,Jackson.object2Json(value));
}
});
} I'll keep testing.Thank you very much for your reply. |
This is my code in the DAO: default List<ProductMini> findAllOrderBy(int limit, OrderBy orderBy, Map<String, Object> params){
...
} This is the exception information:
|
Thanks for the additional information. Is it easy for you to test whether 3.38.0 works with your application? There were significant changes between the releases, and it would help us isolate which code causes the regression you see. |
(the stack trace is actually from 3.38.2) |
So I can reproduce this (with a test case) and for methods that return a Map<String, Object> this behavior existed at least back to 3.34.0. I would be curious in which version that has worked before. The stack trace and the method you listed confuse me. The method returns a Are you sure that the code you showed us may not be a "working" version and there is another version where the code does not return a |
so here is what I can reproduce. This is the test code: @Test
public void testRoundTripDeclarative() throws Exception {
Map<String, JsonBean> result = pgExtension.getJdbi().withExtension(MapDao.class, dao -> dao.getValues(1));
assertThat(result).isNotNull().hasSize(1);
JsonBean bean = result.get("test");
assertThat(bean).isNotNull().extracting("id").isEqualTo(1);
assertThat(bean).extracting("key").isEqualTo("test");
assertThat(bean).extracting("value").isEqualTo(content);
}
public static final class JsonBean {
private final int id;
private final String key;
private final Map<String, Object> value;
public JsonBean(int id, String key, Map<String, Object> value) {
this.id = id;
this.key = key;
this.value = value;
}
public int getId() {
return id;
}
public String getKey() {
return key;
}
public Map<String, Object> getValue() {
return value;
}
}
public interface MapDao {
@SqlQuery("SELECT * FROM json_data WHERE id = :id")
Map<String, JsonBean> getValues(int id);
} I run this code, I get this:
This is how it should be. Just the error message is bad. What it should say is "Jdbi does not know what result column you would like to use for your map key". You can fix this by adding the public interface MapDao {
@SqlQuery("SELECT * FROM json_data WHERE id = :id")
@KeyColumn("key")
Map<String, JsonBean> getValues(int id);
} makes the test pass. Now, Jdbi knows that the key for your map should be taken from the |
ok, so I think while writing tests, I stumbled onto the actual problem. :-) "Is there another method in the DAO that returns a Map? That method may not even be in use. The 3.38.0 code changed the warming up of SQL Object methods to be more aggressive (basically at metadata creation time). So if there is another method in that DAO, it would be warmed as well as soon as the DAO is loaded. And it seems that you have a method (maybe one that you do not use at all) that has the problem described above. So, yes, it actually is a behavior change (I am hesitant to call it a bug because it is a problem with your code) that now surfaces this problem much earlier (at init time, not at call time). |
The next release restores the old behavior. |
I'm sorry for the late reply to your message. The issue has been resolved, and thank you. |
Hello, when updating to version 3.38.1, an exception occurred: org.jdbi.v3.core.mapper.NoSuchMapperException: No row mapper registered for map key class java.lang.String. This issue did not occur in version 3.37.1.
The text was updated successfully, but these errors were encountered: