I want to update the DB and a server with some new data in one transaction.
I'm using java 8 and i was looking for Java transaction API.
I have found JTA but saw no code example. Can someone please link to one?
I saw this post and post
but there were about DB transaction and had no code example.
I want to make the transaction at a higher lever than the DAL level
as it wraps the peer-server update as well.
private void updateDbAndServer() throws Exception {
if (rulesUiRepository.updateRulesUiSnapshot(nonSplittedRulesSnapshot) == -1)
throw new RuntimeException("cannot save ui snapshot in DB");
Map<RuleConditionBl, RtRule> splittedMap = nonSplittedRulesSnapshot.toSplittedMap();
anotherService.updateConfig(splittedMap);
}
I think the Spring Transaction API is very easy to use and allows you to concentrate about your businesses logic. The API is used in a declarative form. Check this for more info.
Related
I'm having a problem where I need to perform several slow HTTP requests on a separate thread after having written to the database using a JpaRepository. The problem is that doActualJob() blocks while waiting for a series of futures to resolve. This seems to prevent the underlying Hibernate session from closing, causing the application to run out of connections shortly after.
How do I write this function so the database connection isn't kept open while doing the blocking I/O? Is it even possible using JpaRepositories, or do I need to use a lower level API like EntityManager/SessionFactory?
#Service
class SomeJobRunner {
private final SomeJobRepository mSomeJobRepository; //extends JpaRepository
#AutoWired
public SomeJobRunner(final SomeJobRepository someJobRepository) {
mSomeJobRepository = someJobRepository;
}
#Async
public void doSlowJob(final long someJobId) {
SomeJob someJob = mSomeJobRepository.findOne(someJobId);
someJob.setJobStarted(Instant.now());
mSomeJobRepository.saveAndFlush(someJob);
doActualjob(); // Synchronous job doing several requests using Unirest in series
someJob = mSomeJobRepository.findOne(someJobId);
someJob.setJobEnded(Instant.now());
mSomeJobRepository.saveAndFlush(someJob);
}
Well - non-blocking database IO is not possible in Java/JDBC world in a standard way .To put it simply - your Spring data repository would be eventually using JPA ORM Implementation ( likes of Hibernate) which in turn will use JDBC to interact with the database which is essentially blocking in nature. There is work being done on this currently by Oracle (Asynchronous Database Access API ) to provide a similar API as JDBC but non-blocking. They intend to propose this as a standard. Also there is an exciting and parallel effort by Spring guys on this namely R2DBC – Reactive Relational Database Connectivity. They have actually integrated this with Spring data as well (link) so that may help you integrate in your solution. A good tutorial by Spring on this can be found here.
EDIT: As of 2022 Hibernate has reactive option as well
I would suggest to write in the database using a separate JTA transaction. Do do so, define a methode like
#Transactional(Transactional.TxType.REQUIRES_NEW)
public void saveJobStart(final long someJobId) {
SomeJob someJob = mSomeJobRepository.findOne(someJobId);
someJob.setJobStarted(Instant.now());
mSomeJobRepository.saveAndFlush(someJob);
}
Of course it is not quite the same. If doActualjob() fails, in your case, the database won't persist the start date. In my proposal, it will persist it. To compensate, you need to remove the start date in a catch bloc in doSlowJob, within a new transaction, and then rethrow the exception.
I am using mongodb with spring mvc for a REST API. I have a controller which adds comments for events. This controller use a method of my event service. And implementation of them are below. However It seems to me as if my mongodb connection works without transaction. I have 1 insert and 1 update in that my controller adding comment. If update of event throws an error, the request commits inserting.Why is it not doing rollback? How can I fix this problem?
public Comments addComment(Comments comment) throws Exception{
comment.setCommentDate(SowUtil.getDateTimeDB());
comment.setCommenter(personService.findPersonById("531cc90e3c37b20bef47dfc7"));
mongoTemplate.insert(comment);
Events adv = findEventById(comment.getEventId());
adv.addComment(comment);
eventService.save(adv);
return comment;
}
MongoDB does not have the same notion of transactions as RDBMS. Based on your example, it looks like you are using two documents.
MongoDB has an article on 2-phase commit patterns involving multiple documents (
http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/). Perhaps, that can help you implementing a rollback?
I believe that Spring also has a notion of transaction support for MongoDB (http://docs.spring.io/spring-integration/reference/html/mongodb.html).
Here consistent request in Mongo I found how to handle consistent request in java code.
DB db...;
db.requestStart();
try {
db.requestEnsureConnection();
code....
} finally {
db.requestDone();
}
Does spring-data cares about it. Or I should do it manually as using plain java driver?
In spring data to execute with in same connection, you should use mongoTemplate.executeInSession(DbCallback action). Please take a look at the Spring documentation Section 5.1.1 about Execution Callback.
Context of problem I want to solve: I have a java spring http interceptor AuditHttpCommunicationInterceptor that audits communication with an external system. The HttpClieant that does the communication is used in a java service class that does some business logic called DoBusinessLogicSevice.
The DoBusinessLogicSevice opens a new transaction and using couple of collaborators does loads of stuff.
Problem to solove: Regardless of the outcome of any of the operations in DoBusinessLogicSevice (unexpected Exceptions, etc) I want audits to be stored in the database by AuditHttpCommunicationInterceptor.
Solution I used: The AuditHttpCommunicationInterceptor will open a new transaction this way:
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
new TransactionTemplate(platformTransactionManager, transactionDefinition).execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// do stuff
}
});
Everything works fine. When a part of DoBusinessLogicSevice throws unexpected exception its transaction is rolled back, but the AuditHttpCommunicationInterceptor manages to store the audit in the database.
Problem that arises from this solution: AuditHttpCommunicationInterceptor uses a new db connection. So for every DoBusinessLogicSevice call I need 2 db connections.
Basicly, I want to know the solution to the problem: how to make TransactionTemplate "suspend" the current transaction and reuse the connection for a new one in this case.
Any ideas? :)
P.S.
One idea might be to take a different design approach: drop the interceptor and create an AuditingHttpClient that is used in DoBusinessLogicSevice directly (not invoked by spring) but I cannot do that because I cannot access all http fields in there.
Spring supports nested transactions (propagation="NESTED"), but this really depends on the database platform, and I don't believe every database platform is capable of handling nested transactions.
I really don't see what's a big deal with taking connection from a pool, doing a quick audit transaction and returning connection back.
Update: While Spring supports nested transactions, it looks like Hibernate doesn't. If that's the case, I say: go with another connection for audit.
I've got kinda problem with JDO persistence of a list of just retrieved objects.
What I want to do is to:
Fetch list of "Orders"
Modify one property "status"
Make bulk update of "Orders"
What I've got so far is "Object with id ... is managed by a different Object Manager".
But wait, I haven't faced such a problem without Spring!
I tried to debug it like this:
List<Orderr> orders = orderDao.findByIdAll(ordersKeys);
for(Orderr o : orders) {
System.out.println(JDOHelper.getPersistenceManager(o).hashCode());
//hashcode is 1524670
o.setSomething(somevalue);
}
orderDao.makePresistentAll(orders); //hashcode inside is 31778523
makePersistentAll does nothing but:
try {
System.out.println(getPersistenceManager().hashCode());
getPersistenceManager().makePersistentAll(entities);
} finally {
getPersistenceManager().close();
}
All my DAOs extend JdoDaoSupport. Pmf is injected and managed by spring.
Finally, here is the question: Why is the persistence manager closed after findByIdAll? Or why do I get new persistence manager instance? My findByIdAll method doesn't call close on persistence manager, of course.
Of course if I call makePersistent for each "order" it works well. But it breaks layering of business and database logic...
UPD
Just found out that all calls to makePersistentAll aren't working at all after migration to spring managed PersistenceManager. Before spring I used plain old PMF.get() helper and everything was shiny!
If your app remains live in response to a HTTP request for longer than 30 seconds, it will be killed. Part of the mode of operation of GAE is that your apps are not long-lived. At all.
Though you wouldn't do this on a site of your own, you'll have to get used to having only short-term access to your DB session manager. A lot of time is sometimes needed to re-open it for every transaction, but that's how GAE makes the process scalable. If you really have a lot of traffic, it can run your application in parallel on several servers.
This is kind of magic. Everytime I ask here a question I know the answer to my question within 24 hours after post.
Of course, factory by its meaning should always create a new pm instance. Now I save reference to my old pm (like I did before spring jdo daos) and everyting is ok.