Search code examples
javaspringspring-jdbc

How to register a GenericConverter to be used by SimpleJdbcCall objects in Spring 3?


Using Spring 3.0.x, I wrote a SimpleJdbcCall defining with returningResultSet() a BeanPropertyRowMapper.

Now I want to replace the java.lang.Long type used for object IDs with a strongly typed ID, that is defining a PersonId class that holds the actual ID values as a Long variable.

I wrote a org.springframework.core.convert.converter.GenericConverter implementation that converts from the BigDecimal that is returned by JDBC to PersonID (and some others, for other classes).

I registered my converter as:

<bean id="conversionService"
    class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
    <set>
        <bean class="xxx.impl.IDTypeConverter"/>
    </set>
</property>

The problem

Spring does not use my converter.

Debugging leads to method org.springframework.beans.TypeConverterDelegate.convertIfNecessary(String, Object, Object, Class, TypeDescriptor) where:

ConversionService conversionService = this.propertyEditorRegistry.getConversionService();

this method returns null. There are no conversions used.

Searching the web I found https://stackoverflow.com/a/12798635 where is says:

spring was trying to find a custom converter registered with PropertyEditor

Then it proposes a solution using a property editor but does not go into detail how to actually do it. (I asked there)

Another answer https://stackoverflow.com/a/53120800 says to use the method jdbcCustomConversions() from (Abstract)JdbcConfiguration, but I can't find those classes in my project.

I could write a custom RowMapper for each JDBC call, but that is a lot of code.

The question is: how to convert those datatypes with as few code as possible?


Solution

  • I came up with this solution in my DAO class, where the SimpleJdbcCall objects are used:

    @Autowired
    private ConversionService conversionService;
    //...
    {
            BeanPropertyRowMapper<MyDTO> rowMapper1 = new BeanPropertyRowMapper<MyDTO>() {
            @Override
            protected void initBeanWrapper(BeanWrapper bw)
            {
                bw.setConversionService(conversionService);
            }
        };
        rowMapper1.setMappedClass(MyDTO.class);
        
        mySimpleJdbcCall.returningResultSet("p_my_object", rowMapper1);
    

    This works (in JUnit tests) for reading data from database.

    Writing

    I used the solution from here: https://stackoverflow.com/a/50368110

    Basically, override the getValue() method to specially handle my type:

    BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(pojo) {
        @Override
        public Object getValue(String paramName) throws IllegalArgumentException {
            Object value = super.getValue(paramName);
            if (value instanceof MyIDType) {
                return ((MyIDType) value).getInternalValue();
            }
    
            return value;
        }
    };