2012-11-08 5 views
5

Disons que j'ai un objet avec deux relations un-à-plusieurs différentes. Tout comme:plusieurs relations un-à-plusieurs ResultSetExtractor

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

Et disons que l'objet mon Customer a deux listes liées à ces deux objets.

J'ai lu cet exemple: http://forum.springsource.org/showthread.php?50617-rowmapper-with-one-to-many-query qui explique comment le faire avec une seule relation un-à-plusieurs. Pour votre commodité, voici la ResultSetExtractor 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());; 
    } 
} 

Je ne pense pas qu'il porte sur la façon de travailler avec les deux. Quelle serait l'approche la plus propre? Existe-t-il un moyen plus simple que d'itérer avec des conditions? Les ensembles seraient-ils mieux lotis que les listes dans ce cas?

+0

quelle structure que votre table ont? – soulcheck

+0

c'est une structure bizarre, c'est un projet hérité. Il n'y a pas de relations explicites qui m'ont forcé à passer à jdbc par opposition à un ORM standard. Mais il existe des relations définies par l'utilisateur, un client pourrait avoir de nombreuses commandes, un client pourrait avoir de nombreuses marques. Donc, par exemple, si j'utilisais hibernate, j'aurais mon objet 'Customer' avec 2 listes comme propriétés et je les annoterais de un à plusieurs, mais puisque j'utilise une requête directe et que je me joins, je pense qu'il faudrait deux différentes requêtes pour remplir une liste d'objet 'Customer', car sinon, cela retournerait un resultset brouillé. – Nimchip

+0

nono, il suffit de dire quelles tables et colonnes vous avez dans ce cas et aussi s'il y a un mappage fonctionnel des marques aux commandes et vice versa ou sont-ils totalement indépendants – soulcheck

Répondre

19

De votre question, je suppose que vous avez trois tables; Client, marques, commandes. Si vous voulez récupérer les propriétés Marques et Commandes du Client dans votre objet client, s'il n'y a pas de relation entre Marques et Commandes, je suggère d'utiliser une requête UNION.Quelque chose comme ceci:

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 

Requête:

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) 

Votre ResultSetExtractor deviendra:

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

C'est ce que j'ai compris de la question. Je me demande encore pourquoi ne pas séparer les requêtes et les extracteurs de résultats. Cela les rendrait beaucoup plus compréhensibles. – tkr

+0

Merci, c'est exactement ce que je cherchais. J'ai oublié tout sur les syndicats travaillant avec les ORM et les tables correctement normalisées. – Nimchip

+0

@tkr que voulez-vous dire en les séparant? – Nimchip

1

Si je vraiment devais le faire, je préférerais RowCallbackHandler sur ResultSetExtractor. Voir RowCallbackHandler api et JDBCTemplate api.

Dans ce cas, vous devez collecter vous-même la collection des clients dans le gestionnaire. Les ensembles peuvent aider à filtrer les doublons.

2

Je pense qu'il n'y a pas de meilleur moyen que de parcourir toutes les lignes, d'extraire les deux objets différents et de l'ajouter à List<Brand> et List<Order> dans l'objet Client.

Vous finiriez dans un objet client:

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

Il y avait un problème sur SpringSource au sujet d'une RowMapper Mutliple: https://jira.springsource.org/browse/SPR-7698

mais il n'y a qu'un seul commentaire relier à un à plusieurs extracteur resultset: https://github.com/SpringSource/spring-data-jdbc-ext/blob/master/spring-data-jdbc-core/src/main/java/org/springframework/data/jdbc/core/OneToManyResultSetExtractor.java

Je pense que vous le faites bien si vous avez vraiment besoin d'aller chercher ardemment. Si vous avez besoin d'un chargement paresseux, vous pouvez charger les ordres et marques respectifs lors de l'accès pendant l'exécution. C'est ainsi que Hibernate et les autres frameworks ORM le font. Cela dépend de votre scénario et de ce que vous faites avec l'objet.

2

Je suppose que le modèle décrit par James Jithin dans sa réponse:

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 

Au lieu de g oing pour une requête, je suggère les trois suivants:

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 

Votre RowCallbackHandlers deviendrait:

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); 
      } 
    } 
} 
Questions connexes