multiple one-to-many relations ResultSetExtractor

后端 未结 4 2008
余生分开走
余生分开走 2020-12-08 05:46

Let\'s say I have an object with two different one-to-many relations. Much like:

Customer 1<->M Brands and Customer 1<->M Orders

相关标签:
4条回答
  • 2020-12-08 06:34

    I assume the model described by James Jithin in his answer:

    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
    

    Instead of going for one Query, I would suggest the following three:

    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 
    

    Your RowCallbackHandlers would become:

    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);
                }
        }
    }
    
    0 讨论(0)
  • 2020-12-08 06:37

    From your question, I assume that you have three tables; Customer, Brands, Orders. If you want to fetch the Brands and Orders properties of the Customer to your customer object, where there is no relationship between Brands and Orders, what I suggest is to use a UNION query. Something like this:

    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
    

    Query:

    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)
    

    Your ResultSetExtractor will become:

    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());
        }
    }
    
    0 讨论(0)
  • 2020-12-08 06:40

    I think there is no better way than to iterate over all rows, extract the two different objects and add it to a List<Brand> and List<Order> within the Customer object.

    So you would end up in a customer object:

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

    There was an issue on SpringSource regarding a mutliple rowmapper: https://jira.springsource.org/browse/SPR-7698

    but there's only one comment linking to a one-to-many resultset extractor: https://github.com/SpringSource/spring-data-jdbc-ext/blob/master/spring-data-jdbc-core/src/main/java/org/springframework/data/jdbc/core/OneToManyResultSetExtractor.java

    I think you're doing it right if you really need eager fetching. If you'd need lazy fetching you could load the respective orders and brands on access during runtime. That's how Hibernate and other ORM frameworks do it. It depends on your scenario and what you do with the object.

    0 讨论(0)
  • 2020-12-08 06:42

    If I really had to do it, I would prefer RowCallbackHandler over ResultSetExtractor. See RowCallbackHandler api and JDBCTemplate api.

    In this case you need to collect the resulting Customers collection yourself in the handler. Sets can help to filter out duplicates.

    0 讨论(0)
提交回复
热议问题