我正在数据库中存储一组免费代理。代理实体包括:
来源基本上是一个我在其中找到此代理信息的网站。这是我的架构:
代理表:
+--------------+-------------+------+-----+---------+-------+
| 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个来源:
如果源表已经包含带有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避免创建重复项。
它可以工作,但是可能有一个更清洁的解决方案来解决这个问题?
答案 0 :(得分:0)
您可以做的避免重复的第一个修改是在source
列的resource
表中创建一个唯一键。这样,即使您在代码中出错,如果您尝试保存重复的寄存器,数据库也会抛出错误。
话虽如此,没有简单的方法可以只保存数据库中不存在的对象。您可以将resource
列用作主键,而将id
列扔掉(我认为这不是一个好选择),或者必须在数据库中进行选择。>
This question has more details on the second option
如果您愿意更改应用程序流程,则可能解决此问题的一种方法是将代理保存分为两步。首先,您注册所有源,然后保存所有源,然后开始注册代理。这样一来,您便知道在保存代理时,它将已经预先保存了所有源,因此此时的工作仅是链接到Proxy
实体上的现有源。