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...
Related
OK, what I'm trying to accomplish is the following:
In a java enterprise bean I want to move a file to a different directory unless a database operation fails (namely, I want to store the correct location of the file in the database. Now if something went wrong within the transaction and it's rolled back, my database points to a wrong location for the file if I already moved the file. Not good.).
I tried to fire an event with an observing method using the transaction phase AFTER_SUCCESS to move the file. So far so good. But the file move could also fail (maybe I don't have access to the target directory or something like that) and I want to write that failure into the data base as well. Unfortunately it seems like the observer method does not provide me with a transaction and my method call fails.
Is the idea of calling a service method from the observing method a bad one? Or am I doing it wrong?
Generally, you should work with transactional resource firstly and then with non-transactional. The reason is that you can roll-back transactional resource but you cannot roll-back non transactional one.
I mean: if you was able to update row in database and then trying to move file and it fails, you can safely roll-back database update. But, if you moving file and it success, but you cannot update database for some reason - you cannot roll-back moved file.
In your particular case I would suggest to not actually move the file, but copy it instead. So that in database you will always have actual location of new copy. And in different thread you can delete old copies somehow. You need to use copies because IOException can be thrown when actual file was moved and when you rollback transaction in database you will end up with wrong old location. Try to use this approach (using EJB container-managed transactions; you can safely find Spring variant of that):
#TransactionAttribute(REQUIRED)
void move(String newLocation, int fileId) throws CouldNotMoveException, DatabaseException {
try {
database.updateFileLocation(fileId, newLocation);
} catch (Exception exc) {
throw new DatabaseException(exc);
}
try {
file.copyFile(fileId, newLocation);
} catch (IOException exc) {
throw new CouldNotMoveException(exc);
}
}
You will need to create your exceptions like this in order to rollback your transactions (or just use RuntimeException - check documentation on your container about reacting to exception and rollback policies):
#ApplicationException(rollback = true)
public class DatabaseException extends Exception {
// omited
}
#ApplicationException(rollback = true)
public class CouldNotMoveException extends Exception {
// omited
}
Here you client code can react on CouldNotMoveException and write the wrong move in database so you will fullfil your requirements.
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.
I'm implementing simultaneous write into database and Oracle Coherence 3.7.1 and want to make whole operation transactional.
I would like to have a critique on my approach.
Currently, I've created façade class like this:
public class Facade {
#EJB
private JdbcDao jdbcDao;
#EJB
private CoherenceDao coherenceDao;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void updateMethod(List<DomainObject> list) {
jdbcDao.update(list);
coherenceDao.update(list);
}
}
I guess JDBC DAO would not need to do anything specific about transactions, it something happens Hibernate would throw some kind of RuntimeException.
public class JdbcDao {
private void update(List<DomainObject> list) {
// I presume there is nothing specific I have to do about transactions.
// if I don't catch any exceptions it would work just fine
}
}
Here is interesting part. How do I make Coherence support transactions?
I guess I should open coherence transaction inside update() method and on any exceptions inside it I should throw RuntimeException myself?
I currently thinking of something like this:
public class CoherenceDao {
private void update(List<DomainObject> list) {
// how should I make it transactional?
// I guess it should somehow throw RuntimeException?
TransactionMap mapTx = CacheFactory.getLocalTransaction(cache);
mapTx.setTransactionIsolation(TransactionMap.TRANSACTION_REPEATABLE_GET);
mapTx.setConcurrency(TransactionMap.CONCUR_PESSIMISTIC);
// gather the cache(s) into a Collection
Collection txnCollection = Collections.singleton(mapTx);
try {
mapTx.begin();
// put into mapTx here
CacheFactory.commitTransactionCollection(txnCollection, 1);
} catch (Throwable t) {
CacheFactory.rollbackTransactionCollection(txnCollection);
throw new RuntimeException();
}
}
}
Would this approach work as expected?
I know that you asked this question a year ago and my answer now might not be as much as value for you after a year but I still give it a try.
What you are trying to do works as long as there is no RuneTimeException after the method call of coherenceDao.update(list); You might be assuming that you don't have any line of codes after that line but that's not the whole story.
As an example: You might have some deferrable constraints in your Database. Those constraints will be applied when the container is trying to commit the transaction which is on method exit of updateMethod(List<DomainObject> list) and after your method call to coherenceDao.update(list). Another cases would be like a connection timeout to database after that coherenceDao.update(list) is executed but still before the transaction commit.
In both cases your update method of CoherenceDAO class is executed safe and sound and your coherence transaction is not rollbacked anymore which will put your cache in an inconsistent state because you will get a RuneTimeException because of those DB or Hibernate Exceptions and that will cause your container managed transaction to be rollbacked!
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.