2012-11-08 11 views
5

Diciamo che ho un oggetto con due diverse relazioni uno-a-molti. Molto simile:più relazioni uno-a-molti ResultSetExtractor

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

E diciamo che il mio oggetto Customer ha due elenchi relativi a questi due oggetti.

Ho letto questo esempio: http://forum.springsource.org/showthread.php?50617-rowmapper-with-one-to-many-query che spiega come farlo con una singola relazione uno-a-molti. Per la vostra comodità ecco la ResultSetExtractor di override:

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

Io non credo che si estende su come lavorare con entrambi. Quale sarebbe l'approccio più pulito? C'è un modo più semplice rispetto a iterare con le condizioni? Gli insiemi sarebbero meglio degli elenchi in questo caso?

+0

quale struttura funziona il vostro tavolo ha? – soulcheck

+0

è una struttura strana, questo è un progetto ereditato. Non ci sono relazioni esplicite che mi hanno costretto a passare a jdbc in opposizione a un ORM standard. Ma ci sono relazioni definite dall'utente, ovvero un cliente potrebbe avere molti ordini, un cliente potrebbe avere molte marche. Quindi, per esempio, se usassi l'ibernazione, avrei il mio oggetto 'Cliente' con 2 elenchi come proprietà e li annoterò come uno a molti, ma dal momento che sto usando una query diretta e join, penso che ci vorrebbero due query diverse per popolare un elenco di oggetti 'Cliente', perché altrimenti restituirebbe un set di risultati confuso. – Nimchip

+0

nono, dì solo quali tabelle e colonne hai in questo caso e anche se c'è una mappatura funzionale da marche ad ordini e viceversa o sono totalmente indipendenti – soulcheck

risposta

19

Dalla tua domanda, presumo che tu abbia tre tabelle; Cliente, Marchi, Ordini. Se si desidera recuperare le proprietà dei marchi e degli ordini del cliente sull'oggetto cliente, in assenza di relazioni tra marche e ordini, è consigliabile utilizzare una query UNION.Qualcosa di simile a questo:

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) 

tuo ResultSetExtractor diventerà:

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

Questo è ciò che ho capito dalla domanda. Mi chiedo ancora perché non separare le query e gli estrattori di risultati. Li renderebbe molto più comprensibili. – tkr

+0

Grazie, questo è esattamente quello che stavo cercando. Ho dimenticato tutto sui sindacati che lavorano con ORM e tabelle decentemente normalizzate. – Nimchip

+0

@ tkr cosa intendi separandoli? – Nimchip

1

Se davvero dovuto farlo, preferirei RowCallbackHandler oltre ResultSetExtractor. Vedi RowCallbackHandler api e JDBCTemplate api.

In questo caso è necessario raccogliere la raccolta Clienti risultante nel gestore. Set può aiutare a filtrare i duplicati.

2

Penso che non ci sia un modo migliore che iterare su tutte le righe, estrarre i due diversi oggetti e aggiungerlo a un List<Brand> e List<Order> all'interno dell'oggetto Cliente.

Così si finirebbe in un oggetto cliente:

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

C'è stato un problema su SpringSource per quanto riguarda un rowmapper mutliple: https://jira.springsource.org/browse/SPR-7698

ma c'è solo un commento collegamento a un uno-a-molti estrattore di risultati: https://github.com/SpringSource/spring-data-jdbc-ext/blob/master/spring-data-jdbc-core/src/main/java/org/springframework/data/jdbc/core/OneToManyResultSetExtractor.java

Penso che lo stai facendo bene se hai davvero bisogno di andare a prendere. Se è necessario il recupero pigro, è possibile caricare gli ordini e le marche in accesso durante il runtime. Ecco come Hibernate e altri framework ORM lo fanno. Dipende dal tuo scenario e da cosa fai con l'oggetto.

2

Suppongo che il modello descritto da James Jithin nella sua risposta:

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 

Invece di g oing per una query, vorrei suggerire i seguenti tre:

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 

vostri RowCallbackHandlers diventerebbero:

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); 
      } 
    } 
} 
Problemi correlati