JPA - How to call method for web service after commit - java

Sorry for something that is probably very simple, but I'm new to JPA and I'm not sure where to insert a function call for a method that needs to be called after a new record is committed to a database. I have the following:
#POST
#Override
#Consumes({"application/xml", "application/json"})
#Path("...")
#RolesAllowed("...")
public void create(LearningActivity entity){
super.create(entity);
}
I need to call a custom method myMethod() after the above process is completed. I thought I could add it immediately after super.create(entity) but apparently the commit process isn't completed until the whole create() method executes. I've also tried adding #PostPersist to the underlying entity but that's still before the record is committed.
Any help would be appreciated.

I'm not sure where to insert a function call for a method that needs
to be called after a new record is committed to a database
It sound like you need to use Event Listener, there is an event called PostPersist that is executed every time a entity has been commited to the database, you can configure that using the above code.
#Entity
public class X{
#PostPersist
public void myMethod(){
//Do anything before the commit of the transaction that save a new entity of type X
}
#PostUpdate
public void myMethod2(){
//This code will run each time you update and save an entity of type X
}
}
PostPersist is called after the commit has been completed, PrePersist executes when you call persist method.

Based on this article, #PostPersist is called before commit.
The alternative that I found is to create a static helper method that will call a Runnable after current transaction session is committed.
public static void afterTransactionCommitted(final Runnable function) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
function.run();
}
}
);
} else {
function.run();
}
}
You will use it like
afterTransactionCommitted(() -> myMethod());

Related

Spring: Not able to write to Autowired repository when using TimerTask

I have a method that should periodically update records in a database.
#Service
public class MyService {
#Autowired
private MyRepository myRepository;
private Boolean flag = false;
#Transactional
public int method1(Args args) {
// do something
if (!flag) {
method2()
}
return x;
}
#Transactional
public int method2(Args args) {
polling = true;
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
List<Records> records = myRepository.getRecords()
for (Record record : records ) {
// prints the Id of each record. Now, they are all have Id=1
System.out.println(record.getId());
// setting the record's Id to 5
record.setId(5);
// prints '5'
System.out.println(record.getId());
}
}
}, 10, 1000*60*4
}
}
Method1 calls Method2. Method2 executes the code inside the run() function every 4 minutes. The code inside run() works properly without scheduling (gets the Ids for each record, prints them, updates the db by setting Id to 5).
However, now, with my use of TimerTask, it still retrieves and prints the Ids, supposedly sets each record's Id to 5, and even prints out '5' at record.get(Id) which would lead me to believe the database was successfully updated with the new Id.
When I actually check my database, I find that the Ids actually haven't been updated to 5. The original Ids remain.
I'm not sure why this is happening, as it seemed like the Ids were being updated. Is this something to do with TimerTask creating a new thread?
The #Transational assumes that it is going to wrap ONE TRANSACTION against the DB, so you need to have your #Transactional functions where they do one thing, let and get out immediately. You know the DB will have either committed or rolled back. Depending on Transaction Isolation levels (another topic you may want to research, but probably don't need to) the thnigs that go on inside the #Transactional never get written to db until that function actually returns. That's probably all you are missing. Chrylis is right to use #Scheduled for 'repeating things' but the real problem is the one i stated, with all due respect to him/her.
Summary: Make your #Scheduled function call a #Transactional function (for each DB transaction you want to do), and then it will work.

Does a transaction with propogation reqiures new can update a object which locked by another transaction?

#Transactional("transaction1")
public void A(){
Actor actor = selectForUpdate(id);
testService.B(actor);
}
#Transactional("transaction1",propagation = Propagation.REQUIRES_NEW)
public void B(Actor actor){
update(actor);
}
Method A lock actor and call method B, method B begin transaction with propogation requires new.I'd like to know whether or not I can update actor in method B.
No it will not allow you to update with Propagation.REQUIRES_NEW.
However, if you want to achieve this functionality, you can do it by:
#Transactional("transaction1",propagation = Propagation.REQUIRED)
public void B(Actor actor){
update(actor);
}
Read the spring transactional document:
http://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html

Make a thread to run a Postgres stored function

I have a stored function that will remove something from the database, but since it could be a very long task, I want to make a thread to execute this function, and let the user keep on doing what he is doing.
Currently I have the following:
The DAO:
#Override
#Transactional
public void deleteAll()
{
Session session = (Session) entityManager.getDelegate();
session.doWork(new Work()
{
#Override
public void execute(Connection connection) throws SQLException
{
try
{
// stored function is currently named delete_function()
CallableStatement deleteAll= connection.prepareCall("{call delete_function()}");
purgeArchived.execute();
purgeArchived.close();
}
catch (SQLException exception)
{
LOGGER.warn(exception);
}
}
});
}
Im afraid when i call getDao.deleteAll() in the rest service, this will be in working for a really long time if the database has lot of stuff to delete. How do I create a thread to do the same thing? Or will this create a thread and execute the function?
Yes, you will need to make your own thread for this. Probably the simplest thing to do is to copy the entire body of your current deleteAll() method to the run() method of a new class, that extends Thread. Assuming you've called that class DeleteAllThread, you'd then replace your method above with
#Override
public void deleteAll() {
new DeleteAllThread().start();
}
Another option is to take a look at using a ExecutorService. This may make things a little cleaner for you. Here is a simple example of how to use an ExecutorService.

No EntityManager bound to this thread JPA

I have a POJO(Myclass in this example) which I persist/update/delete in my app.
I detect changes to that object using a listener class and then in listener class I save the changes to another table.
Here is my class (dummy example) :
EntityListeners({MyListener.class})
class MyClass {
String name;
String surname;
/*...getters...setter..etc */
public void save() {
JPA.em().persist(this);
return this;
}
public void update() {
JPA.em().merge(this);
}
}
class MyListener {
#preUpdate
public void preUpdate() {
/*...some logic to save the changes irrelevant*/
someAuditObj.createWithData(.......);
someAuditObj.save();
}
}
I'm building my web app using play framework v2.1.3, and all this was working great, I was really happy the way it works.
Today I updated play framework to a newer version v2.2.1.
And the for some reason when instance of MyClass changes and the listener picks up the change and it tries to save the changes the transaction fails and I find this in the log :
Caused by: java.lang.RuntimeException: No EntityManager bound to this thread
So it took me a while to figure out that for some reason transaction is not propagated to listener and then I tried something to fix it (Listener class):
#preUpdate
public void preUpdate() {
/*...some logic to save the changes irrelevant*/
JPA.withTransaction(new F.Callback0() {
#Override
public void invoke() throws Throwable {
someAuditObj.createWithData(.......);
someAuditObj.save();
});
}
So this fixes it, and it works like before without issues.
My question(s) is :
Why did it work before without meddling manually with transactions with earlier version of play framework
Is there a better way of achieving the same thing more elegantly (I'm not sure that is the right word for it)?
Update
This is my controller method :
#Transactional
public Result updateName(Long id, String name){
MyClass c = MyClass.findById(id);
c.setName(name);
c.update();
return ok();
}
So transaction should be propagated to all methods right? but why not to listener as well?
My estimate was this :
if a method has #Transactional annotation then all calls that happens inside should be in a transaction?
Seems that you got same problem as mine. Look at my issue: https://github.com/playframework/playframework/issues/2042
Same JPA code works with 2.1.0 but not working with 2.2.1
So i think it's a bug.
Why did it work before without meddling manually with transactions
with earlier version of play framework
Is there a better way of
achieving the same thing more elegantly (I'm not sure that is the
right word for it)?
We have just to wait till this issue will solved or wait some explanation about using threads with JPA transaction from play2 developers in this issue. At this moment issue is open.
In our view, the problem is that JPA.withTransaction() (and #Transactional uses this too) blocks cannot be nested since .withTransaction() unbinds the em unconditionally, and if its an inner .withTransaction(), the outer block will be left without a bound em.
So this test fails at c.save() (save persists the entity in our case)
#Test
public void nestedJPACalls() {
JPATestEntity a = new JPATestEntity();
JPATestEntity b = new JPATestEntity();
JPATestEntity c = new JPATestEntity();
JPA.withTransaction(() -> {
a.save();
JPA.withTransaction(() -> {
b.save();
});
c.save();
});
}
The .withTransaction() methods should check if the em is already bound, and if so, neither bind nor unbind it. I've added that to the discussion at https://github.com/playframework/playframework/issues/2042
We're working on a clean solution now. A temporary but ugly solution is to just try/catch and run your code inside JPA.withTransaction() only if you get the "No EntityManager bound to this thread" exception.
// Create receipt asynch
Akka.future(new Callable() {
public Object call() {
// Issue Receipt
JPA.withTransaction(new F.Callback0() {
#Override
public void invoke() throws Throwable {
// TODO Auto-generated method stub
issueReceipt(pgTxn); // test
}
});
return null;
}
});

Make a temporary data holder in java .spring . hibernate

I work on a project that makes service for mobile apps and also
I should make a project that monitor the Mobile Project.
I want to make some reports that show how many messages come in this moment
and some other reports like that.
but I don’t want to get queries in monitoring project directly from DB.
I want to make a temporary data holder in memory and save the last 10 minutes
data on it (like a variable or a list)
but I don’t know technically how?
I use Spring and Hibernate in my project.
First of all we assume that our program tries to refresh the reports of an entity called SampleEntity every 10 minutes. This is just a simple POJO.
public class SampleEntity
{
// your fields and their getters and setters
}
Next we have a class, I call it SampleEntityDA, which queries the records we need for our reports from db. As you use hibernate you can simply return the result as java.util.List (I think this is one your main problems).
public class SampleEntityDA
{
public List<SampleEntity> queryFromDB()
{
// fetch records you need for your reports here
Session session = ...
return session.createQuery("from sampleEntity").list();
}
}
And at last...
query from db every 10 minutes...
To query from db every 10 minutes, you can simply use java.util.Timer class.
public class ReportTimer extends Observable
{
private Timer timer;
public static void main(String[] args)
{
// Your program starts from here
new ReportTimer().start();
}
private void start()
{
// schedule method of Timer class can execute a task repeatedly.
// This method accepts a TimerTask interface instance as its first parameter.I implement
// it as an anonymous class. TimerTask interface has a run method. Code in this method will execute repeatedly.
// Its second parameter is delay before task gets started to execute.
// And its third parameter is the interval between each execution(10min in your case)
timer = new Timer();
timer.schedule(
new TimerTask()
{
#Override
public void run()
{
notifyObservers(
new SampleEntityDA().queryFromDB() // 10 minutes passed from the last query, now its time to query from db again...
);
}
}, 100, 600000); // 600000ms = 10min
}
public void finish()
{
// call me whenever you get tired of refreshing reports
timer.cancel();
}
}
At last you need to update the data holder of your reports every 10min.
You can do this simply by Observer Pattern. As you know in java this is done by Observer class and Observable interface.
So 1) ReportTimer needs to extend Observer class and 2) in TimerTask we need to notify the listeners; this is done by notifyObservers method.
Our last class has duty of refreshing reports. I call it ReportGenerator. This class refreshes the reports whenever you like. It also has a java.util.List field that has the most recent data of db. ReportGenerator updates this field whenever its Observer - I mean ReportTimer - notifies it.
public class ReportGenerator implements Observer
{
List<SampleEntity> list = new ArrayList<SampleEntity>();
#Override
public void update(Observable o, Object arg)
{
// This method will automatically!?! executed whenever its observer notifies him.
// The arg parameter consists the new records. you just need to put it in the list field.
List<SampleEntity> list = (List<SampleEntity>) arg;
}
public void refreshReport()
{
// you can easily refresh a report with data in list field
}
public void refreshAnotherReport()
{
// you can easily refresh a report with data in list field
}
}
use map, hashMap, or ConcurrentHashMap.
make a crone job that update Map after ten min.
Here is a link for map, HashMap, ConcurrentHashMAP

Categories