Coder Social home page Coder Social logo

Comments (4)

hgschmie avatar hgschmie commented on June 13, 2024

I think you are running against the limitations of what can be expressed with annotations.
The main problem is that your "MyClass" is parameterized but you use the raw type ("MyClass") in the Sql object. In addition, the @RegisterBeanMapper annotation does registers a bean mapper for the bean in question but there is no type information on what to do with the "T" that is part of the type definition.

You basically have two options:

a)

have a concrete type for the row. Instead of MyClass<T> have an MyIntClass extends MyClass<Integer> that resolves the T to an Integer (or whatever type you want to use).

Now this works:

@SqlQuery("select m.name as name, m.* from my_table m where condition=:value;")
@KeyColumn("name")
@RegisterConstructorMapper(MyIntClass.class)
Multimap<String, MyIntClass> getIntMap(String value);

Note that Jdbi still needs to know how to construct the Multimap. If this is a Guava multimap, you need to load the Guava plugin.

b) You do not have a concrete type but want to use a generic:

@SqlQuery("select m.name as name, m.* from my_table m where condition=:value;")
@KeyColumn("name")
Multimap<String, MyClass<Integer>> getMap(int id);

Note that you still need to use a concrete type in the method. Java does not have dynamic typing, you need to provide an actual type information.

Also, you can not register the row mapper with an annotation. Same problem. You need to resolve the type (e.g. MyClass<Integer>) and assign a mapper to it.

So you need to implement the mapper:

public static class MyIntClassMapper implements RowMapper<MyClass<Integer>> {
    @Override
    public User<Integer> map(ResultSet rs, StatementContext ctx) throws SQLException {
        ... map columns onto MyClass<Integer> ...
    }
}

and register it with Jdbi:

jdbi.registerRowMapper(new GenericType<>() {}, new MyIntClassMapper());

(note that at least with reasonably modern Java (this is with 17) it can resolve the type information for the "GenericType<>" from the mapper parameter).

because all of this is a mouthful, I put a complete example together for you at https://github.com/jdbi/jdbi/pull/2636/files, this will go into the e2e testing module as TestIssue2623.

from jdbi.

grssam avatar grssam commented on June 13, 2024

I think you are running against the limitations of what can be expressed with annotations. The main problem is that your "MyClass" is parameterized but you use the raw type ("MyClass") in the Sql object.

As I've mentioned, I've tried all combinations... quote from description:

fails with Map<String, MyClass> or Map<String, MyClass> or MultiMap<String, MyClass> as well.

....

Also, you can not register the row mapper with an annotation. Same problem. You need to resolve the type (e.g. MyClass<Integer>) and assign a mapper to it.

So you need to implement the mapper:

public static class MyIntClassMapper implements RowMapper<MyClass<Integer>> {
    @Override
    public User<Integer> map(ResultSet rs, StatementContext ctx) throws SQLException {
        ... map columns onto MyClass<Integer> ...
    }
}

We tried this - i.e. adding a custom row mapper and using the class with type provided. It works in code, but when we create a jar out of it (packaging our application), then it fails with the same exception of missing row mapper. We will raise a bug separately for this weird behavior.

and register it with Jdbi:

jdbi.registerRowMapper(new GenericType<>() {}, new MyIntClassMapper());

We did not try this, we registered the row mapper using the annotation itself.

(note that at least with reasonably modern Java (this is with 17) it can resolve the type information for the "GenericType<>" from the mapper parameter).

because all of this is a mouthful, I put a complete example together for you at https://github.com/jdbi/jdbi/pull/2636/files, this will go into the e2e testing module as TestIssue2623.

Thanks!

from jdbi.

hgschmie avatar hgschmie commented on June 13, 2024

I think you are running against the limitations of what can be expressed with annotations. The main problem is that your "MyClass" is parameterized but you use the raw type ("MyClass") in the Sql object.

As I've mentioned, I've tried all combinations... quote from description:

fails with Map<String, MyClass> or Map<String, MyClass> or MultiMap<String, MyClass> as well.

As it is expected. MyClass<T> is a parameterized class (the T is the parameter). There is no generic mapper for the type T, it needs to be resolved to a concrete type at some point. Java does not support dynamic typing, so you need to either have a class that extends MyClass<String> if T is a string or MyClass<Integer> if T is an integer. If you want to use the "any" type, you need to register the mapper for MyClass<Object>.

Each of your examples is using a "raw" MyClass. Jdbi can not find a row mapper for a "raw" MyClass. That is why it reports the error.

In the example, the jdbi.registerRowMapper(new GenericType<>() {}, new MyIntClassMapper()); stanza actually registers a mapper for a generic MyClass<Integer> type. Java can infer that type for the first argument from the second, which is why there is just an empty <>. But what the code actually does is jdbi.registerRowMapper(new GenericType<MyClass<Integer>>() {}, new MyIntClassMapper());

All the behavior of Jdbi is consistent with what you configured. Generics are tricky and omitting the generic type parameter and using a raw type generally does not work.

As I said, there are limitations to what you can do with the annotations. Registering mappers is limited to concrete classes and using the @RegisterBeanMapper, @RegisterConstructorMapper or @RegisterFieldMapper require the mapper type to match the row type.

from jdbi.

hgschmie avatar hgschmie commented on June 13, 2024

For the MultiMap, you need a collector btw. If you use the Guava multimap (https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/Multimap.html), we provide a collector as part of the jdbi3-guava module.

If you use another MultiMap, you need to register a collector with Jdbi, otherwise it can not construct the collection object.

from jdbi.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.