【问题标题】:StackOverflow on save entity with bidirectional ManyToMany attributesStackOverflow 上保存具有双向 ManyToMany 属性的实体
【发布时间】:2021-09-28 05:02:44
【问题描述】:

很好,我正在创建一个带有 spring 和 elastic 的 rest api,但是在它的构建过程中我遇到了一个技术问题,结果我需要保存点击了收藏的公司的用户,为此我有 ManyToMany 列表(以便它休眠它生成中间表并将数据保存在两个实体中),系统在休眠中完美运行并将关系正确保存在数据库中,但是当我执行时我调用弹性存储库的保存方法它返回一个堆栈溢出。

如果我添加一个标签以使其明显(在 2 个实体处)它可以正常工作,但我需要弹性以某种方式保持这种关系,我曾想过复制代码并有 2 个列表,一个带有模型,另一个带有ids(模型保存在数据库中,id 字符串保存在 elastic 中)但在我复制代码并做可能的垃圾之前,我想在这里问一下!


java.lang.StackOverflowError: null
    at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936) ~[na:1.8.0_231]
    at org.springframework.data.convert.CustomConversions$TargetTypes.computeIfAbsent(CustomConversions.java:532) ~[spring-data-commons-2.4.5.jar:2.4.5]
    at org.springframework.data.convert.CustomConversions$ConversionTargetsCache.computeIfAbsent(CustomConversions.java:497) ~[spring-data-commons-2.4.5.jar:2.4.5]
    at org.springframework.data.convert.CustomConversions.getCustomWriteTarget(CustomConversions.java:348) ~[spring-data-commons-2.4.5.jar:2.4.5]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeInternal(MappingElasticsearchConverter.java:588) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeCollectionInternal(MappingElasticsearchConverter.java:839) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.createCollection(MappingElasticsearchConverter.java:753) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeProperty(MappingElasticsearchConverter.java:713) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeProperties(MappingElasticsearchConverter.java:674) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeInternal(MappingElasticsearchConverter.java:633) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeInternal(MappingElasticsearchConverter.java:611) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeCollectionInternal(MappingElasticsearchConverter.java:839) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.createCollection(MappingElasticsearchConverter.java:753) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeProperty(MappingElasticsearchConverter.java:713) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeProperties(MappingElasticsearchConverter.java:674) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeInternal(MappingElasticsearchConverter.java:633) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeInternal(MappingElasticsearchConverter.java:611) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]
    at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter.writeCollectionInternal(MappingElasticsearchConverter.java:839) ~[spring-data-elasticsearch-4.2.3.jar:4.2.3]

我把模型的两个属性传给你

商务舱

@Builder.Default
@ManyToMany(mappedBy = "commercesFav", cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity = UserImpl.class)
private List<User> usersFav = new ArrayList<>();

类用户

@Builder.Default
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity = CommerceImpl.class)
@JoinTable(name="favorites",
        joinColumns=@JoinColumn(name="user_id", referencedColumnName="id"),
        inverseJoinColumns=@JoinColumn(name="commerce_id", referencedColumnName="id"))
private List<Commerce> commercesFav = new ArrayList<>();

我正在使用龙目岛,但我认为这无关紧要

顺便说一句,弹性存储库是基本的、空的,它们什么都没有。我尝试在其中一些、Commerce 和 User 中包含 Transient 标签,但它不起作用

如何让 elasticsearch 正确保存对象?

谢谢!


我最终从项目中删除了所有lombok,我无法调试代码,现在代码更稳定,更可控,如果您对复杂代码有同样的问题,我建议删除像lombok这样的库

尽我所能,感谢 JsonManagedReference 和 JsonBackReference,我只能正确返回双向列表

    @ManyToMany(targetEntity = CommerceImpl.class)
    @JsonBackReference
    @JoinTable(name = "favorites",
            joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "commerce_id", referencedColumnName = "id"))
    @org.springframework.data.annotation.Transient
    private Set<Commerce> commercesFav = new HashSet<>();
    @ManyToMany(targetEntity = UserImpl.class, mappedBy = "commercesFav")
    @JsonManagedReference
    @org.springframework.data.annotation.Transient
    private Set<User> usersFav = new HashSet<>();

我正在从控制器中保存 2 个模型,它们只能从商店中恢复(我认为按照设计,最好将其反转并从用户那里返回)

@PostMapping("/commerce/fav/{identifier}")
    public ResponseEntity addFavoriteCommerceToUser(@PathVariable String identifier, @AuthenticationPrincipal UserDetails userDetails, HttpServletRequest request) {
        ResponseEntity<Object> responseEntity = ResponseEntity.status(HttpStatus.NOT_FOUND)
                .headers(RequestCustomUtil.getHeader(request)).body(ResponseUtil.beanNotFound(Commerce.class.getName()));

        try {
            Optional<Commerce> commerce = commerceHandle.get(identifier);
            if (commerce.isPresent()) {
                User user = userHandle.findByUsername(userDetails.getUsername());
                Commerce commerceEntity = commerce.get();

                commerceEntity.getUsersFav().add(user);
                user.getCommercesFav().add(commerce.get());
                commerce = commerceHandle.create(commerceEntity);
                userHandle.save(user);

                boolean isActualUser = commerce.get().getUser().getIdentifier().equals(user.getIdentifier());
                responseEntity = ResponseEntity.ok(CommerceBean.CommerceBeanComplete(commerce.get(), isActualUser));
            }
        } catch (CommerceException e) {
            LOG.error(e.getMessage());
        }

        return responseEntity;
    }

【问题讨论】:

  • Elasticsearch 不是关系数据库。在 Elasticsearch 中,索引中的文档之间没有 n-to-m 关系这样的概念(这不是 Spring Data Elasticsearch 特有的)。

标签: spring-boot rest elasticsearch spring-data-elasticsearch


【解决方案1】:

如果可以请添加堆栈跟踪,这将有助于了解哪个方法被递归调用。

虽然没有明确的想法,但有时 toString() 或 equals() 的 lombok 实现会导致问题。特别是如果关系是双向的。结果,我添加了类似下面的内容来排除。排除列表包含具有双向关系的属性的名称 -

@EqualsAndHashCode(callSuper = false, exclude = {"emails", "phones", "address", "client", "userRoles"})
@ToString( exclude = {"emails", "phones", "address", "client", "userRoles"})

【讨论】:

    猜你喜欢
    • 2020-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多