Say I have following generic dao deployed as a local SLSB:
public interface CrudService {
public <T> T create(T t);
public <T> T find(Object id, Class<T> type);
public <T> T update(T t);
public void delete(Object t);
public List<Object> findByNamedQuery(String queryName);
public List<Object> findByNamedQuery(String queryName, int resultLimit);
public List<Object> findByNamedQuery(String namedQueryName, Map<String, Object> parameters);
public List<Object> findByName开发者_开发问答dQuery(String namedQueryName, Map<String, Object> parameters, int resultLimit);
}
This DAO is used from many other SLSB services. I'd like to abstract whole persistence layer (all operations and exceptions) from business logic. I created an interceptor with @AroundInvoke method like below and put it on DAO's class level:
@AroundInvoke
public Object wrapExceptions(InvocationContext context) throws Exception {
try {
return context.proceed();
} catch(Exception e) {
throw mapToApplicationException(e)
}
}
No exception is caught and therefore mapped having default implementations of dao methods. But if I use flush at the end of persist, update and delete methods it works - and that's ok.
Now my question is: is it the only way to get it working? I know that calling flush is quite heavy and if I need to call let's say update multiple times it's gonna be a serious bottleneck.
Edit: another option is to use BMT, but it causes all facade methods to be polluted with tx.begin() etc...
EDIT after Kris Babic answer:
I have some doubts according to Kris proposal. Dealing with PersisteceExceptions in service layer causes mixing of layers transparency. But this is not the worst for me. Say that I have Service Facade using set of my services or DAOs. Service Facade method need to be executed on it's own transaction so I'd use CMT and mark it with @TransactionAttribute(REQUIRES_NEW). Doing that way there is no place to have exceptions handling point (interceptor won't work, because transaction is still ongoing - this is the same case as above). So I see two ways: either have all facade methods use BMT and deal with all the tx.begin(), tx.commit() etc. stuff, or have another "Facade for Facade" having @TransactionAttribute(NEVER) and then call transactional facade and handle it's exceptions.
I guess it all depends on what you are trying to do. If you are expecting all data to be immediately inserted into the database and any problems caused by that insertion to be immediately available, then you really only have two options: specifically call flush() on the EntityManager which will cause all batch datasource operations to be executed, or specify that a transaction to start and end at the DAO method (there are different options for that, but I won't go into them here). Both of these options can have negative affects on performance and on successful business transaction definition.
One of the features in a JPA implementation is the idea of a PersistenceContext. The PersistenceContext is a set of managed Entities. The lifecycle of these entities are managed by the EntityManager. Part of EntityManager's responsibility is to manage when changes to the PersistenceContext get synchronized with the data source. It is possible that the EntityManager batches together a number of changes to the PersistenceContext and synchronizes them at a later point such as during the transaction commit. In this case, if there is a problem with the data being inserted into the database, you may not see the exception being raised until sometime later. A call to em.flush() causes the data source synchronization to occur immediately, which in turn raises any exceptions which may occur during that database insert.
One good approach to exception handling is to only add it where you need it. Adding it to a bottom layer method that throws RuntimeExceptions is not necessarily a good candidate. If the idea is to add consistant error handling, such as the conversion to a common RuntimeException, then you would want to do so at a common entry point, such as the business layer or similar. This allows you to limit the overall amount of exception handling you need to maintain, especially since errors can really occur anywhere in the application. I would also suggest that you have exception handling at your main transaction entry points as that is where you will need to manage any unexpected exception which may only occur during transaction commit, especially when using optimistic locking.
Just some thoughts, hopefully they help.
精彩评论