I have entity Foo, which maps to sql table with some unique constraints. Thus saving Foo may fail. I am using FooDao to save Foo:
#Repository
public class FooDao
{
#Autowired
private SessionFactory sessionFactory;
#Transactional
#Override
public void add(Foo item) {
sessionFactory.save(item);
}
}
when I call method FooDao#add(Foo) it may fail for two reasons: either because of unique constraint violation (in this case, I know how to handle the problem) or because of some other problem (in this I probably should propagate the exception). How do I distinguish between those two situations?
I could add method find(Foo item) to FooDao and check, whether something like item, which I was trying to add is database. But this would require additional select from database and I am a bit worried about this.
Thats actually SQLState.
do something like this
Catch(HibernateException he){
SQLException sqe = he.getSQLEception();
String sqlState = sqe.getSQLState();
if(sqlState.equals("23000"){
// Handle your exception
}
}
Java doc:
SQLState - an XOPEN or SQL:2003 code identifying the exception
One link I found for ISO sqlStates,
link to reference
But look for exact reference and value..
One obvious (but maybe nasty) solution is that you catch javax.persistence.PersistenceException and parse the error message for "violant" or "constraint".
From my point of view you should do the select/find upfront. Remember that you are using an ORM! Hibernate has caches involved so neither the select/find nor the key contraint error might be the result of an actual db query but the result of an calculation of Hibernate based on your already in cache loaded data.
Sebastian
Catch org.hibernate.JDBCException. This has getErrorCode(). For unique constraint voilation its ORA-00001.
-Maddy
If there is a database exception it gets caught in a HibernateException, which is a checked exception. Spring wraps this in a DataAccessException, which is unchecked. This exception will run up to your Controller and out to the Servlet and end up in a stack trace in the browser and log file. You can catch this exception and print it out so you can see what is happening. This will at least get you to the point where you know what is actually breaking.
Bad keys is probably one issue. But bad values is probably another. Some non-null fields are null, or something isn't long/short enough etc. Fixing this probably involves validation. You can use the Hibernate Validator. You give your fields some nifty annotations and then you get validation errors in java before you even get to the database - errors happen faster.
Related
We have an application with three databases. Two of them are only very seldomly updated. We tried JPA to create transactions around it and it worked for the databases, but grails then did not work on different places (gsp related I am told). This was tried quite a while ago (and not by me).
Due to delivery pressure we needed a solution that at least works for us, so I created a new aspect for the methods changing data in multiple databases. I got this to work, it is a fairly simple approach.
In the aspect we request to start a transaction for each data source, by calling getTransaction(TransactionDefinition def) with the propagation set to REQUIRES_NEW. We then proceed and finally rollback or commit depending on the outcome of the call.
However, one test flow failed. This is the scenario where the code requests a rollback by calling TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(). Of the three TransactionStatusses obtained initially, none actually returns isRollbackOnly() with true. However calling TransactionAspectSupport.currentTransationStatus().isRollbackOnly() does return true. So this seems to point to a different transaction status.
I have not been able to figure out how to make this work, other than checking this additional status. I could not find a way to change the currentTransactionStatus to the one of created TransactionStatus. Looking at the TransactionTemplate implementation, I seem to do things correctly (it also just calls getTransaction() on the datasource).
The code calling the decorated method has specified #Transactional(propagation=Propagation.NOT_SUPPORTED), so I expected no currentTransactionStatus, but one is there.
However, if it is not there the proxied code will not be able to request a rollback the standard way, which I want to be able to fix.
So the question is, how to start a transaction correctly from an Aspect so that the currentTransactionStatus is set correctly or how to set the currentTransactionStatus to what I think is the correct one.
Regards,
Wim Veldhuis.
I finally figured it out.
#Transactional leads to a different code path, where eventually TransactionAspectSupport.invokeWithinTransaction is invoked. This method will set up the current transaction correctly.
So in order to make my approach working, I needed to derive from TransactionAspectSupport, do a number of cast operations so I could get to the correct values for the invokeWithinTransaction call, and within the guarded function block use getTransaction(def) to obtain txns for the OTHER databases. I have choose the most important database to be the one used for invoke...
To make it work I had also to provide a TransactionAttributeSource, that returned my default transaction attributes.That one is stored into the TransactionAspectSupport base class during initialization.
#Around("#annotation(framework.db.MultiDbTransactional)")
public Object multiDbTransaction(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// Get class and method, needed for parent invocation. We need to cast to the actual
// implementation
MethodInvocationProceedingJoinPoint mipJoinPoint = (MethodInvocationProceedingJoinPoint) proceedingJoinPoint;
MethodSignature signature = (MethodSignature) mipJoinPoint.getSignature();
Class<?> clazz = mipJoinPoint.getTarget().getClass();
Method method = signature.getMethod();
return invokeWithinTransaction(method, clazz, new InvocationCallback() {
#Override
public Object proceedWithInvocation() throws Throwable {
// This class will create the other transactions, not of interest here.
MultiDbTxnContext ctx = new MultiDbTxnContext();
ctx.startTransactions();
/*
* We have started the transactions, so do the job. We mimic DEFAULT spring behavior
* regarding exceptions, so runtime exceptions roll back, the rest commits.
*/
try {
Object result = proceedingJoinPoint.proceed();
ctx.finishTransactions();
return result;
} catch (Error | RuntimeException re) {
ctx.rollbackTransactions();
throw re;
} catch (Throwable t) {
ctx.commitTransactions();
throw t;
}
}
});
}
I have some function works with database.
I have set a try/catch for error handling here, and display a message, It works fine.
Now the class calling this delete function need to know if there is a error or not. In my case : refresh the GUI if success, nothing to do if fail (as there already show up a message message dialog).
I come up a idea to return boolean in this function.
public static Boolean delete(int id){
String id2 = Integer.toString(id);
try {
String sql =
"DELETE FROM toDoItem " +
"WHERE id = ?;";
String[] values = {id2};
SQLiteConnection.start();
SQLiteConnection.updateWithPara(sql, values);
} catch (SQLException e) {
Main.getGui().alert("Fail when doing delete in DataBase.");
System.out.println("Exception : "+ e.getMessage());
return false;
}
return true;
}
Don't know if this is good or bad, please tell.
EDIT :
Here is more detail for How do I use :
Let's say the code above is inside Class A,
in Class B :
public boolean deleteItem(int id){
int i = index.get(id);
if(theList[i].delete()){ //<---- here is the function from Class A
theList[i] = null;
index.remove(id);
retutn true;
}
retutn false;
}
I need to pass the boolean in more than one class, I don't know if that can better through...
in Class C :
public void toDoList_deleteItem(){
MyButton btn = (MyButton)source;
int id = btn.getRefId();
List toDoList = Main.getToDoList();
if(toDoList.deleteItem(id)){ //<-------function in Class B
Main.getGui().refresh();
}
}
Edit 2 :
I have notice the question is somehow more likely asking "What should I handle a Exception at database Layer that affect to GUI Layer ?"... Something like that. Please correct me if the question title should be edit.
It looks like you are returning a boolean status to indicate that an exceptional condition had occurred. Generally, this is not a good practice, for two reasons:
It encourages an error-prone way of handling exceptions - it is very easy to miss a status check, leading to ignored errors
It limits your API's ability to report errors - a single pass/fail bit is not always sufficient, it may be desirable to pass more information about the error.
A better approach would be to define an application-specific exception, and use it in your API. This forces the users of your API to pay attention to exceptional situations that may happen, while letting you pass as much (or as little) additional information as you find necessary. At the same time, your code does not get polluted with if (!delete(id)) { /* handle error */ } code on each API call, shrinking your code base, and improving its readability.
Can you tell me more about "define an application-specific exception", or show some code example please?
Here is how I would do it:
public class DataAccessException extends Exception {
... // Define getters/setters for passing more info about the problem
}
...
public static void delete(int id) throws DataAccessException {
try {
... // Do something that may lead to SQLException
} catch (SQLException se) {
// Do additional logging etc., then
throw new DataAccessException("Error deleting "+id, se);
}
}
Note: It is common to give custom exceptions four constructors mirroring the constructors of the Exception class to allow exception chaining. The constructors are described here.
As long as you do not want the caller to know what happens, just that it fails (and that failing is part of its intended behavior) you should be fine.
That being said, I am noticing this: Main.getGui().alert("Fail when doing delete in DataBase.");.
It would seem that you are accessing the GUI layer from some other place. This might cause issues should you decide to multi-thread your application. Also, it is usually considered good practice to have your layers not intersect.
Don't return a Boolean, return a boolean. Since this is not an exception / error condition, it is fine.
Exceptions should be used when you don't expect a failure.
In your case, if it's fine for you that a SQLException is thrown and does not affect your program, it's ok to return a boolean.
If the SQLExcetion causing the delete to fail can cause problems in another part of your application it's better to throw an exception.
Edit:
Based on your edits, it seems that you are doing some maintenance and cleaning when an error happens. In such a case I would recommend to use Exceptions better than using booleans to control the execution.
This question is primarly opinion based. Personally I would prefer not to catch the exception at that point.
Depending on what the caller of delete() should do, you might need other resulutions. So you should better add a throw statement and let the calling method decide if the error is critical - or if it can proceed.
Just true and false is not necessary enough to let the caller decide correctly. He won't know if deletion fails due to database errors, due to foreignkey constraints, or something else.
letting the exception bubble up the call stack will provide the caller with the exact error going on, increasing the chance to handle the error in a proper way, or just displaying a custom error message helping the user to take proper actions.
Heres my problem. I am using mybatis using annotations. For doing that I have created my own mybatis Interface and now I want to throw my own custom exception if anything goes wrong executing the query. Here is in code form what I exactly mean.
Consider the following interface I use to encapsulate some SQL queries:
public interface MyMapper {
#Select("SELECT id, title, description, creationDate, modificationDate, owner_id AS \"owner.id\" "
+ " FROM article WHERE article_id = #{id}")
Article getArticle(int id);//If this fails I want my own custom Exception to be thrown
}
As commented if I have a problem with my query or somehow the execution of getArticle(int id) fails I want it to throw my own custom exception not an IBatisException.If that is not possible by can someone suggest the tree hierarchy for Exceptions Since knowing the tree hierarchy would also help me know what all exceptions I can catch. Unfortunately I can't even catch a SqlException if something goes wrong. Only IBatisException and Exception.
Firstly I'd wrap every call to MyMapper in DAO instance and keep out exception hierarchy in specific place(ExceptionHandler):
public class MyDAO {
MyMapper mapper;
public Article getArticle(int id) {
try {
return mapper.getArticle(id);
} catch (PersistenceException e) {//IbatisException is deprecated
throw ExceptionHandler.handle(e);
}
}
}
try/catch clause probably would be duplicated in every method, so using Java proxy or AspectJ to refactor exception handling functionality might be an option.
It might be possible to use load time weaving and weave around mapper itself instead of DAO. But I'd stil prefer DAO to leave myself room to extend functionality.
Regarding exception hierarchy MyBatis already has exception hierarchy of it's own. And in case your are using Spring, it has it's own data access exception hierarchy, so you might try out MyBatis-Spring integration. But as documentation states, base DataAccessException is
Root of the hierarchy of data access exceptions discussed in Expert
One-On-One J2EE Design and Development
So you might try to implement something similar based on the aforementioned book.
I'm using Hibernte 4.3.x.
If I try to remove(delete) a object from DB = EM.remove(entity) the object will keep in DB (remove not executed) if Hibernate find references in the managed objects in the Entity Manager Context. (to avoid data inconsistency I guess)
Is it possible with configuration/settings (I found nothing so far) that the transaction throws a exception that remove was not possible because of found references?
(this has nothing to do with cascading/remove)
edit:
Example
EM.remove(ObjectA.getObjectB());
If the transaction is executed. All seams fine no exception is thrown. As a "beginner" I expect that the ObjectB is deleted. But it isn’t. It's still there. The EM "see" that there is still a reference from ObjectA to ObjectB and dosn't execute the delete.
If I say to the EM it should persist or remove something and that is not possible I want know this.
(I know with ObjectA.setObjectB(null) the example works)
If your question is really about if you can catch an exception no matter what... you can use the following:
try
{
// your code...
DB = EM.remove(entity)
}
catch(Exception e)
{
System.out.println(e.printStackTrace());
}
catch(Throwable t)
{ // This one catches unchecked exceptions
System.out.println(t.printStackTrace())
}
This can give you more informtion about what is going on...
We all know, that Spring MVC integrate well with Hibernate Validator and JSR-303 in general. But Hibernate Validator, as someone said, is something for Bean Validation only, which means that more complex validations should be pushed to the data layer. Examples of such validations: business key uniqueness, intra-records dependence (which is usually something pointing at DB design problems, but we all live in an imperfect world). Even simple validations like string field length may be driven by some DB value, which makes Hibernate Validator unusable.
So my question is, is there something Spring or Hibernate or JSR offers to perform such complex validations? Is there some established pattern or technology piece to perform such a validation in a standard Controller-Service-Repository setup based on Spring and Hibernate?
UPDATE: Let me be more specific. For example, there's a form which sends an AJAX save request to the controller's save method. If some validation error occurs -- either simple or "complex" -- we should get back to the browser with some json indicating a problematic field and associated error. For simple errors I can extract the field (if any) and error message from BindingResult. What infrastructure (maybe specific, not ad-hoc exceptions?) would you propose for "complex" errors? Using exception handler doesn't seem like a good idea to me, because separating single process of validation between save method and #ExceptionHandler makes things intricate. Currently I use some ad-hoc exception (like, ValidationException):
public #ResponseBody Result save(#Valid Entity entity, BindingResult errors) {
Result r = new Result();
if (errors.hasErrors()) {
r.setStatus(Result.VALIDATION_ERROR);
// ...
} else {
try {
dao.save(entity);
r.setStatus(Result.SUCCESS);
} except (ValidationException e) {
r.setStatus(Result.VALIDATION_ERROR);
r.setText(e.getMessage());
}
}
return r;
}
Can you offer some more optimal approach?
Yes, there is the good old established Java pattern of Exception throwing.
Spring MVC integrates it pretty well (for code examples, you can directly skip to the second part of my answer).
What you call "complex validations" are in fact exceptions : business key unicity error, low layer or DB errors, etc.
Reminder : what is validation in Spring MVC ?
Validation should happen on the presentation layer. It is basically about validating submitted form fields.
We could classify them into two kinds :
1) Light validation (with JSR-303/Hibernate validation) : checking that a submitted field has a given #Size/#Length, that it is #NotNull or #NotEmpty/#NotBlank, checking that it has an #Email format, etc.
2) Heavy validation, or complex validation are more about particular cases of field validations, such as cross-field validation :
Example 1 : The form has fieldA, fieldB and fieldC. Individually, each field can be empty, but at least one of them must not be empty.
Example 2 : if userAge field has a value under 18, responsibleUser field must not be null and responsibleUser's age must be over 21.
These validations can be implemented with Spring Validator implementations, or custom annotations/constraints.
Now I understand that with all these validation facilites, plus the fact that Spring is not intrusive at all and lets you do anything you want (for better or for worse), one can be tempted to use the "validation hammer" for anything vaguely related to error handling.
And it would work : with validation only, you check every possible problem in your validators/annotations (and hardly throw any exception in lower layers). It is bad, because you pray that you thought about all the cases. You don't leverage Java exceptions that would allow you to simplify your logic and reduce the chance of making a mistake by forgetting to check that something had an error.
So in the Spring MVC world, one should not mistake validation (that is to say, UI validation) for lower layer exceptions, such has Service exceptions or DB exceptions (key unicity, etc.).
How to handle exceptions in Spring MVC in a handy way ?
Some people think "Oh god, so in my controller I would have to check all possible checked exceptions one by one, and think about a message error for each of them ? NO WAY !". I am one of those people. :-)
For most of the cases, just use some generic checked exception class that all your exceptions would extend. Then simply handle it in your Spring MVC controller with #ExceptionHandler and a generic error message.
Code example :
public class MyAppTechnicalException extends Exception { ... }
and
#Controller
public class MyController {
...
#RequestMapping(...)
public void createMyObject(...) throws MyAppTechnicalException {
...
someServiceThanCanThrowMyAppTechnicalException.create(...);
...
}
...
#ExceptionHandler(MyAppTechnicalException.class)
public String handleMyAppTechnicalException(MyAppTechnicalException e, Model model) {
// Compute your generic error message/code with e.
// Or just use a generic error/code, in which case you can remove e from the parameters
String genericErrorMessage = "Some technical exception has occured blah blah blah" ;
// There are many other ways to pass an error to the view, but you get the idea
model.addAttribute("myErrors", genericErrorMessage);
return "myView";
}
}
Simple, quick, easy and clean !
For those times when you need to display error messages for some specific exceptions, or when you cannot have a generic top-level exception because of a poorly designed legacy system you cannot modify, just add other #ExceptionHandlers.
Another trick : for less cluttered code, you can process multiple exceptions with
#ExceptionHandler({MyException1.class, MyException2.class, ...})
public String yourMethod(Exception e, Model model) {
...
}
Bottom line : when to use validation ? when to use exceptions ?
Errors from the UI = validation = validation facilities (JSR-303 annotations, custom annotations, Spring validator)
Errors from lower layers = exceptions
When I say "Errors from the UI", I mean "the user entered something wrong in his form".
References :
Passing errors back to the view from the service layer
Very informative blog post about bean validation