Forcing a transaction to rollback on validation errors in Seam

谁说胖子不能爱 提交于 2019-11-30 14:14:24

I must agree with @duffymo about validating before the transaction is initiated. It is quite difficult to handle database exceptions and presenting those to the user.

The reason you get the detached exception is most likely because you think you have written something to the database, and then you call remove on or refresh on the object, and then you try to write something again.

What you need to do instead is create a long-running conversation with flushMode set to MANUAL. Then you start persisting stuff, and then you can perform your validation, and if that is ok you persist again. After you are done and everything is good to go, you call entityManager.flush(). Which will save everything to the database.

And if something failed, you dont flush. You just return null or "error" with some message. Let me show you with some pseudo code.

Lets say you have a Person and Organization entity. Now you need to store Person before you can put person to Organization.

private Person person;
private Organization org;

@Begin(join=true,FlushMode=MANUAL) //yes syntax is wrong, but you get the point
public String savePerson() {
//Inside some save method, and person contains some data that user has filled through a form

//Now you want to save person if they have name filled in (yes I know this example should be done from the view, but this is only an example
try {
  if("".equals(person.getName()) {
    StatusMessages.instance().add("User needs name");
    return "error"; //or null
  }
  entityManager.save(person);
  return "success";
} catch(Exception ex) {
  //handle error
  return "failure";
}
}

Note that we now save person, but we have not flushed the transaction. However, it will check constraints that you have set on your entitybean. (@NotNull, @NotEmpty and so on). So it will only simulate a save.

Now you save organization for person.

@End(FlushMode=MANUAL) //yes syntax is wrong, but you get the point
public String saveOrganization() {
//Inside some save method, and organization contains some data that user has filled through a form, or chosen from combobox

org.setPerson(person); //Yes this is only demonstration and should have been collection (OneToMany)
//Do some constraint or validation check
entityManager.save(org);
//Simulate saving org
//if everything went ok
entityManager.flush() //Now person and organization is finally stored in the database
return "success";
}

Here you can even put stuff in try catch and only return success if no exception occurred, so that you don't get thrown to error page.

Update

You can try this:

@PersistenceContext(type=EXTENDED)
EntityManager em;

This will make a stateful bean has an EJB3 extended persistence context. The messages retrieved in the query remain in the managed state as long as the bean exists, so any subsequent method calls to the stateful bean can update them without needing to make any explicit call to the EntityManager. This might avoid your LazyInitializationException. You can now use em.refresh(user);

I think validation should be done before the transaction is ever initiated.

I have faced this situation lately in a variety of guises.

The best I found is to treat the object you have as a value object (which it basically is after the rollback). To remove it from the database, find its 'attached' twin using a find by its id (which will not go to the database as it is almost certainly cached) and then remove the object that was returned.

Similar for updates : get a fresh copy and update it.

It is a bit of a hassle, but it avoids long transactions and all the evil locking issues related to that.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!