lasbr
lasbr

Reputation: 79

Hibernate EntityExistsException if trying to fetch OneToOne association lazily

I have the bidirectional OneToOne association Account <-> Budget. According to this I tried to lazily load the association:

For the Account.java:

@Audited
@Entity
public class Account {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval=true, fetch=FetchType.LAZY)
  private  Budget mainBudget;
  } 
...
}

For the Budget.java:

@Audited
@Entity
public class Budget {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(fetch=FetchType.LAZY)
  @MapsId
  private Account account;

  @OneToMany(targetEntity = Budget.class, cascade = CascadeType.ALL, 
    mappedBy = "parentBudget", orphanRemoval=true)
  @Fetch(value = FetchMode.SUBSELECT)
  private  List<Budget> subBudget; 
...
} 

Now I am trying to make a new account with a new budget with the create-method in the DAOClass looking as follows:

@Resource(name = "DBRouter", type = DatabaseRouter.class)
protected DatabaseRouter router;

@PersistenceContext
protected EntityManager em;

@Transactional
public D create(D d, boolean flush, String resource) {
  router.setDataSource(resource);
  if (flush) {
    em.flush();
  }
  D obj = em.merge(d);
  if (flush) {
    em.flush();
  }
  return obj;
}

This gives me the following exception:

javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [[PACKACKENAMES].Account#50](31 internal lines)
    at org.apache.openejb.persistence.JtaEntityManager.merge(JtaEntityManager.java:203)
    at [PACKACKENAMES].DAOClass.create(DaoClass.java:75)
    at [PACKACKENAMES].DaoClass.create(DaoClass.java:66)
...

If I remove the @MapsId annotation, then I don't have this problem, although then the lazy loading does not work properly. What is the reason for this exception and how to solve it?

Upvotes: 0

Views: 182

Answers (2)

Gavin King
Gavin King

Reputation: 4303

You have:

public class Budget {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  protected long id;

  @OneToOne(fetch=FetchType.LAZY)
  @MapsId
  private Account account;

But this mapping doesn't make sense. The id can't be a @GeneratedValue if it's mapped by the one-to-one association to Account.

So:

  1. remove the @GeneratedValue annotation, and also
  2. make sure you explicitly set the id field to the value of the id field of the associated Account.

Or, alternatively:

  • just don't use @MapsId, if what you want is a foreign key column that is separate from the primary key.

See also:

Hibernate error (EntityExistsException) when persisting entity with children multiple times

Which is a similar problem.

Upvotes: 0

Mar-Z
Mar-Z

Reputation: 4858

In your case Account is a parent entity for Budget. @MapsId annotation says JPA to use the id of the parent entity for the mapping of annotated one-to-one relationship. Both ids will be identical for associated entities (rows). There is no need to generate a separate id for Budget. As we can see from the exception it would even not work.

Solution - remove the @GeneratedValue annotation from the Budget entity.

public class Budget {
    @Id
    private long id;

    @OneToOne(fetch=FetchType.LAZY)
    @MapsId
    private Account account;
...
}

Upvotes: 1

Related Questions