【问题标题】:multiple one-to-many relations ResultSetExtractor多个一对多关系ResultSetExtractor
【发布时间】:2012-10-29 00:52:48
【问题描述】:

假设我有一个具有两个不同的一对多关系的对象。很像:

Customer 1<->M BrandsCustomer 1<->M Orders

假设我的对象Customer 有两个与这两个对象相关的列表。

我读过这个例子: http://forum.springsource.org/showthread.php?50617-rowmapper-with-one-to-many-query 它解释了如何使用单个一对多关系来做到这一点。为了您的方便,这里是 ResultSetExtractor 覆盖:

private class MyObjectExtractor implements ResultSetExtractor{

    public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
        Map<Integer, MyObject> map = new HashMap<Integer, MyObject>();
        MyObject myObject = null;
        while (rs.next()) {
            Integer id = rs.getInt("ID);
            myObject = map.get(id);
          if(myObject == null){
              String description = rs,getString("Description");
              myObject = new MyObject(id, description);
              map.put(id, myObject);
          }
      MyFoo foo = new MyFoo(rs.getString("Foo"), rs.getString("Bar"));
      myObject.add(myFoo);
        }
        return new ArrayList<MyObject>(map.values());;
    }
}

我不认为它涵盖了如何同时使用两者。什么是最干净的方法?有没有比条件迭代更简单的方法?在这种情况下,集合会比列表更好吗?

【问题讨论】:

  • 你的表有什么结构?
  • 这是一个奇怪的结构,这是一个继承的项目。没有明确的关系迫使我转向 jdbc,而不是标准的 ORM。但是有用户定义的关系,也就是一个客户可以有很多订单,一个客户可以有很多品牌。因此,例如,如果我使用休眠,我会将我的对象Customer 与 2 个列表作为属性并将它们注释为一对多,但由于我使用的是直接查询和连接,我认为这需要两个不同的查询来填充对象列表Customer,否则它会返回一个混乱的结果集。
  • nono,只需告诉您在这种情况下有哪些表和列,以及是否存在从品牌到订单的功能映射,反之亦然,或者它们是完全独立的
  • 好吧,客户有很多列(我真的是这个意思),并且有customer_idcustomer_account_number。这些是我用来分别加入订单和品牌的那些,尽管它们在数据库中没有标记为 FK,因此没有明确定义关系。是的,它是一个 db 的 cluster-f,但我没有办法解决这个问题。
  • 品牌和订单没有关系。这些是独立的。

标签: java spring jdbc jdbctemplate spring-jdbc


【解决方案1】:

如果我真的必须这样做,我会更喜欢 RowCallbackHandler 而不是 ResultSetExtractor。请参阅 RowCallbackHandler apiJDBCTemplate api

在这种情况下,您需要自己在处理程序中收集生成的客户集合。 Sets 可以帮助过滤掉重复项。

【讨论】:

    【解决方案2】:

    我认为没有比遍历所有行、提取两个不同的对象并将其添加到 Customer 对象中的 List&lt;Brand&gt;List&lt;Order&gt; 更好的方法了。

    所以你最终会成为一个客户对象:

    public class Customer {
         private List<Brand> brands;
         private List<Order> orders;
    ....
    }
    

    SpringSource 上有一个关于多个行映射器的问题:https://jira.springsource.org/browse/SPR-7698

    但只有一条评论链接到一对多结果集提取器: https://github.com/SpringSource/spring-data-jdbc-ext/blob/master/spring-data-jdbc-core/src/main/java/org/springframework/data/jdbc/core/OneToManyResultSetExtractor.java

    如果您真的需要急切获取,我认为您做对了。 如果您需要延迟获取,您可以在运行时加载相应的订单和品牌。这就是 Hibernate 和其他 ORM 框架的做法。这取决于您的场景以及您对对象的操作。

    【讨论】:

      【解决方案3】:

      根据您的问题,我假设您有三个表;客户、品牌、订单。如果您想将客户的品牌和订单属性获取到您的客户对象,品牌和订单之间没有关系,我建议使用 UNION 查询。像这样的:

      TBL_CUSTOMER
      ------------
      CUSTOMER_ID
      CUSTOMER_ACCOUNT_NO
      CUSTOMER_NAME
      
      TBL_CUSTOMER_BRANDS
      -------------------
      CUSTOMER_BRAND_ID            - UK
      BRAND_NAME
      CUSTOMER_ID                  - FK
      
      TBL_ORDERS
      -------------------
      ORDER_ID                     - UK
      CUSTOMER_ID                  - FK
      

      查询:

      SELECT CUS.*, BRANDS.CUSTOMER_BRAND_ID COL_A, BRANDS.BRAND_NAME COL_B, 1 IS_BRAND FROM TBL_CUSTOMER CUS JOIN TBL_CUSTOMER_BRANDS BRANDS ON (CUS.CUSTOMER_ID = BRANDS.CUSTOMER_ID)
      UNION ALL
      SELECT CUS.*, ORDERS.ORDER_ID, '', 0 IS_BRAND FROM TBL_CUSTOMER CUS JOIN TBL_ORDERS ORDERS ON (CUS.CUSTOMER_ID = ORDERS.CUSTOMER_ID)
      

      您的 ResultSetExtractor 将变为:

      private class MyObjectExtractor implements ResultSetExtractor{
      
          public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
                  Map<Long, Customer> map = new HashMap<Long, Customer>();
      
              while (rs.next()) {
                  Long id = rs.getLong("CUSTOMER_ID");
                  Customer customer = map.get(id);
                  if(customer == null){
                      customer = new Customer();
                      customer.setId(id);
                      customer.setName(rs.getString("CUSTOMER_NAME"));
                      customer.setAccountNumber(rs.getLong("CUSTOMER_ACCOUNT_NO"));
                      map.put(id, customer);
                          }
      
                  int type = rs.getInt("IS_BRAND");
                  if(type == 1) {
                      List brandList = customer.getBrands();
                      if(brandsList == null) {
                          brandsList = new ArrayList<Brand>();
                          customer.setBrands(brandsList);
                      }
                      Brand brand = new Brand();
                      brand.setId(rs.getLong("COL_A"));
                      brand.setName(rs.getString("COL_B"));
                      brandsList.add(brand);
                  } else if(type == 0) {
                      List ordersList = customer.getOrders();
                      if(ordersList == null) {
                          ordersList = new ArrayList<Order>();
                          customer.setOrders(ordersList);
                      }
                      Order order = new Order();
                      order.setId(rs.getLong("COL_A"));
                      ordersList.add(order);
                  }
              }
              return new ArrayList<Customer>(map.values());
          }
      }
      

      【讨论】:

      • 这是我从问题中了解到的。我仍然想知道为什么不将查询和结果集提取器分开。这将使它们更易于理解。
      • 谢谢,这正是我想要的。我完全忘记了使用 ORM 和规范化表的 Unions。
      • @tkr 你把它们分开是什么意思?
      • 评论有点大,请看下面我的回答。请注意,我这里没有 IDE,所以代码可能包含一些错误。我还按照 Vasyl 的建议使用了 RowCallbackHandler,因为它消除了调用 rs.next() 并返回对象的必要性。
      • 来自不可变对象爱好者的一点建议。而不是每次都检查brandList是否为空->在Customer类中创建一个所有字段构造函数并在那里初始化brandList。此外,我更喜欢使用构造函数初始化我的 Customer 对象,而不是分别设置每个字段(个人偏好)。
      【解决方案4】:

      我假设 James Jithin 在他的回答中描述的模型:

      TBL_CUSTOMER
      ------------
      CUSTOMER_ID
      CUSTOMER_ACCOUNT_NO
      CUSTOMER_NAME
      
      TBL_CUSTOMER_BRANDS
      -------------------
      CUSTOMER_BRAND_ID            - UK
      BRAND_NAME
      CUSTOMER_ID                  - FK
      
      TBL_ORDERS
      -------------------
      ORDER_ID                     - UK
      CUSTOMER_ID                  - FK
      

      我建议以下三个,而不是一个查询:

      SELECT CUS.* FROM TBL_CUSTOMER CUS 
      
      SELECT BRANDS.CUSTOMER_ID, BRANDS.CUSTOMER_BRAND_ID, BRANDS.BRAND_NAME FROM TBL_CUSTOMER_BRANDS BRANDS
      
      SELECT ORDERS.CUSTOMER_ID, ORDERS.ORDER_ID FROM TBL_ORDERS ORDERS 
      

      您的 RowCallbackHandlers 将变为:

      private class CustomerRowCallbackHandler  implements RowCallbackHandler {
      
          private final Map<Long, Customer> customerMap;
      
          public BrandRowCallbackHandler(Map<Long, Customer> customerMap) { this.customerMap = customerMap}
      
          public void processRow(ResultSet rs) throws SQLException {
                  Long id = rs.getLong("CUSTOMER_ID");
                  Customer customer = map.get(id);
                  if(customer == null){
                      customer = new Customer();
                      customer.setId(id);
                      customer.setName(rs.getString("CUSTOMER_NAME"));
                      customer.setAccountNumber(rs.getLong("CUSTOMER_ACCOUNT_NO"));
                      map.put(id, customer);
                          }
          }
      }
      
      private class BrandRowCallbackHandler implements RowCallbackHandler {
      
          private final Map<Long, Customer> customerMap;
      
          public BrandRowCallbackHandler(Map<Long, Customer> customerMap) { this.customerMap = customerMap}
      
          public void processRow(ResultSet rs) throws SQLException {
                  Long id = rs.getLong("CUSTOMER_ID");
                  Customer customer = map.get(id);
                  if(customer != null){
                      List brandList = customer.getBrands();
                      if(brandsList == null) {
                          brandsList = new ArrayList<Brand>();
                          customer.setBrands(brandsList);
                      }
                      Brand brand = new Brand();
                      brand.setId(rs.getLong("CUSTOMER_BRAND_ID"));
                      brand.setName(rs.getString("CUSTOMER_BRAND_NAME"));
                      brandsList.add(brand);
                  } 
          }
      }
      
      private class OrderRowCallbackHandler implements RowCallbackHandler {
      
          private final Map<Long, Customer> customerMap;
      
          public OrderRowCallbackHandler(Map<Long, Customer> customerMap) { this.customerMap = customerMap}
      
          public void processRow(ResultSet rs) throws SQLException {
                  Long id = rs.getLong("CUSTOMER_ID");
                  Customer customer = map.get(id);
                  if(customer != null){
                      List ordersList = customer.getOrders();
                      if(ordersList == null) {
                          ordersList = new ArrayList<Order>();
                          customer.setOrders(ordersList);
                      }
                      Order order = new Order();
                      order.setId(rs.getLong("ORDER_ID"));
                      ordersList.add(order);
                  }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-06-15
        • 2019-11-07
        • 2023-03-04
        • 1970-01-01
        • 1970-01-01
        • 2015-09-20
        • 2018-03-16
        相关资源
        最近更新 更多