You need the way to throw an exception in updateData()
method to rollback a transaction. And you need to not rollback persistError()
transaction at the same time.
@Transactional(rollbackFor = Exception.class)
public void updateData() {
try{
processAndPersist(); // <- db operation with inserts
int i = 1/0; // <- Runtime error
}catch (Exception e){
persistError()
trackReportError(filename, e.getMessage());
throw ex; // if throw error here, will not work
}
}
Just throwing an error will not help because persistError()
will have the same transaction as updateData()
has. Because persistError()
is called using this
reference, not a reference to a proxy.
Options to solve
- Using self reference.
- Using self injection Spring self injection for transactions
- Move the call of
persistError()
outside updateData()
(and transaction). Remove @Transactional
from persistError()
(it will not work) and use transaction of Repository
in persistError2Db()
.
- Move
persistError()
to a separate serface. It will be called using a proxy in this case.
- Don't use declarative transactions (with
@Transactional
annotation). Use Programmatic transaction management to set transaction boundaries manually https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch11s06.html
Also keep in mind that persistError()
can produce error too (and with high probability will do it).
Using self reference
You can use self reference to MyService to have a transaction, because you will be able to call not a method of MyServiceImpl
, but a method of Spring proxy.
@Service
public class MyServiceImpl implements MyService {
public void doWork(MyService self) {
DataEntity data = loadData();
try {
self.updateData(data);
} catch (Exception ex) {
log.error("Error for dataId={}", data.getId(), ex);
self.persistError("Error");
trackReportError(filename, ex);
}
}
@Transactional
public void updateData(DataEntity data) {
persist(data); // <- db operation with inserts
}
@Transactional
public void persistError(String message) {
try {
persistError2Db(message); // <- db operation with insert
} catch (Exception ex) {
log.error("Error for message={}", message, ex);
}
}
}
public interface MyService {
void doWork(MyService self);
void updateData(DataEntity data);
void persistError(String message);
}
To use
MyService service = ...;
service.doWork(service);