【Java】网上商城 两层加载(由数据库中的id转换成java中的对象)+两层循环

本文介绍了订单系统的架构设计,包括实体类定义与数据库表结构。详细解析了如何通过两层加载方式实现从数据库中获取用户订单及其包含的商品详情。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.环境:

1.1首先有三个实体类

Order.java:

package com.tencent.bookstore.order.domain;

import java.util.Date;
import java.util.List;

import com.tencent.bookstore.user.domain.User;

/**
 * 订单类           
 * @author TypantK
 *
 */
public class Order {
	private String oid;
	private Date ordertime;
	private double total;
	private int state;	//四种订单状态:1未付款 2已付款未发货 3已发货未确认 4已确认
	private User owner;	//订单所有者
	private String address;
	
	private List<OrderItem> orderItemList;	//当前订单下所有条目
	
	//省略get/set等方法
	
}

OrderItem.java

package com.tencent.bookstore.order.domain;

import java.util.List;

import com.tencent.bookstore.book.domain.Book;

/**
 * 订单条目
 * @author TypantK
 *
 */
public class OrderItem {
	private String iid;
	private int count;
	private double subtotal;
	private Order order;	//所属订单
	private Book book;	//所购买的书
	
	
 //省略get/set等方法
	
	
}

Book.java

package com.tencent.bookstore.book.domain;

import com.tencent.bookstore.category.domain.Category;

public class Book {
	private String bid;
	private String bname;
	private double price;
	private String author;
	private String image;
	private Category category;
	

    //省略get/set等方法
}

1.2数据库表架构设计

2.实现:

在Dao层写一个方法,通过传入的uid(用户的编号,上面类没写)来查询出当前用户所有的orders(订单)

因为要给orders对象的orderItemList(即订单中的条目)赋值,所以这里需要第一层加载

public List<Order> findByUid(String uid) {
		/*
		 * 通过uid查询出当前用户的所有order
		 * 循环遍历每个order 为其加载所有的orderitem
		 */
		try {
			String sql = "select * from orders where uid=?";
			List<Order> orderList = qr.query(sql, new BeanListHandler<Order>(Order.class), uid);
			
			for(Order order : orderList) {    //一个用户有多个订单
				loadOrderItems(order);
			}
			return orderList;
		}catch(SQLException e) {
			
			throw new RuntimeException(e);
		}
		
	}

而要给List里面的每个orderItem赋值,就要给orderItem里面的每个Book类赋一个对象,所以这里需要第二层加载

/**
	 * 加载指定订单所有的订单条目
	 * @param order
	 */
	private void loadOrderItems(Order order) throws SQLException{
		/*
		 * 要查询两张表:orderitem和book(因为orderitem里面有bid,要用到book表里面的信息)
		 */
		String sql = "select * from orderitem i, book b where i.bid=b.bid and oid=?";
		/*
		 * 因为一行结果集对应的不再是一个javabean(OrderItem+Book),所以不能再使用BeanListHandler
		 * 而是使用MapListHandler
		 * mapList是多个map,每个map对应一行结果集
		 */
		List<Map<String,Object>> mapList = qr.query(sql, new MapListHandler(), order.getOid());
		/*
		 * 用map生成两个对象:OrderItem Book 然后建立两者的关系(将Book设置给OrderItem
		 */
		List<OrderItem> orderItemList = toOrderItemList(mapList);
		order.setOrderItemList(orderItemList);
	}

/**
	 * 将mapList中每个map转换成两个对象并建立关系
	 * @param mapList
	 * @return
	 */
	private List<OrderItem> toOrderItemList(List<Map<String, Object>> mapList) {
		
		List<OrderItem> orderItemList = new ArrayList<OrderItem>();	//用来装已经加载好Book类的orderItem的集合
		for(Map<String,Object> map : mapList) {
			OrderItem item = toOrderItem(map);
			orderItemList.add(item);
		}
		
		return orderItemList;
	}
	
	/**
	 * 把一个Map转化成一个OrderItem对象和Book对象,并将Book对象赋值给OrderItem对象(即建立关系)
	 */
	private OrderItem toOrderItem(Map<String, Object> map) {
		OrderItem orderItem = CommonUtils.toBean(map, OrderItem.class);
		Book book = CommonUtils.toBean(map, Book.class);
		orderItem.setBook(book);
		return orderItem;
	}


/**
	 * 把map转换成对象
	 * @param map
	 * @param clazz
	 * @return
	 * 
	 * 把Map转换成指定类型
	 */
	@SuppressWarnings("rawtypes")
	public static <T> T toBean(Map map, Class<T> clazz) {
		try {
			/*
			 * 1. 通过参数clazz创建实例
			 * 2. 使用BeanUtils.populate把map的数据封闭到bean中
			 */
			T bean = clazz.newInstance();
			ConvertUtils.register(new DateConverter(), java.util.Date.class);
			BeanUtils.populate(bean, map);
			return bean;
		} catch(Exception e) {
			throw new RuntimeException(e);
		}
	}

3.总结:

主要难点就是 加载+循环 之间的交融

第一层循环:遍历指定用户的所有order(订单)

for(Order order : orderList) {    //一个用户有多个订单
				loadOrderItems(order);
			}

 

第二层循环:遍历指定order的所有orderItem(订单条目)

for(Map<String,Object> map : mapList) {    //一个订单有多个订单项
			OrderItem item = toOrderItem(map);
			orderItemList.add(item);
		}

 

第一层加载:给每一个order的orderItemList赋对象,通过每一次的第二层循环来对每一个orderItem赋对象

 

第二层加载:给每一个orderItem的book赋对象

<think>我们正在处理一个Java编程问题,用户希望实现一个功能:在点击筛选数据时,如果当前页的筛选结果不足10条,则自动加载下一页并继续应用相同的筛选条件,直到满足10条数据或没有更多数据为止。 思路: 1. 我们需要一个方法,该方法接受筛选条件作为输入,并返回至少10条满足条件的数据(如果存在的话)。 2. 该方法应该从第一页开始查询,然后逐页查询,直到累积到至少10条满足条件的数据,或者没有更多数据。 3. 每次查询一页数据(比如每页固定大小,例如10条),然后在这一页数据中应用筛选条件,将满足条件的记录加入结果集。 4. 如果当前页筛选后,结果集达到10条,则停止;否则,继续查询下一页。 5. 注意:为了避免无限循环,需要有一个终止条件,即当查询到一页数据为空(表示没有更多数据)时终止。 假设我们有一个数据访问层方法,用于分页查询数据(不带筛选条件,因为筛选条件我们将在内存中应用?或者如果可能的话,最好在数据库层进行筛选,这样效率更高。但用户需求似乎是在点击筛选时触发,可能筛选条件由用户输入,且可能每次点击筛选条件不同,因此我们可能需要将整个数据按页加载到内存中然后筛选?但这样效率可能很低,尤其是数据量大的时候。) 然而,用户的问题描述中并没有明确数据源是什么。我们假设数据源是数据库,那么更高效的做法是在数据库层进行筛选,然后分页查询直到满足10条。 但是,用户要求是“点击筛选数据”,可能意味着筛选条件是在前端选择的,然后传递给后端。因此,我们可以在数据库查询中使用这些筛选条件。 因此,我们有两种情况: 情况A:在内存中筛选(如果数据量不大,或者已经加载到内存中) 情况B:在数据库查询中筛选(推荐,效率高) 由于问题中没有明确,我们分别考虑: 方案A(内存筛选): 步骤: 1. 定义一个结果列表(result)用于存放满足条件的记录。 2. 设置当前页码(page)为0(或1,根据你的分页规则),每页大小(pageSize)可以设为10(或者更大,但至少10条)。 3. 循环直到结果集大小>=10或者没有更多数据: a. 查询下一页数据(不带筛选条件,因为我们在内存中筛选),得到一页的数据列表(currentPageData)。 b. 如果currentPageData为空,则跳出循环。 c. 遍历currentPageData,对每条数据应用筛选条件,如果满足条件,则加入结果集。 d. 如果结果集大小达到10,则跳出循环。 4. 返回结果集的前10条(或者如果要求最多10条,那么返回结果集即可,因为结果集最多10条)。 方案A的缺点:如果数据量很大,且满足条件的记录很少,我们可能需要查询很多页才能找到10条,甚至可能加载全部数据,效率低。 方案B(数据库筛选): 步骤: 1. 定义一个结果列表(result)用于存放满足条件的记录。 2. 设置当前页码(page)从0开始(或1),每页大小(pageSize)为10(注意:这里每页查询10条,但满足条件的可能不足10条,所以我们需要在数据库查询时应用筛选条件,然后返回筛选后的数据,并且我们要求每次查询一页(筛选后的)数据,但这样分页就不连续了,因为筛选条件改变了记录的顺序和分布?) 实际上,在数据库筛选中,我们无法直接知道满足条件的记录在原始数据中的分布情况。因此,我们需要在数据库中进行分页查询,并且每次查询都使用相同的筛选条件。 具体步骤: 1. 设置偏移量(offset)为0,每页大小(pageSize)为10(注意:这里每页查询10条满足条件的数据,但这样我们只需要查询一页,因为一页就10条了。但用户要求是:如果当前页筛选结果不足10条,则加载下一页。这里的“页”指的是原始数据的分页,还是筛选后结果的分页?) 用户需求:当筛选结果不足10条时自动加载下一页(原始数据的下一页)并继续过滤。这意味着用户是按照原始数据的分页来一页一页加载的,然后在每一页中筛选。所以,这里的分页是指原始数据的分页。 因此,方案A更符合用户的描述。但是,如果数据量很大,方案A效率很低。 我们重新理解需求:用户希望在前端点击筛选按钮后,后端返回至少10条满足条件的数据(如果存在的话)。为了实现这个功能,后端需要从第一页开始查询原始数据(每页固定大小,比如pageSize=10),然后对每一页的数据应用筛选条件,直到累积到10条满足条件的数据,或者遍历完所有数据。 所以,我们采用方案A,但注意:这里的分页查询原始数据,每页的大小(pageSize)可以设置为一个固定值,比如10(或者也可以设置大一些,比如50,以减少查询次数,但用户没有说明,我们假设每页10条原始数据)。 但是,如果每页原始数据只有10条,但满足条件的可能很少,甚至没有,那么我们就需要查询很多页。因此,我们可以适当增加每页原始数据的大小,比如100条,这样一次查询可以检查更多数据,减少数据库查询次数。 因此,我们可以在后端设计一个方法,参数为筛选条件,返回至少10条满足条件的数据(如果存在)。 伪代码: 方法:List<Data> loadDataWithFilter(Filter filter, int minCount) List<Data> result = new ArrayList<>(); int page = 0; int pageSize = 50; // 每次从数据库查询50条原始数据 while (result.size() < minCount) { // 查询第page页的数据 List<Data> pageData = dataService.getByPage(page, pageSize); if (pageData.isEmpty()) { break; // 没有更多数据了 } // 对当前页的数据应用筛选 for (Data data : pageData) { if (filter.satisfy(data)) { result.add(data); // 如果已经达到minCount,提前退出循环 if (result.size() >= minCount) { break; } } } page++; // 下一页 } return result; 注意:minCount就是10。 但是,这里有一个问题:我们每次查询下一页,直到满足10条。但是,如果数据量很大,满足条件的记录很少,我们可能需要查询很多页,这样效率低。因此,我们可以在数据库层进行筛选,然后分页查询满足条件的记录,直到满足10条。这样,每次查询我们只取满足条件的记录,并且每次查询的页是筛选后的页(而不是原始数据的页)。 方案C(数据库筛选,且分页查询筛选后的结果): 步骤: 1. 第一次查询:查询满足条件的第一页数据(每页10条),如果返回的记录数等于10,则直接返回。 2. 如果返回的记录数小于10,则查询第二页(筛选后的第二页),将结果追加到结果集,直到结果集达到10条或者没有更多满足条件的记录。 伪代码: 方法:List<Data> loadDataWithFilter(Filter filter, int minCount) List<Data> result = new ArrayList<>(); int page = 0; int pageSize = minCount; // 每页查询minCount条(满足条件的) while (result.size() < minCount) { // 查询满足条件的第page页,每页pageSize条(注意:这里pageSize是满足条件的记录数,我们要求每页返回minCount条,但实际可能不足) List<Data> pageData = dataService.getByPageWithFilter(page, pageSize, filter); result.addAll(pageData); if (pageData.size() < pageSize) { // 说明已经到最后一页了,没有更多数据了 break; } page++; } // 注意:我们可能收集了超过minCount条,但我们只需要minCount条 if (result.size() > minCount) { result = result.subList(0, minCount); } return result; 方案C的优点:数据库查询次数少(每次查询10条满足条件的记录,最多查询两次:第一页10条就够了,如果第一页只有5条,再查第二页5条就够了)。但是,这里有一个问题:我们要求每页返回10条满足条件的记录,但数据库查询中,分页查询满足条件的记录,第一页我们查询0-10条,第二页查询10-20条,但用户要求的是“当筛选结果不足10条时自动加载下一页”,这里的“下一页”指的是原始数据的下一页,还是筛选后结果的下一页? 用户需求:当筛选结果不足10条时自动加载下一页(原始数据的下一页)并继续过滤。所以,用户要求的是原始数据的下一页。因此,方案C不符合用户要求,因为方案C加载的是筛选后结果的下一页。 但是,从效率角度,方案C更优。我们需要和用户确认,但这里我们按照用户描述实现(方案A)。 然而,用户问题中并没有明确数据源是数据库还是内存,我们假设是数据库,并且有一个分页查询方法(查询原始数据的分页)。 因此,我们按照方案A实现。 代码示例(Java): 假设我们有一个DataService,它有一个方法:List<Data> getDataByPage(int page, int pageSize),用于分页获取原始数据。 筛选条件我们用一个Filter接口表示,该接口有一个方法boolean satisfy(Data data)。 实现: 首先,定义筛选条件接口: ``` @FunctionalInterface public interface Filter { boolean satisfy(Data data); } ``` 然后,实现加载方法: ``` public List<Data> loadDataWithFilter(Filter filter, int minCount) { List<Data> result = new ArrayList<>(); int page = 0; int pageSize = 50; // 每次从数据库查询50条原始数据,这个值可以根据实际情况调整 while (result.size() < minCount) { List<Data> pageData = dataService.getDataByPage(page, pageSize); if (pageData.isEmpty()) { break; } for (Data data : pageData) { if (filter.satisfy(data)) { result.add(data); if (result.size() >= minCount) { break; } } } page++; } return result; } ``` 注意:这里每次查询一页原始数据(pageSize=50),然后逐条应用筛选条件,直到收集到minCount(10)条满足条件的数据,或者没有更多数据。 调用示例: 假设用户点击筛选,传递一个筛选条件(比如,数据的状态为“active”),那么我们可以这样调用: ``` List<Data> filteredData = loadDataWithFilter(data -> "active".equals(data.getStatus()), 10); ``` 这样,filteredData就是满足条件的至少10条数据(如果存在的话),如果不足10条,则返回所有满足条件的数据。 但是,这里有一个性能问题:如果数据量很大,而且满足条件的记录很少,我们可能需要查询很多页原始数据(每次50条)才能凑够10条,这样数据库查询次数多,且传输的数据量大。 优化方向: 1. 如果可能,将筛选条件传递给数据库,在数据库中进行筛选,然后我们只需要分页查询满足条件的记录(方案C)。但是,这需要改变分页查询的方式,而且用户需求是“原始数据的下一页”,所以不符合。 2. 增加每页原始数据的查询量(pageSize),比如设置为200或500,这样每次查询的数据量增大,减少查询次数,但每次传输的数据量增大,需要权衡。 3. 如果筛选条件可以转换为数据库查询条件,那么我们可以采用方案C,并且修改需求:不再要求原始数据的下一页,而是要求满足条件的记录的下一页。这需要和产品经理确认。 考虑到用户需求描述,我们按照方案A实现,但需要提醒用户注意性能问题。 另外,我们还需要注意:如果数据源是数据库,并且数据量很大,频繁的分页查询(特别是深分页)对数据库压力很大。例如,MySQL中使用LIMIT offset, pageSize,当offset很大时,效率很低。 因此,我们可以考虑使用更高效的分页方式,比如基于游标的分页(例如使用id>lastId ORDER BY id LIMIT pageSize),但需要数据有自增ID或其他唯一有序字段。 假设我们的数据有自增ID,我们可以修改分页查询方法,使用游标分页: 伪代码(使用游标分页): ``` public List<Data> loadDataWithFilter(Filter filter, int minCount) { List<Data> result = new ArrayList<>(); int pageSize = 50; Long lastId = null; // 游标,记录上一页最后一条记录的ID while (result.size() < minCount) { List<Data> pageData = dataService.getDataByPageAfterId(lastId, pageSize); if (pageData.isEmpty()) { break; } // 更新lastId为当前页最后一条记录的ID lastId = pageData.get(pageData.size() - 1).getId(); for (Data data : pageData) { if (filter.satisfy(data)) { result.add(data); if (result.size() >= minCount) { break; } } } } return result; } ``` 其中,getDataByPageAfterId方法实现类似:SELECT * FROM table WHERE id > ? ORDER BY id LIMIT pageSize 这样,分页效率会提高。 综上所述,我们给出两种实现方式(普通分页和游标分页)的代码,并提醒用户根据数据量和性能要求选择。 相关问题: 1. 如何优化这种分页筛选的性能? 2. 如果筛选条件复杂,能否在数据库查询中实现? 3. 游标分页和传统分页(使用offset)有什么区别? 4. 如何避免在内存中筛选大量数据? 5. 如果数据源不是数据库,而是其他服务接口,如何实现?</think>### Java实现点击筛选数据自动加载下一页 在Java中实现点击筛选数据并在结果不足10条时自动加载下一页,直到满足数量或无更多数据,可以按照以下步骤实现: #### 核心实现思路 1. **分页查询**:使用分页参数(页码、页大小)查询数据 2. **内存筛选**:在内存中应用筛选条件过滤数据 3. **结果累积**:当筛选结果不足10条时自动加载下一页 4. **终止条件**:满足10条数据或数据源耗尽 #### 示例代码实现 ```java import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; public class PaginatedFilter { private final DataService dataService; // 数据服务接口 private final int minCount = 10; // 最小需求数量 public List<Data> filterWithPagination(Predicate<Data> filterCondition) { List<Data> result = new ArrayList<>(); int page = 0; boolean hasMore = true; while (result.size() < minCount && hasMore) { // 分页查询数据(每页建议10-20条) List<Data> pageData = dataService.fetchPage(page, 15); if (pageData.isEmpty()) { hasMore = false; // 无更多数据 continue; } // 应用筛选条件 for (Data item : pageData) { if (filterCondition.test(item)) { result.add(item); // 达到最小数量即停止 if (result.size() >= minCount) break; } } page++; // 准备加载下一页 } return result; } } // 辅助类定义 interface DataService { List<Data> fetchPage(int page, int size); // 分页查询方法 } class Data { // 数据模型定义(根据实际业务调整) private String field1; private int field2; // getters/setters... } ``` #### 关键实现说明 1. **分页查询**: - 使用`fetchPage(page, size)`获取分页数据 - 每页大小建议设置为10-20条(平衡性能与效率) - 数据库实现参考子查询分页方案[^1] 2. **内存筛选**: - 使用`Predicate<Data>`函数式接口定义筛选条件 - 示例筛选条件: ```java // 筛选field1包含"重要"且field2>100的数据 Predicate<Data> filter = data -> data.getField1().contains("重要") && data.getField2() > 100; ``` 3. **终止条件**: - 结果集达到10条 - 或数据源返回空页(无更多数据) #### 性能优化建议 1. **数据库层筛选**: ```java // 在SQL中直接应用筛选条件(推荐) String sql = "SELECT * FROM table WHERE condition LIMIT ?, ?"; ``` 这比内存筛选更高效,避免加载不必要的数据[^1] 2. **游标分页优化**: - 使用`WHERE id > lastId`替代传统分页 - 避免深分页性能问题 3. **批量处理**: - 适当增大每页大小(如50条) - 但需平衡内存占用和网络开销 4. **异步加载**: ```java CompletableFuture.supplyAsync(() -> fetchPage(page, size)) ``` 避免UI线程阻塞 #### 注意事项 1. 当数据源来自外部API时,需处理限流和重试机制 2. 大数据场景建议结合布隆过滤器预筛选[^3] 3. 空值处理需与数据源约定统一标准(如NULL转空串)[^2] ### 处理流程示意图 ``` 开始筛选 ↓ 加载第1页 → 应用筛选 → 结果≥10条? → 是 → 返回结果 ↓否 加载第2页 → 应用筛选 → 结果≥10条? → 是 → 返回结果 ↓否 ...(循环直到满足条件或无数据) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值