EJB3 Getting at original JDBC Errors - java

I am using EJB3 on Glassfish using the default TopLink persistance manager. Within a Session Bean, when the persistence manager catches a DB exception, it marks the transaction to be rolled back, and throws an EJBException, in turn wrapping a RollbackException. Now I was expecting to be able to get the original jdbc exception out of the caused by exception of one of these exceptions, but it is not.
It is important that I do retrieve the original exception, as I need to report back to the users what the problem is, and to do this I need to analyse the SQL error codes.
Does anyone know if it is possible to get this information from Toplink? Or whether Hibernate makes it possible?
Thanks,

I had the same issue. I ended up using the AroundInvoke interceptor method , that way you can catch any exception on the server side , and extract whatever info you want to and wrap it to throw your own exception , and set the EjbContext to rollback the transaction.
I can provide you with an example if you don't come right.

Good question, Ant
I know you want to throw a database exception but when it occurs the application, in most of the time, is not able to restore its initial state or it does not know how to recover from it. So it should be handled as a runtime exception. Some problems in database exceptions includes
database connection failure
query is wrong
table or column does not exist
Above you see the application is not be able to restore its initial state. If you think it is possible restore its initial state so you should use a application exception. Client will get the same application exception thrown by your business method. If you want to be able to get the exact exception thrown by your business method you have two choices:
Use a business delegate pattern to access your EJB
As you know, runtime exception is wrapped by a EJBException, so you shold use something like
Let's suppose you have this Stateless session bean
#Stateless
public class BeanImpl implements Bean {
public void doSomething() {
try {
// some code
} catch(SomeException e) {
throw new EJBException(e);
}
}
}
So you wrap your session bean through a business delegate
public class BeamBusinessDelegate implements Bean {
// your stateless session bean goes here
private Bean bean;
public BeamImpl() {
InitialContext i = new InitialContext();
bean = (Bean) i.lookup(<GLOBAL_JNDI_ADDRESS_OR_RELATIVE_ENVIRONMENT_NAMING_CONTEXT_ADDRESS>);
}
public void doSomething() {
try {
bean.doSomething()
} catch(EJBException e) {
throw e.getCause();
}
}
}
Or you can extends EJBException according to your needs
public class DatabaseException extends EJBException {
}
So in your business method
#Stateless
public class BeanImpl implements Bean {
public void doSomething() {
try {
// some code
} catch(SomeException e) {
throw new DatabaseException();
}
}
}
regards,

The only way I've found to do what I want, is to force the manager to write to the db using manager.flush(), and then catch the PersistenceException that that throws. I can then log the database error as I want, and throw an EJBException to force rollback. Leaving the container to do the flush seems to irretrievably lose any useful messages with TopLink.

I have the same question : how to get the SQL error message generated from JPA?
I haven't found the solution either but, I added this line in my persistence.xml
<properties>
<property name="toplink.logging.level" value="FINE" />
</properties>
and now, I can see the sql commands issued.
Reference :
http://www.jairrillo.com/blog/2008/09/04/introduction-to-jpa-part-1-getting-started/

Related

Can i prevent Spring Boot from catching Mysql exceptions and instead throw my own exceptions?

Hi i'm doing a spring boot server and i realised that in order to register a user, i was checking if it existed first. This means more sql queries and more money for microsoft azure.
I've been trying with some options online, mainly implementing SQLExceptionTranslator or extending it's subclasses and i tried to inject it into jdbc template object by making a CommandLineRunner and seting it there. The thing is: it just doesn't work. I ve put a whole load of prints in my implementation and nothing prints. It's just not using it and keeps defaulting the SQLIntegrityConstraintViolationException to the spring managed DataIntegrityViolationException.
Is there anyway to let me make the mapping instead of allowing spring to map and then catching and resolving it on the #Service?
EDIT: If any of you knows who do i inject my implementation to the jdbc template, that would work perfectly too. I've tried autowiring and setting in the CommandLineRunner, i dont know any other way to set it, and this one failed miserably
EDIT 2 My very basic implementation (basic just testing if it would work)
public class SqlExceptionResolver implements SQLExceptionTranslator {
#Override
public DataAccessException translate(String task, String sql, SQLException sqlEx) {
return new Conflict409Exception(task, sqlEx);
}
}
Then to add to the template
#Autowired
JdbcTemplate t;
#Override
public void run(String... args) throws Exception {
t.setExceptionTranslator(new SqlExceptionResolver());
}

Handling exceptions during a #Transactional method in Spring

I am trying to figure out how to best handle persistence (and potentially other) exceptions in combination with Spring's #Transactional.
For this post I am just going to take the simple example of a user registration, which can cause DataIntegrityViolationException due to duplicate username.
The following things I have tried and they are not really satisfactory to me:
1. Naive approach: Just catch the Exception
val entity = UserEntity(...)
try {
repo.save(entity)
} catch (e: DataIntegrityViolationException) {
// not included: some checks for which constraint failed
throw DuplicateUsername(username) // to be handled by the controller
}
This does not work when in a #Transactional method, since the persistence exceptions won't happen until the transaction is commited, which happens outside my service method in the spring transaction wrapper.
2. Flush the EntityManager before exiting
Explicitly call flush on the EntityManager at the end of my service methods. This will force the write to the database and as such trigger the exception. However it is potentially inefficient, as I now must take care to not flush multiple times during a request for no reason. I also better not ever forget it or exceptions will disappear into thin air.
3. Make two service classes
Put the #Transactional methods in a separate spring bean and try-catch around them in the main service. This is weird, as I must take care to do one part of my code in place A and the other in place B.
4. Handle DataIntegrityViolationException in the controller
Just... no. The controller has no business (hue hue hue) in handling exceptions from the database.
5. Don't catch DataIntegrityViolationException
I have seen several resources on the web, especially in combination with Hibernate, suggesting that catching this exception is wrong and that one should just check the condition before saving (i.e. check if the username exists with a manual query). This does not work in a concurrent scenario, even with a transaction. Yes, you will get consistency with a transaction, but you'll still get DataIntegrityViolationException when "someone else comes first". Therefor this is not an acceptable solution.
7. Do not use declarative transaction management
Use Spring's TransactionTemplate instead of #Transactional. This is the only somewhat satisfactory solution. However it is quite a bit more "clunky" to use than "just throwing #Transactional on the method" and even the Spring documentation seems to nudge you towards using #Transactional.
I would like some advice about how to best handle this situation. Is there a better alternative to my last proposed solution?
I use the following approach in my project.
Custom annotation.
public #interface InterceptExceptions
{
}
Bean and aspect at spring context.
<beans ...>
<bean id="exceptionInterceptor" class="com.example.ExceptionInterceptor"/>
<aop:config>
<aop:aspect ref="exceptionInterceptor">
<aop:pointcut id="exception" expression="#annotation(com.example.InterceptExceptions)"/>
<aop:around pointcut-ref="exception" method="catchExceptions"/>
</aop:aspect>
</aop:config>
</beans>
import org.aspectj.lang.ProceedingJoinPoint;
public class ExceptionInterceptor
{
public Object catchExceptions(ProceedingJoinPoint joinPoint)
{
try
{
return joinPoint.proceed();
}
catch (MyException e)
{
// ...
}
}
}
And finally, usage.
#Service
#Transactional
public class SomeService
{
// ...
#InterceptExceptions
public SomeResponse doSomething(...)
{
// ...
}
}
Voted to (3),like:
#Service
public class UserExtService extend UserService{
}
#Service
public class UserService {
#Autowired
UserExtService userExtService;
public int saveUser(User user) {
try {
return userExtService.save(user);
} catch (DataIntegrityViolationException e) {
throw DuplicateUsername(username);// GlobalExceptionHandler to response
}
return 0;
}
#Transactional(rollbackFor = Exception.class)
public int save(User user) {
//...
return 0;
}
}
You can use a class annotated with #ControllerAdvice or #RestControllerAdvice to handle the exceptions
When a controller throw a exception you can catch it at this class and change the response status to a suitable one or add an extra info of the exception
This method helps you to maintain a clean code
You have numerous examples:
https://www.javainuse.com/spring/boot-exception-handling
https://dzone.com/articles/best-practice-for-exception-handling-in-spring-boo

Does Spring handle Hibernate NonUniqueObjectException?

I'm using Spring MVC to write a RESTful web service, with a database back-end. My save routine is wrapped in a try/catch ...
private void saveData() {
try {
service.saveReport(xmlData);
}
catch(DataAccessException e) { // Spring's DataAccessException
throw new MyException();
}
}
which works for DB errors like a unique constraint violation for instance.
But I'm getting Hibernate's NonUniqueObjectException currently, due to a bug in my code.
I'm curious however, why doesn't Spring's DataAccessException "catch" the NonUniqueObjectException? Is there another Spring Exception which I should use (in addition to DataAccessException) to "catch" this Hibernate Exception?
Or I can certainly catch the Hibernate exception myself. But I didn't know if Spring would/could.
UPDATE -------
My service.save(xmlData) actually does more than just "save" but first does some processing, copying the XML data into the correct (list of) #Entity beans, then looping through the Entity beans, saving each one. The Hibernate Exception occurs during setting up the Entity beans themselves, not during the actual dao.save(entityBean).
Thanks!
Chris
Your DAO neeeds to be proxied to allow for Spring automatic exception translation. Either add the #Repository annotation to your DAO or explictly configure a PersistenceExceptionTranslationPostProcessor bean post processor to your configuration

Transaction not rollbacked

I have a set of operation i would like to be rollbacked if there are an error.
My class
public class BSException extends RuntimeException{
...
}
public class saleFacade{
public update(){
for (){
try{
renewSale();
}
catch(BSException){
logger.error();
}
}
}
#Transactional
public renewSale(){
try{
findSale(); // read only Transactional
xxx.renewSpecialSale();
}
catch(Exception e){
logger.error(...);
}
}
}
public class xxx(){
public void renewSpecialSale(){
payFee(); //write to db
if(error){
throw new BSException();
}
}
#Transactional(propagation = Propagation.REQUIRED)
public payFee(){
try{
...
}
catch(BsException e){
...
}
catch(Exception e){
...
}
}
}
#Configuration
#EnableTransactionManagement
public class DBConfiguration{
#Bean(name = "dataSource")
public BasicDataSource dataSource(){
...
}
}
Inn renewSpecialSale error is throw.
In the renewSale method, if there is an error, i would like to rollback.
Right now nothing is rollbacked
any idea?
If you catch the exception before it leaves the method, then there is no way the proxy wrapping the method can know that an exception was thrown.
Either remove the try-catch entirely or rethrow the exception so that the exception can leave the method that you marked #Transactional (and get intercepted by the proxy), and the rollback will take place.
I recommend removing exception-handling from all these methods. Set up a central exception handler so that anything thrown from the controllers gets caught and logged, and otherwise let exceptions get thrown.
Make sure each of these classes that does something transactional is annotated separately, if you annotate on the method-level then each method that does something transactional should be annotated. Calling a transactional method from a non-transactional method on the same object doesn't go through the proxy (the proxy intercepts only those calls coming in from outside the object, and only then if that method is marked as transactional) so it isn't part of a transaction (+1 to Peter's answer for pointing this out).
I have no idea what your error flag is doing, it seems odd. Spring services shouldn't have state. If you fix the exception-handling you shouldn't need error flags.
The problem comes from your nesting of Method calls and the usage of #Transactional. By default Springs Transaction Management
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Spring Transaction Management
This means, that the call of
xxx.payFee()
is not surrounded by a transaction when it's called through
saleFacade.update() -> saleFacade.renewSale() -> xxx.renewSpecialSale()
As far as I got it, you have at least these options
Mark xxx.renewSpecialSale() as #Transactional
Mark saleFacade.update() as #Transactional
Mark both classes xxx and saleFacade #Transactional
Also you can create your own custom exception class and throw it.
Just throw any RuntimeException from a method marked as #Transactional.
By default all RuntimeExceptions rollback transaction whereas checked exceptions don't. This is an EJB legacy. You can configure this by using rollbackFor() and noRollbackFor() annotation parameters:
#Transactional(rollbackFor=Exception.class)
This will rollback transaction after throwing any exception.
#Transactional(rollbackFor = MyCheckedException.class)
public void foo() {
throw new RuntimeException();
}
remove try catch block from your method and put below line of code
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
use this line of code insted of
#Transactional(propagation = Propagation.REQUIRED)
In above code spring container handle whole transaction management and here if we provide rollback attribute it automatically manage if any kind of exception occur it will rollback your transaction
and make sure that you have entry of transaction in your configuration file as below
<tx:annotation-driven />
and also
I hope it will sure help you

Hibernate Validator Exceptions

I'm using Hibernate Validator in an application with EJB and Spring MVC. I'm using JBoss 5, Hibernate 3 and Spring MVC version 3.
I'd like to try and catch validation exceptions at Spring Exception Resolver level and generate an error message based on the InvalidStateException message. I don't want to put exception handling logic in the data access layer; I want this to be driven by annotations and just handle the validation errors in one centralized place.
The domain model is being correctly verified and an exception of type InvalidStateException is thrown as expected when I try to create an invalid entity (e.g. violate a length constraint on a field). At the point of trying to catch the exception in my instance of a Spring Exception resolver, I find my original exception has disappeared and a javax.ejb.EJBTransactionRolledbackException has taken its place. The original exception is not in the caused by list.
My first guess was that org.hibernate.validator.InvalidStateException wasn't annotated with ApplicationException, so I installed a Hibernate event listener, caught the original exception and rethrew it as a new exception annotated with ApplicationException. This has no effect.
One further complexity is that the web tier is calling EJBs via a Remote interface. I annotated my exception class with WebFault, but to no avail.
What should I do to ensure the exception bubbles all the way up?
Try this?
protected InvalidStateException extractValidationException(Throwable ex) {
Throwable e = ex;
while (e != null) {
if (e instanceof InvalidStateException) {
return (ValidationException) e;
} else if (e instanceof EJBException) {
e = ((EJBException) e).getCausedByException();
} else {
e = e.getCause();
}
}
return null;
}

Categories