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.
Related
I have a spring boot application which has two beans - AppState and Users.
AppState depends on the bean Users as it autowires it. The skeleton codes for the two beans are as follows.
#Component
#EnableScheduling
public class Users {
#Getter // lombok
private List<String> users;
#PostConstruct
public void init(){
users = new ArrayList<>();
load(); // I removed this later
}
#Scheduled(fixedRate = 3600000)
public void load(){
// load list of users from (say) a file and populate ArrayList 'users'
// this method takes at least 3 mins to finish
}
}
AppState is
#Component
public class AppState {
#Atowired
private Users users;
public List<String> getUsers(){
return users.getUsers();
}
}
I noticed that the method load() in Users was getting triggered twice - probably once during init() and the other time while scheduling load() right after Users bean had been created. So I removed the call to load() in init(). This fixed the redundant call issue.
However, now I find that my service starts as soon as AppState and Users beans have been created, even though Users has not been populated with data yet. This is risky for me as the service, during this time, will return 0 users if queried.
I need help with ANY of the following.
Should I move load() back into init() to make sure that when the bean is done with PostConstruct, it does have all users info? If I go this route, how can I prevent redundant run of load()?
If load() should stay out of init(), how can I ensure that AppState is not ready unless Users has executed load()? I tried using the following code in AppState but it just hangs.
The code is as follows.
#PostConstruct
public void appStateInit(){
while(users.getUsers().size()==0){
try{
Thread.sleep(10000); // sleep 10s
}catch(whatever){
}
}
}
I would suggest having a flag in the class and setting it to true once init() has finished. You can skip the execution of load() if flag is not set yet, e.g.:
private AtomicBoolean shouldExecute;
#PostConstruct
public void init(){
users = new ArrayList<>();
shouldExecute = true;
}
#Scheduled(fixedRate = 3600000)
public void load(){
if(shouldExecute){
// load list of users from (say) a file and populate ArrayList 'users'
// this method takes at least 3 mins to finish
}
}
Also, another solution will be to configure initialDelay in #Scheduled annotation (documentation here) which would delay the first execution by configured number of milliseconds, e.g:
#Scheduled(fixedRate = 3600000, initialDelay=180000)
public void load(){
// load list of users from (say) a file and populate ArrayList 'users'
// this method takes at least 3 mins to finish
}
I am currently trying to make a hibernate query inside of a TimerTask (Runnable). This task makes no saves or updates to the database. It just retrieves a list of jobs. Anytime I run this task, I get HibernateException: Unable to locate current JTA transaction.
I believe this has to do with the fact that it's being started from a runnable because I use this same query outside of this TimerTask.
I can't share the code I am working with because it is for work and proprietary. My research on this issue has only really led me to solutions with Spring, but I am not able to use Spring for this work.
I will attempt to make some pseudo code.
public class JobManager extends TimerTask {
#Override
public void run() {
...
List<String> jobs = Handler.getJobs();
...
}
}
public class Handler {
public static List<String> getJobs() {
return DAO.getJobs();
}
}
public class DAO {
public List<Object> getJobs() {
try {
session = HibernateManager.getSessionFactory().getCurrentSession();
Query myQuery = session.createQuery("query string");
List = myQuery.list();
} catch(HibernateException he) {
log.error(he);
}
return list;
}
}
The exception occurs when the runnable calls getJobs(). This method work everywhere else outside of the TimerTask.
I understand that this is limited information to work with. I can try to accommodate for any other information if it is needed.
I believe every transaction has some time out, so you can not put the regular timer task code inside the running transaction. As it is just reading the data you wont need to start the transaction, just session is enough
I have encountered the same problem and solved by creating the new session
session = sessionFactory.openSession();
EDIT
session.getCurrentSession() takes the current session from the current thread, so it wont work inside timer task. Use openSession()
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;
}
});
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
I'm trying to get the percentage of the progress from a EJB Asynchronous process. Is this possible?
Does anyone have an idea how I could do this?
To get to know the progress of asynchronous processes is always tricky, especially if you don't know if they have actually started yet.
The best way I have found is to write another function that just gets the progress, so, if you have some unique id for each call, then update a hashmap with the current process. You may want to look at Concurrent Hashmap (http://download-llnw.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html)
Then this other lookup function will just take the unique id, and return the progress back to the client.
If it hasn't been started, you can also return that, and ideally you may want to also be able to return any error messages that came up in the processing.
Then, when it has finished, and you returned the error message or success, then delete it from the hashmap, the client got the information, and that info won't change, so no point it keeping it around.
UPDATE:
In your interface make a new function
String progressDone(String id);
You will then refer to that synchronously, as it just goes out and comes right back, so it can look up the id in the hashmap and return either the percentage done or an error message.
But, this means that your actually worker function needs to every so often put information in the hashmap as to where it is, which is why I suggested using the concurrent hashmap, so that you don't have to worry about concurrent writes, and so locking considerations.
The solution I have found is an context object shared between asynchronous method and main thread. Here is an example:
Asynchronous job itself:
#Stateless
public class AsyncRunner implements AsyncRunnerLocal {
#Asynchronous
public Future<ResultObject> doWorkAsynchronous(WorkContext context) {
context.setRunning(true);
for (int i = 0; i < 100; i++) {
//Do the next iteration of your work here
context.setProgress(i);
}
context.setRunning(false);
return new AsyncResult(new ResultObject());
}
}
Shared context object. Important thing here is volatile keyword. Field values will be locally cached in each thread without it and progress will not be visible in main thread:
public class WorkContext {
//volatile is important!
private volatile Integer progress = 0;
private volatile boolean running = false;
//getters and setters are omitted
}
Usage example:
public class ProgressChecker {
#EJB
private AsyncRunnerLocal asyncRunner;
private WorkContext context;
private Future<ResultObject> future;
public void startJob() {
this.context = new WorkContext();
future = asyncRunner.doWorkAsynchronous(this.context);
//the job is running now
while (!future.isDone()) {
System.out.println("Progress: " + this.context.getProgress());
Thread.sleep(1000); //try catch is omitted
}
}
}
In EJB3.1 #Asynchronous method-calls can return java.util.concurrent.Future, this interface provides information boolean isCancelled() or boolean isDone(), but no information if the execution started. From my point of view, there is no way to get the information if the process started its execution via the EJB-Container in standard ways.