java - Spring Data JDBC 的奇怪一对多行为

标签 java spring-boot hsqldb lombok spring-data-jdbc

我碰巧在 Spring Data JDBC(使用 Spring Boot 2.1 和必要的启动器)聚合处理中遇到了一些非常奇怪的事情。让我解释一下这个情况(我正在使用 Lombok,不过这个问题可能是相关的)...

这是我的实体的摘录:

import java.util.Set;
@Data
public class Person {
    @Id
    private Long id;
    ...
    private Set<Address> address;
}

这是一个关联的 Spring 数据存储库:

public interface PersonsRepository extends CrudRepository<Person, Long> {
}

这是一个失败的测试:

@Autowired
private PersonsRepository personDao;
...
Person person = personDao.findById(1L).get();
Assert.assertTrue(person.getAddress().isEmpty());
person.getAddress().add(myAddress); // builder made, whatever
person = personDao.save(person);
Assert.assertEquals(1, person.getAddress().size()); // count is... 2!

事实是,通过调试,我发现地址集合(这是一个集合)包含附加地址的同一实例的两个引用。 我不明白两个引用是如何结束的,最重要的是一个 SET(实际上是一个 LinkedHashSet,为了记录)如何能够处理同一个实例两次!

person  Person  (id=218)    
    address LinkedHashSet<E>  (id=228)  
        [0] Address  (id=206)   
        [1] Address  (id=206)   

有人知道这种情况吗?谢谢

最佳答案

一个(Linked)HashSet当该实例同时发生变化时,可以(作为副作用)存储同一个实例两次(引用自 Set ):

Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.

所以这就是可能发生的情况:

  1. 您创建 Address 的新实例但其 ID 未设置( id=null )。
  2. 您将其添加到 Set ,其哈希码计算为某个值 A .
  3. 您调用PersonsRepository.save这很可能持续存在 Address并为其设置一些非空 ID。
  4. PersonsRepository.save可能还打电话HashSet.add确保地址集中。但由于 ID 更改,哈希码现在计算为某个值 B .
  5. 哈希码 AB映射到 HashSet 中的不同存储桶,所以Address.equals方法甚至在 HashSet.add 期间都不会被调用。因此,您最终会在两个不同的存储桶中获得相同的实例。

最后,我认为您的实体应该有 equals/hashCode仅基于 ID 的语义。要使用 Lombok 实现它,您可以使用 @EqualsAndHashCode如下:

@Data
@EqualsAndHashCode(of = "id")
public class Person {
    @Id
    private Long id;
    ...
}

@Data
@EqualsAndHashCode(of = "id")
public class Address {
    @Id
    private Long id;
    ...
}

不过,这并不能解决您遇到的问题,因为 ID 发生了变化,因此哈希码仍然会有所不同。

处理此问题的一种方法是保留 Address 将其添加到 Set 之前.

关于java - Spring Data JDBC 的奇怪一对多行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53543604/

相关文章:

java - 使用 jSTL 时期望获得等号

java - 两个Android模拟器之间的通信

css - Spring-Boot ResourceLocations 未添加导致 404 的 css 文件

java - OpenJPA HSQLdb - 如何处理 ID

java - 下面的片段是什么意思?

java - Android/Java - 像 Gmail 应用程序一样刷新 Activity

java - 为 Spring Web 客户端禁用 SSL 检查

javascript - 定时自动更新 JSP 页面

java - hsqldb 2.3.2 jdbc 驱动程序不支持 ResultSet.first()?

java - Hsql过载总和