2012-11-08 20 views
5

Załóżmy, że mam obiekt z dwoma różnymi relacjami jeden-do-wielu. Podobnie jak:wiele relacji jeden-do-wielu ResultSetExtractor

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

I powiedzmy, że mój obiekt Customer ma dwie listy związanych z tymi dwoma obiektami.

Przeczytałem ten przykład: http://forum.springsource.org/showthread.php?50617-rowmapper-with-one-to-many-query , który wyjaśnia, jak to zrobić z pojedynczą relacją jeden do wielu. Dla wygody oto ResultSetExtractor przesłanianie:

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());; 
    } 
} 

Nie sądzę, że obejmuje, jak pracować z obu. Jakie byłoby najczystsze podejście? Czy istnieje prostszy sposób niż iterowanie z warunkami? Czy zestawy byłyby lepsze niż listy w tym przypadku?

+0

co struktura ma tabela mieć? – soulcheck

+0

To dziwna struktura, jest to dziedziczny proyect. Nie ma wyraźnych relacji, które zmusiły mnie do przejścia na jdbc w przeciwieństwie do standardowego ORM. Ale są też relacje zdefiniowane przez użytkownika, a klient może mieć wiele zamówień, klient może mieć wiele marek. Tak więc na przykład, jeśli użyłbym hibernacji, miałbym obiekt 'Customer' z 2 listami jako właściwości i przypisałbym je jako jeden do wielu, ale ponieważ używam prostego zapytania i dołączam myślę, że zajmie to dwa różne zapytania do zapełnienia listy obiektów 'Klient', ponieważ w przeciwnym razie zwróciłby pomieszany zestaw wyników. – Nimchip

+0

nono, po prostu powiedz, jakie tabele i kolumny masz w tym przypadku, a także czy istnieje mapowanie funkcjonalne od marek do zamówień i na odwrót, czy też są one całkowicie niezależne – soulcheck

Odpowiedz

19

Z twojego pytania zakładam, że masz trzy tabele; Klient, marki, zamówienia. Jeśli chcesz pobrać właściwości Marki i Zleceń klienta do obiektu klienta, gdzie nie ma związku między markami a zamówieniami, sugeruję użycie zapytania UNION.Coś takiego:

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 

Zapytanie:

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) 

Twój ResultSetExtractor staną:

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()); 
    } 
} 
+1

Oto, co zrozumiałem z tego pytania. Wciąż zastanawiam się, dlaczego nie rozdzielać zapytań i ekstraktorów zestawów wyników. Dzięki temu będą bardziej zrozumiałe. – tkr

+0

Dziękuję, właśnie tego szukałem. Zapomniałem o związkach pracujących z ORM i przyzwoicie znormalizowanych tabelach. – Nimchip

+0

@tkr co masz na myśli, oddzielając je? – Nimchip

1

Jeśli Naprawdę musiał to zrobić, wolałbym RowCallbackHandler nad ResultSetExtractor. Zobacz RowCallbackHandler api i JDBCTemplate api.

W takim przypadku należy samodzielnie zebrać wynikową kolekcję klientów w programie obsługi. Zestawy może pomóc odfiltrować duplikaty.

2

Myślę, że nie ma lepszego sposobu niż iterowanie po wszystkich wierszach, wyodrębnienie dwóch różnych obiektów i dodanie ich do obiektu List<Brand> i List<Order> w obiekcie klienta.

Więc skończyć się w obiekcie klienta:

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

Wystąpił problem na SpringSource dotycząca mutliple rowmapper: https://jira.springsource.org/browse/SPR-7698

ale jest tylko jeden komentarz z linkami do jeden-do-wielu ekstraktor zestaw wyników: https://github.com/SpringSource/spring-data-jdbc-ext/blob/master/spring-data-jdbc-core/src/main/java/org/springframework/data/jdbc/core/OneToManyResultSetExtractor.java

Myślę, że robisz to dobrze, jeśli naprawdę potrzebujesz gorliwego pobierania. Jeśli potrzebujesz leniwego pobierania, możesz załadować odpowiednie zamówienia i marki podczas dostępu w czasie wykonywania. Tak właśnie to robią Hibernate i inne struktury ORM. To zależy od twojego scenariusza i tego, co robisz z tym obiektem.

2

zakładam model opisany przez Jamesa Jithin w jego odpowiedź:

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 

Zamiast g oing dla jednego zapytania, chciałbym zaproponować następujących trzech kryteriów:

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 

Twoje RowCallbackHandlers staną:

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); 
      } 
    } 
} 
Powiązane problemy