使用 JDBC 在一对多中获取“多”

Posted

技术标签:

【中文标题】使用 JDBC 在一对多中获取“多”【英文标题】:Getting the "many" in one-to-many using JDBC 【发布时间】:2021-01-08 03:20:14 【问题描述】:

我现在有两个表,它们形成一对多的关系。一张餐桌,映射到多次检查。我的 postgres 表是这样定义的:

CREATE TABLE restaurant (
    id UUID NOT NULL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    zip VARCHAR(255),
    address VARCHAR(255),
    phone VARCHAR(255)
);

CREATE TABLE inspection (
    restaurant_id UUID NOT NULL,
    date VARCHAR(255) NOT NULL,
    score int,
    violation VARCHAR(255),
    risk VARCHAR(255),
    FOREIGN KEY (restaurant_id) REFERENCES restaurant(id)
);

当我查询一家餐厅时,我不确定如何推断所有检查。我正在使用resultSet.getString("fieldName") 获取餐厅的所有字段,但是当我尝试resultSet.getString("inspection") 时它不起作用。我需要什么代码来进行这些检查?这是我目前获取所有餐馆的查询。

@Override
    public Optional<Restaurant> selectRestaurantById(UUID id) 
        final String sql = "SELECT * FROM restaurant WHERE id = ?"; 
        List<Restaurant> res = jdbcTemplate.query(
                sql, 
                new Object[] id, 
                (resultSet, i) -> 
            UUID restaurant_id = UUID.fromString(resultSet.getString("id"));
            String name = resultSet.getString("name");
            String zip = resultSet.getString("zip");
            String address = resultSet.getString("address");
            String phone = resultSet.getString("phone");

            // How do I get an ArrayList of inspections from postgres??
            Restaurant r = new Restaurant(restaurant_id, name, zip, address, phone);
            return r;
        );
        Restaurant found = res.size() > 0 ? res.get(0) : null;
        return Optional.ofNullable(found);
    

【问题讨论】:

您应该加入表格以获取所有详细信息 - SELECT * FROM restaurant r INNER JOIN INSPECTION i on r.id = i。 restaurant_id WHERE r.id = ?由于餐厅的一对多关系,您将获得重复的餐厅,因此您可以使用连接进行两次查询或首先处理重复。 【参考方案1】:

这与 java 或 JDBC 无关。启动一个基本的 sql 终端(如果您在终端上使用 postgres,psql,那会很好)并首先学习 SQL。幸运的是,网上有很多教程可以找到:)

查询只会生成另一个表,然后您会循环遍历该表。 SELECT * FROM foo 只是.. 给你那张桌子。请注意,只是那张桌子。餐厅的桌子就是这样。餐馆。里面的任何地方都没有检查。 FOREIGN KEY 并不神奇,SQL 并没有真正具有一对多关系的概念。至少,不是这样定义的——它们有连接的概念,它可以让你在你想要的任何东西之间临时定义这种关系。定义中不需要FOREIGN KEY

FOREIGN KEY 所做的所有事情都是对 DB 说:您能否为该字段创建索引,并且请使任何事务失败,否则该事务将在该表中写入 'restaurant_id' 字段具有以下值的行与餐厅表中某行的“id”值不匹配。这和CHECK v &gt; 0 一样有趣。只是在提交时运行的检查。就是这样。

SELECT r.*, i.* FROM restaurant r LEFT JOIN inspection i ON i.restaurant_id = r.id WHERE r.id = ? 会这样做 - 可以这么说,这会创建一个新的“表”。包含行中的每一列,以及检查中的每一列。如果一家餐馆有 0 次检查,你会得到 1 行,其中每个 i. 相关字段都是 NULL(因为 LEFT JOIN;如果你写了 INNER JOIN,你根本不会得到那些餐馆)。对于有 10 次检查的餐厅,您将获得 10 行,其中所有 r. 相关字段都重复。

如果这听起来很烦人 - 是的,我可以看到。然后运行第二个查询:SELECT * FROM inspection WHERE restaurant_id = ?,获取数据,填充您的数组列表。

注意:您的 tabledef 很奇怪。 score 应该有 NOT NULL 限制。

【讨论】:

感谢您的周到回答!我很好奇,如果我将 10 个检查映射到一家餐厅,访问它们并将它们放入列表的最佳方法是什么? 我可以在函数之外有一个列表,只需将它们添加进去,然后设置获取返回的餐厅列表,但是当我想要返回餐厅列表时我不能这样做,每个都有自己的检查清单。 更一般地说,“首先我将表映射到对象,然后我看到我需要的东西”的想法与 SQL 并不完全吻合,这更适合你只是查询你需要的东西。所以,回答“如果我有 10 个检查映射到一家餐厅,访问它们并将它们放入列表的最佳方式是什么?”我必须知道你为什么想要这些数据以及你想用它做什么。 例如,“我将把数据库中的所有数据库中的所有东西放入一个对象中”的模型就是一个糟糕的模型。想象一下,您不仅有检查,还有其他 40 项。那将是一个巨大的物体。相反,您需要什么?如果它是显示您所在地区的违规行为的网页,那么就这样做。 SELECT r.name, STRING_AGG(i.violation, ', ') FROM restaurant r INNER JOIN inspection i ON i.restaurant_id = r.id WHERE r.zip IN (?, ?, ?, ?) AND v.score &lt; 6 ORDER BY r.name. 我正在开发一个后端 API,所以理想情况下,当有餐厅请求进入时,我应该发送所有餐厅的 JSON 以及他们的检查列表跨度>

以上是关于使用 JDBC 在一对多中获取“多”的主要内容,如果未能解决你的问题,请参考以下文章

hibernate的一对多配置

如何用jdbc实现一对多

Spring JDBC中的多个一对多关系

如何使用 Java 和 JDBC 防止一对多关系中的重复?

mybatis进行一对多时发现的问题总结

hibernate多对多 一对多 及简单入门