本文目录导读:

在Java中实现一对多查询(一个用户对应多个订单,一个班级对应多个学生)主要有以下几种方式,我按照从简单到复杂的顺序为你介绍:
SQL层实现(最基础)
表结构设计
-- 用户表
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(50)
);
-- 订单表(外键关联用户)
CREATE TABLE order (
id INT PRIMARY KEY,
user_id INT,
order_no VARCHAR(50),
FOREIGN KEY (user_id) REFERENCES user(id)
);
查询方式
两次查询(简单明了)
// 1. 先查询用户 User user = userMapper.getUserById(1); // 2. 再查询该用户的订单 List<Order> orders = orderMapper.getOrdersByUserId(user.getId()); user.setOrders(orders);
一次查询(JOIN + 手动处理)
// SQL: SELECT u.*, o.* FROM user u LEFT JOIN order o ON u.id = o.user_id WHERE u.id = 1
// 结果集需要手动处理重复数据
List<Map<String, Object>> result = jdbcTemplate.queryForList(sql);
// 手动组装
User user = null;
List<Order> orders = new ArrayList<>();
for (Map<String, Object> row : result) {
if (user == null) {
user = new User();
user.setId((int) row.get("u.id"));
user.setName((String) row.get("u.name"));
}
Order order = new Order();
order.setId((int) row.get("o.id"));
order.setOrderNo((String) row.get("o.order_no"));
orders.add(order);
}
user.setOrders(orders);
ORM框架实现(MyBatis示例)
MyBatis XML配置
<!-- UserMapper.xml -->
<resultMap id="UserOrderMap" type="com.example.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<!-- 一对多映射 -->
<collection property="orders" ofType="com.example.Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
</collection>
</resultMap>
<!-- 查询方法 -->
<select id="getUserWithOrders" resultMap="UserOrderMap">
SELECT
u.id as user_id,
u.name as user_name,
o.id as order_id,
o.order_no
FROM user u
LEFT JOIN order o ON u.id = o.user_id
WHERE u.id = #{userId}
</select>
Java实体类
public class User {
private Integer id;
private String name;
private List<Order> orders; // 一对多关系
// getter/setter
}
public class Order {
private Integer id;
private String orderNo;
// getter/setter
}
完整案例:用户-订单查询
数据库层
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
// 方式1:使用MyBatis(推荐)
public User getUserWithOrders(Integer userId) {
// 实际使用MyBatis的Mapper
return userMapper.selectUserWithOrders(userId);
}
// 方式2:JDBC手动实现
public User getUserWithOrdersJdbc(Integer userId) {
String sql = "SELECT u.*, o.* FROM user u " +
"LEFT JOIN `order` o ON u.id = o.user_id " +
"WHERE u.id = ?";
List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, userId);
if (rows.isEmpty()) {
return null;
}
User user = new User();
List<Order> orders = new ArrayList<>();
for (Map<String, Object> row : rows) {
// 用户信息(只设置一次)
if (user.getId() == null) {
user.setId((Integer) row.get("id"));
user.setName((String) row.get("name"));
}
// 订单信息
if (row.get("o.id") != null) {
Order order = new Order();
order.setId((Integer) row.get("o.id"));
order.setOrderNo((String) row.get("order_no"));
order.setUserId((Integer) row.get("user_id"));
orders.add(order);
}
}
user.setOrders(orders);
return user;
}
}
Service层
@Service
public class UserService {
@Autowired
private UserDao userDao;
public User getUserInfo(Integer userId) {
User user = userDao.getUserWithOrders(userId);
// 业务逻辑处理
if (user != null && user.getOrders() != null) {
System.out.println("用户 " + user.getName() + " 有 " +
user.getOrders().size() + " 个订单");
}
return user;
}
}
Controller层
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public Result getUser(@PathVariable Integer id) {
User user = userService.getUserInfo(id);
return Result.success(user);
}
}
性能优化建议
延迟加载
<!-- MyBatis延迟加载 -->
<resultMap id="UserOrderMap" type="User">
<collection property="orders"
ofType="Order"
select="com.example.OrderMapper.getOrdersByUserId"
column="id"
fetchType="lazy"> <!-- lazy: 使用时才加载 -->
</collection>
</resultMap>
分批查询(N+1问题优化)
<!-- 使用IN查询减少查询次数 -->
<select id="getUsersWithOrders" resultMap="UserOrderMap">
SELECT u.*, o.*
FROM user u
LEFT JOIN order o ON u.id = o.user_id
WHERE u.id IN
<foreach collection="userIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 两次查询 | 简单易懂,避免笛卡尔积 | 需要多次查询 | 数据量小 |
| JOIN查询 | 一次查询获取全部数据 | 数据量大时效率低 | 数据量适中 |
| MyBatis resultMap | 自动映射,代码简洁 | 配置相对复杂 | 复杂业务场景 |
| 延迟加载 | 按需加载,节省资源 | 可能产生N+1问题 | 关联数据不一定需要 |
推荐方案:对于大多数业务场景,使用MyBatis的resultMap + collection配置,配合延迟加载或分批查询策略,既能保持代码简洁,又能获得较好的性能。