Alex Vergara
Alex Vergara

Reputation: 2219

Generic RowMapper JDBC

I am trying to create a generic row mapper for JDBC.

My initial attempt looks like:

public class GenericRowMapper<T> implements RowMapper<T> {

    private final Class<?> clazz;

    public GenericRowMapper(T instance) {
        this.clazz = instance.getClass().getComponentType();
    }

    @Override
    public T mapRow(ResultSet rs, int rowNum) throws SQLException {
        Field[] fields = clazz.getDeclaredFields();
        for(Field f : fields){
            System.out.println("field name : " + f.getName() + " , type : " + f.getType());
        }

        try {
            return (T) clazz.getConstructor().newInstance();
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }
}

But in the constructor, the getComponentType() is returning null, so clazz = null; and I am not being able to make any progress.

What am I doing wrong?

Upvotes: 0

Views: 1117

Answers (1)

rzwitserloot
rzwitserloot

Reputation: 102903

What I am doing wrong?

A mistake many newbie programmers make (and some experts, too!).

You have failed to read documentation.

Had you done so, you may have noticed that the docs for getComponentType() tell you that it returns the component type of an array.

Given, say, Class<?> c = String[].class, I can call c.getComponentType() and I get String.class back. Or new String[10].getClass().getComponentType() would get me the same thing.

You're clearly invoking .getComponentType() on a class that is not representing an array type, thus, you get null back.

Doing it right

Your code is rather confused. T is a type variable, thus, taking a parameter of T instance means you must provide an actual instance of it, i.e. if you have a RowMapper that maps a row into a string, you'd have to provide a string, which doesn't make sense. Presumably what you actually want as arg is Class<T> instead, in which case - voila, there is your class. No need to invoke .getClass() or .getComponentType() on it in the first place. If truly you intended for this method to require that you pass an actual instance first, .getClass() would be the method you presumably want, except I don't think that's going to work: .getClass() always gives you the actual type, i.e. calling .getClass() on a variable of type List cannot ever return List.class - it would return perhaps ArrayList.class or some anonymous inner class that implements List.

The rest of your code is similarly confused

.getDeclaredFields() only returns the fields in the class definition itself, not any parent classes (whose fields are just as much part of any instance of the class as any other) - again, read docs to figure this out.

'print a stack trace, return null' is very silly exception handling. The right 'I do not (yet) care about it' exception handler is throw new RuntimeException("unhandled", e); - shorter, better. There is no reason not to. Fix your IDE's templates if you must.

Not ready for modern java

Many, many modern takes on java (notably including the record feature) has an immutable class where you therefore cannot just 'set' a bunch of fields, and there won't be a no-argument constructor (which .getConstructor() gets you, thus, .getConstructor() would fail). Instead, the constructor itself takes all args; therefore, you're not interested in the fields, you're interested in the parameters of the constructor. Except there could be more than one, and classes do not (neccessarily) contain the names of parameters.

Upvotes: 2

Related Questions