Search code examples
javahibernatejpaentitymanager

HibernateException: Shared references invoked by jpa itself (?)


I'm currently facing some trouble writing objects to database. My problem does arise with a quite complex data model, but I will try break it down to an understandable example.

Let's say there is class A:

@Entity(name = "a")
@Table(schema = "foo")
public class A {
    private static final long serialVersionUID = -5305374150112492804L;
    logger = LoggerFactory.getLogger(A.class);

    @Id
    @Column(name = "tid", nullable = false, unique = true)
    @SequenceGenerator(name = "globalSequenceGen", schema = "foo", sequenceName = "foo_sequence")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "globalSequenceGen")
    private Integer TID;

    @Column(name = "b")
    private String bId;

    @OneToMany(targetEntity = B.class)
    @JoinColumn(name = "random_b_attribute", referencedColumnName = "b", updatable = false, insertable = false)
    private List<B> b;

    @PrePersist
    @PreUpdate
    public void prePersist() {
        bId = b == null || b.isEmpty() ? null : b.get(0).getRandomAttribute();
    }
    ...
}

The referenced class B is quite simple: It only contains @Column annotated attributes. Please notice, that instances of A can and will refer to the same "Set" of Bs in it's attribute b.

There is a third class D referencing A:

@Entity(name = "d")
@Table(schema = "foo")
public class D {
    private static final long serialVersionUID = -5305374150112492804L;
    logger = LoggerFactory.getLogger(D.class);

    @Id
    @Column(name = "tid", nullable = false, unique = true)
    @SequenceGenerator(name = "globalSequenceGen", schema = "foo", sequenceName = "foo_sequence")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "globalSequenceGen")
    private Integer TID;

    @Column(name = "a")
    private Integer aId;

    @ManyToOne(targetEntity = A.class)
    @JoinColumn(name = "a", referencedColumnName = "tid", updatable = false, insertable = false)
    private A a;

    @PrePersist
    @PreUpdate
    public void prePersist() {
        aId = a == null ? null : a.getTID();
    }
    ...
}

Coming to the problem:

  1. I do retrieve objects of D from database (lazy). None of them does refer to any objects of A (e.g. the column a in database is empty).
  2. I do retrieve all objects of A from database with the same EntityManager as I would later like to insert them into D instances (also lazy).
  3. I do NOT touch any D#a nor A#b attribute.
  4. Trying to EntityManager#persist any newly created instance of D ends up in getting the following exception:
Caused by: org.hibernate.HibernateException: Found shared references to a collection: random.package.structure.A.b

During my research I found, that the error may be caused when providing the same Collection (not to be confused with a Collection having the same entries). Obviously this stems from initialisation of A instances by jpa itself?! Seems, to be a good idea to recycle the same Collection, but obviously causes problems when updating the unaltered objects...

Clearing the EntityManager before persisting objects seem to solve the problem. But, as everything is read lazy using those objects afterwards will cause other problems.

Do you have any hint on how to create new Collections for every instance of A or prevent the failure in other ways?

Many thanks in advance, if I did miss any information, please just ask!


Solution

  • Thanks a lot for your answers! The hint of @crizzis did the trick.

    Of course it is a @ManyToMany relation. I altered my data model (to use Mapping tables) and that solved my problem!