如何更新@OneToMany集合中的现有条目?

时间:2019-01-19 20:30:07

标签: java mysql spring hibernate orm

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

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

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

代理表:

+--------------+-------------+------+-----+---------+-------+
| 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. with resource =“ res1”
  2. with resource =“ res2”

如果源表已经包含带有source =“ res1”的条目,我想在数据库中的java对象中填充其id属性,以避免创建重复项。

现在,我在“存储库”类中手动进行此操作:

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避免创建重复项。

它可以工作,但是可能有一个更清洁的解决方案来解决这个问题?

1 个答案:

答案 0 :(得分:0)

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

话虽如此,没有简单的方法可以只保存数据库中不存在的对象。您可以将resource列用作主键,而将id列扔掉(我认为这不是一个好选择),或者必须在数据库中进行选择。

This question has more details on the second option

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