Java案例怎么实现一对多查询?

wen java案例 31

本文目录导读:

Java案例怎么实现一对多查询?

  1. SQL层实现(最基础)
  2. ORM框架实现(MyBatis示例)
  3. 完整案例:用户-订单查询
  4. 性能优化建议

在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配置,配合延迟加载分批查询策略,既能保持代码简洁,又能获得较好的性能。

抱歉,评论功能暂时关闭!