【问题标题】:Jdbi and Inheritance: Conditional Mapping?Jdbi 和继承:条件映射?
【发布时间】:2021-04-27 19:37:23
【问题描述】:

我有一个名为Tags 的表,它将“标签”存储为一行,而不管它们代表什么特定的子类。一些行代表 modbus 标签,一些 snmp,一些其他协议。继承自 Tag 的所有类都将其数据存储在这张表中,未使用的列仅包含空值。

目前,我有像 getAllModBusTags() 这样的 DAO 方法,其中包含一条指令 mapToBean(ModBusTag.class)。最终,所有 Tag 的子类都从数据库中获取(每个协议一次获取),然后添加到超类型 Tag 的 ArrayList 中。

我的问题是,Jdbi 是否有一种简单的方法来执行行的条件映射,以便如果行包含特定值,则将其映射到 ModBusTag.class 但如果行包含不同的值,则将其映射到 SNMPTag .class,等等等等?

我的最终目标是有一个单一的 select 语句,它从数据库中获取每个标签,逐行自动映射到正确的 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;
}

【问题讨论】:

    标签: java inheritance polymorphism jdbi jdbi3


    【解决方案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);
        }
    
    }
    
    

    【讨论】:

    • 这太完美了!谢谢!您刚刚为我节省了 10 次昂贵的数据库调用,并大大简化了我的代码!
    猜你喜欢
    • 1970-01-01
    • 2017-12-07
    • 2011-11-22
    • 2010-11-25
    • 1970-01-01
    • 1970-01-01
    • 2014-07-22
    • 2018-08-25
    相关资源
    最近更新 更多