【问题标题】:java.lang.IllegalStateException: Multiple representations of the same entity [] are being merged. Detached: []; Detached: []java.lang.IllegalStateException:正在合并同一实体 [] 的多个表示。分离:[];分离:[]
【发布时间】:2019-06-08 14:56:16
【问题描述】:

我有以下三个实体EntityA、EntityB和EntityC:

实体A:

import lombok.*;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "Entity_A")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "locationA")
@ToString(of = "locationA")
public class EntityA {
    @Id
    @Column(name = "Name_A", length = 10)
    private String nameA;
    @Column(name = "Loc_A", length = 10)
    private String locationA;
    @ManyToMany(cascade = { CascadeType.MERGE })
    @JoinTable(
            name = "En_A_On_B",
            joinColumns = { @JoinColumn(name = "Name_A") },
            inverseJoinColumns = { @JoinColumn(name = "B_id") }
    )
    private Set<EntityB> bs;
}

实体B:

import lombok.*;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "Entity_B")
@Setter
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "locationB")
@ToString(of = "locationB")
public class EntityB {
    @Id
    @GeneratedValue
    @Column(name = "B_id")
    private int id;
    @Column(name = "Loc_B", length = 10)
    private String locationB;
    @ManyToMany(cascade = { CascadeType.MERGE })
    @JoinTable(
            name = "En_C_on_B",
            joinColumns = { @JoinColumn(name = "B_id") },
            inverseJoinColumns = { @JoinColumn(name = "C") }
    )
    private Set<EntityC> cs;
 }

实体C:

 import lombok.*;

import javax.persistence.*;
import java.util.Set;

@Entity
@Table(name = "Entity_C")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "c")
@ToString(of = "c")
public class EntityC {
    @Id
    @Column(name = "C", length = 20)
    private String c;    
}

要保存的服务类:

@Service
@Slf4j
public class ServiceClass {
    @Autowired
    private EntityARepository entityARepository;

    private Set<EntityC> cs1 = new HashSet<>(asList(
            EntityC.builder().c("100").build(),
            EntityC.builder().c("10").build()
    ));

    private Set<EntityC> cs2 = new HashSet<>(asList(
            EntityC.builder().c("100").build(),
            EntityC.builder().c("200").build()
    ));

    //METHOD TO SAVE
    public void save() {
        Map<String, Set<EntityC>> map = new HashMap<>();
        map.put("B1", cs1);
        map.put("B2", cs2);

        List<String> bs = asList("B1", "B2");
        EntityA aa = EntityA.builder().nameA("abcd").locationA("mon").build();
        EntityA ab = EntityA.builder().nameA("abcde").locationA("money").build();

        bs.forEach(b -> {
            EntityB entityB = EntityB.builder().locationB("100xxx").build()
            entityB.getCs().addAll(map.get(b));            

            aa.getBs().add(entityB);
            ab.getBs().add(entityB);
        });

        entityARepository.save(aa);
        entityARepository.save(ab);
    }
}

执行上述代码会引发以下异常

原因:java.lang.IllegalStateException:正在合并同一实体 [com.xxx.xxx.xxx.xxx.EntityC#100] 的多个表示。分离:[(c=100)];分离:[(c=100)]

注意:我在互联网上进行了探索,但没有一个与我的场景匹配

知道如何解决这个问题

【问题讨论】:

  • 您是否尝试更改级联类型(关于 CascadeType.MERGE ):stackoverflow.com/questions/26591521/…
  • 使用 Spring Boot 和 H2 数据库的代码可以正常工作。
  • 我正在使用 mssql。它对我来说是抛出异常。
  • CascadeType.MERGE 更改为CascadeType.ALL,看看发生了什么。
  • 它正在抛出相同的异常

标签: java hibernate jpa many-to-many


【解决方案1】:

问题就在这里:

private Set<EntityC> cs1 = new HashSet<>(asList(
        EntityC.builder().c("100").build(), //this entity instance has the same identifier...
        EntityC.builder().c("10").build()
));

private Set<EntityC> cs2 = new HashSet<>(asList(
        EntityC.builder().c("100").build(), //...as this one
        EntityC.builder().c("200").build()
));

您试图在一个工作单元中保留同一实体的两个版本。想象一下你把:

EntityC.builder().c("100").name("A name").build()

cs1 和:

EntityC.builder().c("100").name("Another name").build()

改为cs2。由于两个实体具有相同的id (c="100"),Hibernate 应该如何知道哪个版本“获胜”?

尝试将EntityC 的相同实例放入两个集合中,问题应该会消失。

【讨论】:

  • 它不会是与实际场景相同的实例,它将是两个不同的未编组实例。我为此使用 equals 和 hashCode 方法。
  • 那么,恐怕同样的问题仍然存在。无论您从哪里获取实体,如果您尝试合并具有相同 ID 的不同实体实例,Hibernate 无法自行决定哪个版本是正确的。您需要强制引用相等,否则它将不起作用。如果要保存EntityBEntityC 之间的关联,而不是整个未编组的EntityC 状态,则需要获取对pre-现有的EntityC 使用repository.getOne(),并在entityB.getCs().addAll(...) 行中使用该实例而不是map.get(b);
  • 使用 Spring Boot 和 H2 数据库,OP 代码可以正常工作。
  • @EugenCovaci 您是否在事务中对save() 进行了调用?如果没有,那么entityARepository.save(aa)entityARepository.save(ab) 创建两个单独的事务,所以问题不会浮出水面
  • @crizzis 无论entityARepository.save 被标记为@Transactional,它都能正常工作。顺便说一句,制作或不制作 entityARepository.save 事务不会创建两个事务。
猜你喜欢
  • 2016-03-14
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
  • 2015-08-10
  • 2012-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多