springjpa:postload方法中对transient字段的赋值破坏了从多个关系中删除实体的过程

tktrz96b  于 2021-06-27  发布在  Java
关注(0)|答案(1)|浏览(371)

有一个简单的 Entry 实体。。。

@Data
@NoArgsConstructor
@Entity
public class Entry {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private Double a;

    @Column
    private Double b;
}

... 和一个 Container 与条目有@manytomy关系的实体。

@Data
@Entity
public class Container {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long container_id;

    @ManyToMany(fetch = FetchType.LAZY)
    private Set<Entry> entries;

    public Container() {
        entries = new HashSet<>();
    }

    public void removeEntry(Entry e) {
        entries.remove(e);
    }

    public void add(Entry e) {
        entries.add(e);
    }
}

我可以使用以下事务方法从容器中删除条目。

@Transactional
    public void deleteFromContainer(Long id, Predicate<Entry> predicate) {
        Container container = containerRepository.findById(id).orElseThrow(IllegalStateException::new);

        Set<Entry> toBeDeleted = container.getEntries().stream()
                .filter(predicate)
                .collect(Collectors.toSet());

        toBeDeleted.forEach(entry -> container.removeEntry(entry));
        entryRepository.deleteAll(toBeDeleted);

        if (container.getEntries().isEmpty()) {
            containerRepository.delete(container);
        }
    }

一切都很好,直到我改变主意 Entry 实体。通过添加一个 transient 字段和一个为其赋值的@postload方法,删除就不可能了。

@Transient
    private Double[] fields;

    @PrePersist
    @PreUpdate
    public void setMappedFields() {
        a = fields[0];
        b = fields[1];
    }

    @PostLoad
    public void setTransientFields() {
        fields = new Double[]{a, b};
    }

实际上 Container 类在内部存储的条目集中找不到匹配的条目。因此,这些条目在被删除之前不会从manytomy关系中删除 entryRepository.deleteAll(toBeDeleted); 将出现以下错误:

DEBUG 12744 --- [  restartedMain] org.hibernate.SQL                        : delete from entry where id=?
TRACE 12744 --- [  restartedMain] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [2]
WARN  12744 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 23503, SQLState: 23503
ERROR 12744 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : Referential integrity constraint violation: "FKGWCV239MEJWTTQTY8LBBOYRYO: PUBLIC.CONTAINER_ENTRIES FOREIGN KEY(ENTRIES_ID) REFERENCES PUBLIC.ENTRY(ID) (2)"
    Referential integrity constraint violation: "FKGWCV239MEJWTTQTY8LBBOYRYO: PUBLIC.CONTAINER_ENTRIES FOREIGN KEY(ENTRIES_ID) REFERENCES PUBLIC.ENTRY(ID) (2)"; SQL statement:
    delete from entry where id=? [23503-200]

尽管我和调试器检查了要删除的值在集合中是否可用。
添加了另一个函数来调试行为。只是得到一个具体的答案 Container 从数据库中删除第一个 Entry 在里面。

@Transactional
    public void deleteFirstEntryFromContainer(Long id) {
        Container container = containerRepository.findById(id).orElseThrow(IllegalStateException::new);

        Set<Entry> entries = container.getEntries();

        Entry entryToBeDeleted = entries.stream().findFirst().orElseThrow(IllegalStateException::new);

        int sizeBefore = entries.size();
        boolean contains = entries.contains(entryToBeDeleted);

        entries.add(entryToBeDeleted);
        int sizeAfter = entries.size();

        HashSet<Entry> newSet = new HashSet<>(entries);
        int sizeOfNew = newSet.size();
    }

原来,同一个对象示例有时返回两个不同的hashcode()值。这样我就可以将同一个示例添加到 Set .
我粘贴了一张图片来显示这个。显示同一示例的调试结果可以添加两次到 Set .
有人对此有什么提示吗?谢谢你的帮助。

zpf6vheq

zpf6vheq1#

您知道基于哈希的集合中的哪些条件元素与要添加/删除的元素相匹配吗?基于hashcode和equals方法。您并没有展示如何实现equals/hashcode,但我猜您只是在使用lombok生成的内容,这可能不是您想要的。默认情况下,lombok为 @Data 基于类的整个状态的带注解类、equals和hashcode方法。这是一个非常常见的初学者错误,你会发现关于这个主题的文章吨。我建议您避免同时使用lombok,自己实现equals/hashcode,这样您就知道会发生什么。

相关问题