【问题标题】:How to update existing entries in @OneToMany collection?如何更新@OneToMany 集合中的现有条目?
【发布时间】:2019-06-13 17:31:45
【问题描述】:

我在数据库中存储了一组免费代理。代理实体包括:

  • IP 地址
  • 端口
  • 来源列表

Source 基本上是我在其中找到此代理信息的网站。这是我的架构:

代理表:

+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| id           | varchar(45) | NO   | PRI | NULL    |       |
| ip_address   | varchar(40) | NO   |     | NULL    |       |
| port         | smallint(6) | NO   |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+

来源:

+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| resource | varchar(200) | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

proxy_sources 连接前两个表:

+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| proxy_id  | varchar(45) | NO   | MUL | NULL    |       |
| source_id | int(11)     | NO   | MUL | NULL    |       |
+-----------+-------------+------+-----+---------+-------+

我的 Java ORM 类:

@Entity
@Table(name = "proxy")
public class Proxy {

    @Id
    @Column(name = "id")
    private String id;

    @Column(name = "ip_address")
    private String ipAddress;

    @Column(name = "port")
    private int port;

    @OneToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
    @JoinTable(
            name = "proxy_sources",
            joinColumns = @JoinColumn(name = "proxy_id"),
            inverseJoinColumns = @JoinColumn(name = "source_id")
    )
    private List<Source> sources = new ArrayList<>();

    ...
}


@Entity
@Table(name = "source")
public class Source {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "resource")
    private String resource;

    ...
}

每当我保存代理对象时,我都想避免重复现有的源。例如:

代理对象有 2 个来源:

  1. 资源 = "res1"
  2. 资源 = "res2"

如果源表已经包含 source = "res1" 的条目,我想从数据库中将它的 id 属性填充到 java 对象中以避免创建重复。

现在我在我的 Repository 类中手动完成:

public String save(Proxy proxy) {
    populate(proxy.getSources());
    return (String) sessionFactory.getCurrentSession().save(proxy);
}

这里是填充方法:

private void populate(List<Source> sources) {
    if (sources.isEmpty()) {
        return;
    }

    List<String> resources = sources.stream().map(Source::getResource).collect(toList());

    List<Source> existing = sessionFactory.getCurrentSession()
            .createQuery("FROM Source source WHERE source.resource IN (:resources)", Source.class)
            .setParameterList("resources", resources)
            .list();

    sources.forEach(source -> existing.stream()
            .filter(s -> s.getResource().equals(source.getResource()))
            .findAny()
            .ifPresent(s -> source.setId(s.getId())));
}

基本上我所做的是检查源集合中的每个源是否存在。如果已经存在具有相同资源值的源,我会从数据库中填充它的 id。非空 id 避免创建重复。

它有效,但可能有更清洁的解决方案来解决这个问题?

【问题讨论】:

    标签: java mysql spring hibernate orm


    【解决方案1】:

    您可以进行的第一个修改以避免重复是在source 表中的resource 列上创建一个唯一键。这样,即使您在代码中出错,如果您尝试保存重复的寄存器,数据库也会抛出错误。

    话虽如此,没有简单的方法可以只保存数据库中不存在的对象。您要么将 resource 列作为主键,然后将 id 列丢弃(我认为这不是一个好的选择),要么您必须在数据库上进行选择。

    This question has more details on the second option

    如果您愿意更改应用程序流程,可以解决此问题的一种方法是将代理保存分为两个步骤。首先,您注册所有源,并在保存所有源之后,然后开始注册代理。这样您就知道,当您保存代理时,它已经预先保存了所有源,此时您的工作只是链接到 Proxy 实体上的现有源。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-16
      • 2015-09-11
      • 2016-08-17
      • 2012-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多