【问题标题】:Java Spring Data @Query with @OneToMany relation returns no result具有@OneToMany 关系的Java Spring Data @Query 不返回结果
【发布时间】:2015-06-04 12:25:40
【问题描述】:

我有以下实体:

@Entity
public class Customer extends BaseEntity {

    private String firstname;
    private String lastname;
    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
    private Set<Address> addresses;

    ...

@Entity
public class Address extends BaseEntity {

    private String street;
    private String houseNumber;
    private String zipCode;
    private String city;
    @ManyToOne
    private Customer customer;

    ...

还有如下存储库接口类:

@Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {

    @Query("select c from Customer c join c.addresses a where (a.city = :cityName)")
    List<Customer> findByCity(@Param("cityName")String city);

}

现在,我正在尝试运行以下集成测试,但它失败了,我完全不知道为什么。不幸的是,我是 Spring 的初学者,我正在努力学习它;-)

@Test
public void testFindCustomerByCity() {
    Customer customer = new Customer("Max", "Tester");
    Address address = new Address("Street", "1", "12345", "City");
    HashSet<Address> addresses = new HashSet<Address>();
    addresses.add(address);
    customer.setAddresses(addresses);
    Customer savedCustomer = customerRepository.save(customer);
    Assert.assertTrue(savedCustomer.getId() > 0);

    List<Customer> customerList = customerRepository.findByCity("City");
    Assert.assertThat(customerList.size(), is(1));
}

错误信息是:

java.lang.AssertionError: 预期:是 但是:是

为什么结果为空。我的测试设置错了吗?实体关系? 很好,如果你能帮助我。

【问题讨论】:

  • 你没有在地址上设置客户,只在客户中设置地址。
  • @M 好的我已经添加了address.setCustomer(customer); 并且它有效......但我真的不明白......我想,给父实体提供子实体就足够了。也许您可以简短地解释一下,或者给我一个链接网址作为提示...无论如何,谢谢您的快速回答。
  • 您有一个带有null 客户的地址,正如您指定的那样,该关系由地址中的客户属性管理。如果没有设置,数据库应该如何知道存在关系。另外,setAddresses 是向客户添加地址的一种非常糟糕的方式。删除该方法并创建一个您可能管理关系的addAddress 方法。
  • 要明确一点,当您没有在address 中设置customer 时,是否在没有客户外键的情况下保存到数据库中的地址?还是查找查询有问题?对于以后访问此问题的任何人来说,很高兴知道这一点。
  • @DuncanKinnear 没有客户外键

标签: java spring jpa assertion


【解决方案1】:

您可以使用这样的查询方法。使用 下划线 (_) 获取属性子项。

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
    List<Customer> findByAddresses_City(String city);
}

【讨论】:

  • 下划线不是必须的。
  • 下划线是必要的,因为城市不是客户属性,而是它的子项(地址)的属性。
  • 下划线不是绝对必要的。如果我们有 findByAdressesCity,它将首先搜索 AddressesCity,然后从右侧开始切割 CamelCase(!),然后它将搜索 Adresses.City 并获得成功。更多关于这方面的文档可以在这里找到:docs.spring.io/spring-data/jpa/docs/current/reference/html/…
【解决方案2】:

您在Customer 实体的addresses 字段中有@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)。这基本上意味着该关系由Address 实体中的customer 字段中的值管理。

在您的测试代码中,您只是在客户上设置地址,而不是在地址上设置客户。它仍然为空,因此数据库中可能有 2 条记录但没有关系。因此,您的查询不会返回任何内容。

像使用setAddresses 那样设置集合是在 JPA 环境中做事的一种非常糟糕的方式(当您在已经存在的实例上执行此操作时,您将覆盖持久集合)。移除setAddresses 方法,并改为在Customer 上创建addAddress 方法。

@Entity
public class Customer extends BaseEntity {

    private String firstname;
    private String lastname;

    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
    private final Set<Address> addresses = new HashSet<Address>();

    // No setter, only a getter which returns an immutable collection
    public Set<Address> getAddresses() {
        return Collections.unmodifiableSet(this.addresses);
    }

    public void addAddress(Address address) {
        address.setCustomer(this);
        this.addresses.add(address);
    }

}

这也会稍微清理你的测试代码。

@Test
public void testFindCustomerByCity() {
    Customer customer = new Customer("Max", "Tester");
    customer.addAddress(new Address("Street", "1", "12345", "City"));
    Customer savedCustomer = customerRepository.save(customer);

    Assert.assertTrue(savedCustomer.getId() > 0);

    List<Customer> customerList = customerRepository.findByCity("City");
    Assert.assertThat(customerList.size(), is(1));
}

【讨论】:

    猜你喜欢
    • 2018-02-09
    • 1970-01-01
    • 2021-07-17
    • 1970-01-01
    • 1970-01-01
    • 2021-02-10
    • 2014-01-18
    • 2019-04-26
    • 1970-01-01
    相关资源
    最近更新 更多