【问题标题】:How should I write RowMapper with other Object dependencies?我应该如何使用其他对象依赖项编写 RowMapper?
【发布时间】:2016-02-22 11:41:56
【问题描述】:

简介

这是交易,让我们假设我的豆子是这样的:

public class Bean1 {
    private long id;
    private long idBean2;
    private Bean2 bean2;
}

public class Bean2 {
    private long id;
}

(假设对应的getter和setter)

我想用关联的 DAO 从我的数据库中检索 非常大量 的 Bean1,并且我希望 Bean2 与之关联。我应该如何进行?我有三个想法,各有利弊,我应该选择哪一个?

(假定自动装配、服务、JDBC 初始化)

想法 1

public class Bean1Dao {
    public List<Bean1> selectBean1s() {
        String query =
            "SELECT " +
            "  b1.* " +
            "  b2.* " +
            "FROM " +
            "  BEAN1 b1 " +
            "  INNER JOIN BEAN2 b2 ON b1.ID_B2 = b2.ID";

        return jdbcTemplate.query(query, new Bean1Mapper());
    }

    public static class Bean1Mapper implements ParameterizedRowMapper<Bean1> {
        @Override
        public Bean1 mapRow(final ResultSet rs, final int rownum) throws SQLException {
            Bean1 bean1 = new Bean1();
            bean1.setId = rs.getLong("b1.ID");
            bean1.setIdBean2 = rs.getLong("b1.ID_B2");
            try {
                bean1.setBean2 = new Bean2Dao.Bean2Mapper().mapRow(rs, rownum);
            } catch (Exception e) {
                // Log exception here... Or not
            }
        }
    }
}

public class Bean2Dao {
    public List<Bean2> selectBean2s() {
        String query = "SELECT b2.* FROM BEAN2";
        return jdbcTemplate.query(query, new Bean2Mapper());
    }

    public static class Bean2Mapper implements ParameterizedRowMapper<Bean2> {
        @Override
        public Bean2 mapRow(final ResultSet rs, final int rownum) throws SQLException {
            Bean2 bean2 = new Bean2();
            bean2.setId = rs.getLong("b2.ID");
        }
    }
}

优点:1 个 SQL 查询

缺点:需要在所有看起来很繁重的 SQL 查询中指定前缀,并且(我很确定)会由于缺少前缀而导致烦人的错误。

想法 2

public class Bean1Dao {
    public List<Bean1> selectBean1s() {
        String query = "SELECT * FROM BEAN1";
        return jdbcTemplate.query(query, new Bean1Mapper());
    }

    public static class Bean1Mapper implements ParameterizedRowMapper<Bean1> {
        @Override
        public Bean1 mapRow(final ResultSet rs, final int rownum) throws SQLException {
            Bean1 bean1 = new Bean1();
            bean1.setId = rs.getLong("ID");
            bean1.setIdBean2 = rs.getLong("ID_B2");
        }
    }
}

在这种情况下,DAO 没有映射 Bean2 对象,服务必须循环遍历 Bean1 对象并为每个对象调用 Bean2Dao,以便使用 idBean2 属性映射它。

优点:非常简单的查询

缺点:执行速度非常慢(我在这里说的是数千或百万个对象,这意味着在这种情况下需要相同数量的数据库调用)

想法 3

public class Bean1Dao {
    public Map<Long, Bean1> selectBean1Map() {
        String query = "SELECT * FROM BEAN1";
        List<Bean1> bean1List = jdbcTemplate.query(query, new Bean1Mapper());

        Map<Long, Bean1> bean1Map = new HashMap<Long, Bean1>();
        for (Bean1 bean1 : bean1List) {
            bean1Map.put(bean1.getId(), bean1);
        }
        return bean1Map;
    }

    public static class Bean1Mapper implements ParameterizedRowMapper<Bean1> {
        @Override
        public Bean1 mapRow(final ResultSet rs, final int rownum) throws SQLException {
            Bean1 bean1 = new Bean1();
            bean1.setId = rs.getLong("ID");
            bean1.setIdBean2 = rs.getLong("ID_B2");
        }
    }
}

public class Bean2Dao {
    public Map<Long, Bean2> selectBean2Map() {
        String query = "SELECT * FROM BEAN2";
        List<Bean2> bean2List = jdbcTemplate.query(query, new Bean2Mapper());

        Map<Long, Bean2> bean2Map = new HashMap<Long, Bean2>();
        for (Bean2 bean2 : bean2List) {
            bean2Map.put(bean2.getId(), bean2);
        }
        return bean2Map;
    }

    public static class Bean2Mapper implements ParameterizedRowMapper<Bean2> {
        @Override
        public Bean2 mapRow(final ResultSet rs, final int rownum) throws SQLException {
            Bean2 bean2 = new Bean2();
            bean2.setId = rs.getLong("ID");
        }
    }
}

这里服务需要循环遍历bean1Map来分配bean2Map中的对象。

优点:n 个查询和看起来很简单的 DAO(n 是要在 DB 中查找的 Bean1 成员的数量(这里 n = 1,但在我的情况下它更大))

缺点:n 个查询(想法 1 只有 1 个查询)

结论

我不太确定要采用哪种解决方案。也许我错过了另一个更好的解决方案,或者我错过了一个定义应该如何做的标准。 对此我将不胜感激。

TL;DR:如果数据量非常大,我应该如何从数据库中检索对象并链接其也在数据库中的成员?

【问题讨论】:

  • “非常大的数字”大约是多少?
  • 从 10.000 到 1.000.000,它仍然可以再次变大。所以,好的,根据上下文,它可以是一个小数字,但就循环那个数字到执行 SQL 查询而言,它很大。

标签: java mysql spring jdbc dao


【解决方案1】:

您是否绝对需要在某个时间点将所有 bean 都留在您的记忆中?

我的建议:选择 1 选项 - 即使您必须为您的内容添加前缀。

缺点:是时候编写 SQL 了(一次性)

专业版:

  • 性能:您声称您有大量的行。说 1.000.000 行。想想 1.000.000 次额外 SQL 往返的成本(这些包括网络 I/O,因此以毫秒为单位)。通过这种方式,您可以轻松地将数千秒也就是几小时的执行时间添加到您的代码中。
  • 内存占用:所有 bean 对象。没办法。

选项 3 看起来也很有希望 - 但是您将 1.000.000 个额外的查找以及额外的 SQL 语句添加到您的代码中。

顺便说一句:如果你把bean1Map.put(bean1.getIdBean2(), bean1);和稍后在bean2循环中找到匹配的bean1并设置bean2,你只需要一个映射......

【讨论】:

  • 是的,我也有点喜欢这个解决方案。最终目标是在一个导出文件中显示来自两个 bean 的内容。所以在实践中,我需要内存中的两个bean。但即使把它拆开,我想知道什么是使这种可扩展性和可推广到其他 DAO 的良好做法。
  • 在这种情况下,您真的想沿着第 1 通道前进 - 并在遇到项目时导出它们,而不是用 all填满你的记忆> 对象(解决方案 #3),然后进行导出。
  • 嗯,是的,但是从概念的角度来看,将 DAO 链接到它们之间不是被认为是不好的做法吗?那个,加上前缀,感觉很
  • 创建对象来导出数据可能并不是 DAO 最强大的地方。关于前缀 - 见过 hibernate 获取一些链接对象的方法吗?看起来很糟糕,但效果很好。
【解决方案2】:

您没有说目标是内存效率、快速执行还是最少的编程。如果您正在寻找内存效率,我会使用选项 1。

您可以通过使用多个线程来缩短任何方法的执行时间。每个线程都需要选择要处理的行的子集。

如果您愿意使用其他库,我推荐我编写的 sormula。您可以使用ScalarSelectOperationSelectIterator 一次读取一个Bean1。 Sormula 将为 Bean2 执行级联选择。 Sormula 将只使用 2 条准备好的 JDBC 语句,一条用于 Bean1 表,一条用于 Bean2 表。

【讨论】:

  • 我对一般的良好做法很感兴趣。我正在寻找最准确的DAO设计,理论上应该如何编写。我将很快发布对帖子的答案,以解释我选择了什么以及为什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-30
  • 1970-01-01
  • 2018-11-10
  • 2013-07-31
  • 2021-08-04
  • 2014-07-01
  • 1970-01-01
相关资源
最近更新 更多