Let the presentation layer (JSF) handle business exceptions from the service layer (EJB)
Update the EJB method of the provided entity (using CMT):
@Override @SuppressWarnings("unchecked") public boolean update(Entity entity) throws OptimisticLockException { // Code to merge the entity. return true; }
If a concurrent update is detected, javax. XML is thrown persistence. Optimisticlockexception, the update will be handled precisely by the caller (managed bean)
public void onRowEdit(RowEditEvent event) { try { service.update((Entity) event.getObject()) } catch(OptimisticLockException e) { // Add a user-friendly faces message. } }
But doing so will have an impact on javax. XML on the presentation layer The persistence API creates additional dependencies, a design smell that leads to tight coupling
Which exception should be wrapped so that the tight coupling problem is completely omitted? Or is there a standard way to handle this exception, which in turn does not result in enforcing any service layer dependencies on the presentation layer?
By the way, I find it awkward to catch this exception in EJB (on the service layer itself) and then return a flag value to the client (JSF)
Solution
Create a runtime exception specific to the custom service layer, which is annotated with @ applicationexception with rollback = true
@ApplicationException(rollback=true) public abstract class ServiceException extends RuntimeException {}
Create some specific subclasses for general business exceptions, such as constraint violations, required entities, and optimistic locks
public class DuplicateEntityException extends ServiceException {}
public class EntityNotFoundException extends ServiceException {}
public class EntityAlreadyModifiedException extends ServiceException {}
Some of them can be thrown directly
public void register(User user) { if (findByEmail(user.getEmail()) != null) { throw new DuplicateEntityException(); } // ... }
public void addToOrder(OrderItem item,Long orderId) { Order order = orderService.getById(orderId); if (order == null) { throw new EntityNotFoundException(); } // ... }
Some of them require global interceptors
@Interceptor public class ExceptionInterceptor implements Serializable { @AroundInvoke public Object handle(InvocationContext context) throws Exception { try { return context.proceed(); } catch (javax.persistence.EntityNotFoundException e) { // Can be thrown by Query#getSingleResult(). throw new EntityNotFoundException(e); } catch (OptimisticLockException e) { throw new EntityAlreadyModifiedException(e); } } }
In EJB jar XML as the default interceptor (on all EJBs), as shown below
<interceptors> <interceptor> <interceptor-class>com.example.service.ExceptionInterceptor</interceptor-class> </interceptor> </interceptors> <assembly-descriptor> <interceptor-binding> <ejb-name>*</ejb-name> <interceptor-class>com.example.service.ExceptionInterceptor</interceptor-class> </interceptor-binding> </assembly-descriptor>
As a general hint, in JSF, you can also have a global exception handler that adds only one faces message Starting with this kickoff example, you can do the following in the yourexceptionhandler#handle() method:
if (exception instanceof EntityAlreadyModifiedException) { // Unwrap if necessary. // Add FATAL faces message and return. } else { // Continue as usual. }