假设我有一个具有两个不同的一对多关系的对象。很像:
Customer 1<->M Brands
和Customer 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());;
}
}
我认为它不包括如何使用它们。什么是最干净的方法?有没有比条件迭代更简单的方法?在这种情况下,套装会比列表更好吗?
答案 0 :(得分:21)
从你的问题来看,我假设你有三张桌子;客户,品牌,订单。如果您想要将客户的品牌和订单属性提取到客户对象,而品牌和订单之间没有关系,我建议使用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());
}
}
答案 1 :(得分:2)
我认为没有比迭代所有行更好的方法,提取两个不同的对象并将其添加到Customer对象中的List<Brand>
和List<Order>
。
所以你最终会成为客户对象:
public class Customer {
private List<Brand> brands;
private List<Order> orders;
....
}
SpringSource上有关于多行rowmapper的问题: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框架的作用。这取决于您的场景以及您对该对象的处理方式。
答案 2 :(得分:2)
我假设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);
}
}
}
答案 3 :(得分:1)
如果我真的必须这样做,我希望 RowCallbackHandler 而不是 ResultSetExtractor 。请参阅RowCallbackHandler api和JDBCTemplate api。
在这种情况下,您需要自己在处理程序中收集生成的Customers集合。 集可以帮助过滤掉重复项。