I would like to know whats the best way to handle org.springframework.dao.EmptyResultDataAccessException in my application.
This error occurs when I try to select records from the database that doesn't exist.
Exceptions(data related) caught in DAO layer should not be passed in Service layer. You would handle all exceptions in dao layer and rethrow them with your custom exceptions like :
public class SomeDaoImpl implements SomeDaoInterface {
public void someDaoMethod() throws Exception {
// code ...
try {
// code
} catch(EmptyResultDataAccessException dataAccessException) {
// rethrow exception
throw new Exception(dataAccessException.toString()); // you can rethrow RuntimeException or Custom exceptions
}
}
}
Related
There's a piece of code that throws a exception:
java.lang.RuntimeException: cn.dev33.satoken.exception.NotLoginException: Invalid Token:ldxutBDDKBEDa9LjWNTKLFbW7g7B86qU.
And then it goes into handleRuntimeException rather than returnNotLoginException method.
#Component
#Slf4j
#Primary
public class MyLockKeyBuilder extends DefaultLockKeyBuilder {
#Override
public String buildKey(MethodInvocation invocation, String[] definitionKeys) {
String key = super.buildKey(invocation, definitionKeys);
Object loginId = StpUtil.getLoginId(); // throw a exception
key = loginId.toString();
return key;
}
}
#ControllerAdvice(basePackages = "com.test")
#Slf4j
public class GraceExceptionHandlerApp {
#ResponseStatus(HttpStatus.UNAUTHORIZED)
#ExceptionHandler(value = NotLoginException.class)
#ResponseBody
public JSONObject returnNotLoginException(NotLoginException e) {
e.printStackTrace();
String message = e.getMessage();
ResponseStatusEnum failed = ResponseStatusEnum.UNAUTHORIZED;
failed.setMsg(message);
return ZheliResult.exception(failed);
}
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(RuntimeException.class)
#ResponseBody
public JSONObject handleRuntimeException(RuntimeException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
return ZheliResult.errorCustom(ResponseStatusEnum.FAILED);
}
...
}
I want it goes into the returnNotLoginException method, could anyone tell me how to do it?
UPDATE
I've made a mistake, really, for I didn't offer enough info.
Missed Info:
My application was a distributed system and services to invoke another via rpc communication. MyLockKeyBuilder was on the provider service, and GraceExceptionHandlerApp was on the comsumer service.
When the provider service throw a exception and before it being passed to the comsumer sevice, it would be filter by a Filter called ExceptionFilter, which wrap the exception that the comsumer side doesn't recognize to RuntimeException, to avoid serialization issue.
Finally I solved this problem by rewritting the ExceptionFilter class to allow original NotLoginException to be passed to the consumer side.
NotLoginException is the inner exception of your RuntimeException. If you want your controller advice to handle it, catch the RuntimeException buildKey and throw its inner exception.
Based on your question,
java.lang.RuntimeException: cn.dev33.satoken.exception.NotLoginException: Invalid Token:
Your exception type is java.lang.RuntimeException & cause of exception is NotLoginException.
Controller advice will invoke respective method when type of exception matches & not cause of exception.
So if you really want to invoke returnNotLoginException, then you need to throw NotLoginException in your logic instead of throwing RuntimeException.
Something like:
..
throw new NotLoginException("exception"); //in your StpUtil.getLoginId();
..
I have a method Service method - SampleServiceImpl().
I have declared the Service method as following:
#Transactional
#Override
public sampleDTO SampleServiceImpl(SampleDTO sampleDTO) throws SampleException, ParseException {
// Method call to methodA.
createDataA(sampleDTO);
// Method call to methodB.
createDataB(sampleDTO);
return sampleDTO ;
}
Here the DataA is created and it does not throw any exception.
But in DataB, we are trying to create DataB based on DataA. Due to some logic, we cannot create DataB. So, we throw a Sample Exception like:
count = checkIfDataBExisting(sampleDTO);
if(count == 1){
throw new SampleException(ErrorConstants.DATA_B_EXISTING);
}
But the problem is, the transaction that got committed during createDataA(sampleDTO) method call, does not get rolled Back.
Why does this don't actually work? I'm bit confused with this behavior.
EDIT:1
createData1(sampleDTO) method -
private ADTO createDataA(SampleDTO sampleDTO) throws SampleException{
ADTO aDTO = null;
try {
//CREATE NEW WORK DRIVER
aDTO = createNewDataA(sampleDTO);
//Other arbitary database transactions occurs after creation of AData.
} catch (SampleException exc) {
SampleException newException = new SampleException (exc.getExceptionObject().getExceptionCode(), exc);
throw newException ;
}
return aDTO;
}
EDIT2:
SampleException Declaration -
public class SampleException extends Exception{
//Definitions and Declarations.
}
By default declarative transactions rollback only for Runtime exceptions.
SampleException has to be RuntimeException for the Transaction to be rolled back.
Your method signature:
public sampleDTO SampleServiceImpl(SampleDTO sampleDTO) throws SampleException, ParseException
makes me think that SampleException is a checked one.
See the documentation for #Transactional:
If no rules are relevant to the exception, it will be treated like DefaultTransactionAttribute (rolling back on RuntimeException and Error but not on checked exceptions).
You can either make SampleException extends RuntimeException or set the rollbackFor attribute of #Transactional.
In my application I have different layers like the rest layer, service layer and DB layer, according to business scenarios I am trowing different business exceptions from the service layer.
But now, I have to set different HTTP codes like 400, 403, 409, 412.. to REST responses.
How can I set different HTTP status codes based on different scenarios?
Which is the most feasible way like: aspect, exception mapper, or ....?
Since I can set HTTP status only once in rest layer (
referred this ), I am not able to map to different HTTP codes because my exception is from service layer.
My exception class looks like this:
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BusinessException(ErrorEnumeration error) {
}
public BusinessException(Exception e, ErrorEnumeration error) {
}
}
and the exception will be thrown from the service like this:
throw new BusinessException(ErrorEnumeration.VALIDATION_FAILED);
Please help by suggesting a solution
You can use exceptions defined in jax-rs or you can use your own exceptions. Fist catch your business exceptions and convert them to jax-rs versions. For example, for 404 you can throw javax.ws.rs.NotFoundException.
You can also write your own exceptions by extending them from javax.ws.rs.ClientErrorException
Here is an example for 409-Conflict status exception
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.core.Response;
public class ConflictException extends ClientErrorException{
public ConflictException(Response.Status status) {
super(Response.Status.CONFLICT); // 409
}
}
Update
Most simple and feasible way is catching your business exceptions and re-throw them with jax-rs exceptions.
try{
businessService.executeBusinessRule();
}catch (BusinessException e){
// It is better if your BusinessException has some child class to handle
if(e.getError() == ErrorEnumeration.VALIDATION_FAILED){
throw new BadRequestException();
}else{
throw new ConflictException();
}
}
If you are using spring you can always catch these exceptions using aop.
#Aspect
public class BusinessExceptionInterceptor{
#AfterThrowing(pointcut = "execution(* com.your.service.packge..* (..))", throwing = "e")
public void errorInterceptor(BusinessException e) {
// re-throw again...
}
Update 2
Also it is better to define a new exception instead of reusing same exception with different state. You can define a new ValidationException which extends from BusinessException like this.
public class ValidationException extends BusinessException{
public ValidationException() {
super(ErrorEnumeration.VALIDATION_FAILED);
}
}
By using this way you can still handle all the BusinessException but it is easier to identify or map them to Jax-rs exceptions.
I have a JBOSS ESB that uses a standard out of the box EJBProcessor action. How do I get hold of an exception, if the exception be thrown in the method call that was run in the EJB?
Any advice would be helpful.
You can subclass EJBProcessor and override the process method like this:
#Override
public Message process(Message pMessage) {
try {
pMessage = super.process(pMessage);
} catch (Throwable wEx) {
handleProcessError(pMessage, wEx);
}
return pMessage;
}
You will more than likely catch an instance of ActionProcessingException, and you can look at the cause to see the exception in your EJB.
Your action configuration in your jboss-esb.xml will remain exactly the same, except you will substitute the name of your subclass for org.jboss.soa.esb.actions.EJBProcessor.
I need to extract data from a DB2 table, run some processing on each returned row and output to a flat file. I'm using iBatis but found that using the queryForList I started getting out of memory errors, I'll be looking at 100k+ rows of data increasing.
I've looked at using queryWithRowHandler instead but the iBatis RowHandler interface doesn't throw an exception from its handleRow function so if it gets an error I can't properly report it back and stop iterating the rest of the data. It looks like I can throw a RuntimeException but that doesn't strike me as a neat way of doing things.
I'd like to be able to stop processing while throwing a meaningful Exception indicating whether the error occurred on the data manipulation, the file access or whatever.
Has anyone had experience with this approach or have an alternative solution using iBatis. I know I could look to do this without iBatis, just using JDBC, but as iBatis is used for all other DB access in my app I'd like to avail of this architecture if possible.
1) Create your own RowHandler interface with checked Exceptions in signature:
public interface MySpecialRowHandler {
public void handleRow(Object row)
throws DataException, FileException, WhateverException;
}
2) Inherit (or even better, delegate ) from SqlMapDaoTemplate to add a new method that will manage your own handler with the same Exceptions in signature:
public class MySpecialTemplate extends SqlMapDaoTemplate {
...
public void queryWithRowHandler(String id,
final MySpecialRowHandler myRowHandler
) throws DataException, FileException, WhateverException {
// "holder" will hold the exception thrown by your special rowHandler
// both "holder" and "myRowHandler" need to be declared as "final"
final Set<Exception> holder = new HashSet<Exception>();
this.queryWithRowHandler(id,new RowHandler() {
public void handleRow(Object row) {
try {
// your own row handler is executed in IBatis row handler
myRowHandler.handleRow(row);
} catch (Exception e) {
holder.add(e);
}
}
});
// if an exception was thrown, rethrow it.
if (!holder.isEmpty()) {
Exception e = holder.iterator().next();
if (e instanceof DataException) throw (DataException)e;
if (e instanceof FileException) throw (FileException)e;
if (e instanceof WhateverException) throw (WhateverException)e;
// You'll need this, in case none of the above works
throw (RuntimeException)e;
}
}
}
3) Your business code will look like this:
// create your rowHandler
public class Db2RowHandler implements MySpecialRowHandler {
void handleRow(Object row) throws DataException, FileException, WhateverException {
// what you would have done in ibatis RowHandler, with your own exceptions
}
}
// use it.
MySpecialTemplate template = new MySpecialTemplate(daoManager);
try {
template.queryWithRowHandler("selectAllDb2", new Db2RowHandler());
} catch (DataException e) {
// ...
} catch (FileException e) {
...