Jdbi и наследование: условное сопоставление?

У меня есть единственная таблица с именем Tags, в которой тег хранится в виде строки, независимо от того, какой конкретный подкласс они представляют. Некоторые строки представляют теги modbus, некоторые snmp, некоторые другие протоколы. Все классы, унаследованные от Tag, хранят свои данные в этой одной таблице, а неиспользуемые столбцы просто содержат нулевые значения.

На данный момент у меня есть такие методы DAO, как getAllModBusTags(), которые содержат инструкцию mapToBean(ModBusTag.class). В конце концов все подклассы Tag извлекаются из базы данных (по одному на каждый протокол), а затем добавляются в список ArrayList надтипа Tag.

Мой вопрос заключается в том, есть ли в Jdbi простое средство для выполнения условного сопоставления строк, чтобы, если строка содержит определенное значение, она сопоставлялась с ModBusTag.class, а если строка содержала другое значение, она сопоставлялась с SNMPTag.class, и так далее и тому подобное?

Моя конечная цель состоит в том, чтобы иметь единый оператор выбора, который извлекает каждый тег из базы данных, автоматически сопоставляется с правильным bean-компонентом построчно, а затем сохраняет все эти bean-компоненты подкласса в списке тегов супертипа.

Пример метода для одного типа:

@Override
public List<SNMPTag> getSNMPTags(){
    try(Handle handle = daoFactory.getDataSourceController().open()) {
        return handle.createQuery("SELECT * FROM dbo.Tags WHERE Active = 1 AND Protocol = 'SNMP'")
                .mapToBean(SNMPTag.class)
                .list();
    }
    catch(Exception e){
        if(sysconfig.getVerbose()){ e.printStackTrace(); }
    }
    return null;
}

Какой-то плохой псевдокод, чтобы указать, что я хочу сделать:

@Override
public List<Tag> getAllTags(){
    try(Handle handle = daoFactory.getDataSourceController().open()) {
        return handle.createQuery("SELECT * FROM dbo.Tags WHERE Active = 1")
        .mapRows(row -> row.Protocol.equals("SNMP").mapToBean(SNMPTag.class)
        .mapRows(row -> row.Protocol.equals("ModBus").mapToBean(ModBusTag.class)
        //etc
        .list();
    }
    catch(Exception e){
        if(sysconfig.getVerbose()){ e.printStackTrace(); }
    }
    return null;
}

person TheFunk    schedule 27.04.2021    source источник


Ответы (1)


Вы можете использовать RowMapper с некоторым количеством пользовательского кода для достижения того, что вам нужно, мы успешно используем такой подход в нашей проект. Вот упрощенный общий пример этой техники:

public class PolymorphicRowMapper implements RowMapper<Parent> {

    @Override
    public Parent map(ResultSet rs, StatementContext ctx) throws SQLException {
        Type type = Type.valueOf(rs.getString("type"));
        if (type == Type.A) {
            return mapTo(rs, ctx, ChildA.class);
        } else if (type == Type.B) {
            return mapTo(rs, ctx, ChildB.class);
        }
        throw new IllegalStateException("Could not resolve mapping strategy for object");
    }

    private static <T extends Parent> T mapTo(
        ResultSet rs,
        StatementContext ctx,
        Class<T> targetClass
    ) throws SQLException {
        return ctx.getConfig().get(Mappers.class)
            .findFor(targetClass)
            .orElseThrow(() ->
                new NoSuchMapperException(String.format("No mapper registered for %s class", targetClass))
            )
            .map(rs, ctx);
    }

}

public static void main(String[] args) {
    var jdbi = Jdbi.create("...")
        .registerRowMapper(BeanMapper.factory(ChildA.class))
        .registerRowMapper(BeanMapper.factory(ChildB.class));
    try (Handle handle = jdbi.open()) {
        handle.createQuery("SELECT * FROM table")
            .map(new PolymorphicRowMapper());
    }
}

public enum Type {
    A, B
}

public abstract class Parent {
    final Type type;

    protected Parent(final Type type) {
        this.type = type;
    }
}

public class ChildA extends Parent {

    public ChildA() {
        super(Type.A);
    }

}

public class ChildB extends Parent {

    public ChildB() {
        super(Type.B);
    }

}

person Yurii Melnychuk    schedule 28.04.2021
comment
Это потрясающе! Благодарю вас! Вы только что сэкономили мне 10 дорогостоящих обращений к базе данных и упростили мой код на много миль! - person TheFunk; 28.04.2021