views:

43

answers:

1

I have two entities: Question and FavoritesCounter. FavoritesCounter should be created when the question is added to favorites for the first time.

Consider a use case when two users tries to add a question to favorites simultaneously - this will cause EntityExistsException when entityManager.persist(counter) is called for the second user.

But the code below doesn't work, because when EntityExistsException is thrown, container marks transaction as rollback only and attempt to return getFavoritesCounter(question) fails with javax.resource.ResourceException: Transaction is not active

@Stateless
public class FavoritesServiceBean implements FavoritesService {

  ...

  public void addToFavorites(Question question) {
    FavoritesCounter counter = getCounter(question);
    if (counter == null) {
      counter = createCounter(question);
    }
    //increase counter
  }

  private FavoritesCounter createCounter(Question question) {
    try {
      FavoritesCounter counter = new FavoritesCounter();
      counter.setQuestion(question);
      entityManager.persist(counter);
      entityManager.flush();
      return counter;
    } catch (EntityExistsException e) {
      return getFavoritesCounter(question);
    }
  }

  private FavoritesCounter getFavoritesCounter(Question question) {
    Query counterQuery = entityManager.createQery("SELECT counter FROM FavoritesCounter counter WHERE counter.question = :question");
    counterQuery.setParameter("question", question);
    List<FavoritesCounter> result = counterQuery.getResultList();
    if (result.isEmpty()) return null;
    return result.get(0);
  }

}

Question

@Entity
public class Question implements Serializable {

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

  //getter and setter for id

}

FavoritesCounter

@Entity
public class FavoritesCounter implements Serializable {

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

  @OneToOne
  @Column(unique = true)
  private Question question;

  //getter and setter

}

What is the best way to return already created entity after EntityExistsException?

+1  A: 

You can move createCounter() to another Session Bean and mark that method with @TransactionAttribute(REQUIRES_NEW)

Alternately you can move createCounter() to the local interface of FavoritesServiceBean (also ensuring you add the @TransactionAttribute(REQUIRES_NEW) annotation) and call it as follows:

@Stateless
public class FavoritesServiceBean implements FavoritesService 
{

    ...
    @Resource
    protected SessionContext sessionContext;

    public void addToFavorites(Question question) 
    {
        FavoritesCounter counter = getCounter(question);
        if (counter == null) {
          counter = sessionContext.getBusinessObject( FavoritesService.class )
                                  .createCounter(question);
        }
       //increase counter
    }

    @TransactionAttribute(REQUIRES_NEW)
    public FavoritesCounter createCounter(Question question) 
    {
        ...
    }
    ...
}

You need to go through the business/local interface rather calling createCounter() directly so the container knows to start a new transaction.

mtpettyp
@mtpettyp, thanks, it works!
Ivan Yatskevich