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()
Related
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.
I have a MySQL database running on my localhost. To this database I have JDBC-connection. Over a RESTful WebService a softwareagent puts results into this database. I implemented a garbage collector in Java and want to execute that garbage collector after a certain period. My aim is that I do not have too many entries in the table.
The Garbage Collector looks as follows:
public void collectGarbageResults() {
ArrayList<Integer> ids = new ArrayList<Integer>();
ResultSet rs = this.execQuery("SELECT * FROM results");
try {
while (rs.next()) {
if ( (System.currentTimeMillis()) - (Timestamp.valueOf(rs.getString("tmstmp")).getTime()) > Long.parseLong(appconfigs.get("resultstableactuality")) ) {
ids.add(rs.getInt("id"));
}
}
for (Integer i : ids) {
this.deleteResultsWhereId(i);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
The method I call looks as follows:
private void deleteResultsWhereId(int id) {
retint = this.updateQuery("DELETE FROM results WHERE id=" + id + ";");
}
I am using JSF in an Eclipse's Dynamic Web Project and these methods are implemented in a managed bean. I am going to store the interval in an XML-file which I use for my application's configuration. Over the variable appconfigs I call the method 'get' and ask for the node containing my interval.
Now my question:
How can I call the method of the garbage collector after a certain period in my WebApp?
If you have any questions do not hesitate to comment.
My Solution :
#WebListener
public class BackgroundJobManager implements ServletContextListener{
private ScheduledExecutorService scheduler;
#Override
public void contextInitialized(ServletContextEvent event) {
long delayToStart = 0;
long numberOfUnits = 60;
TimeUnit tu = TimeUnit.SECONDS;
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new ExecuteGarbageCollector(), delayToStart, numberOfUnits, tu);
}
#Override
public void contextDestroyed(ServletContextEvent event) {
scheduler.shutdownNow();
}
}
The class I call implements Runnable as follows:
public class ExecuteGarbageCollector implements Runnable{
#Override
public void run() {
DAO dao = new DAO();
dao.collectGarbageResults();
}
}
You could use ScheduledExecutorService or Quartz
Quartz has a lot of facilities, but If you choose ScheduledExecutorService you don't have to add any new dependencies to your project. and as you just want this scheduled task, it's simple.
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(...);
Take a look at some comments about both in: Quartz vs. ScheduledExecutorService in Java web application
Check out a scheduler library. You can use Quartz for example.
Call Java from MySQL. You could either have this kicked off by a job or trigger
Just stick to SQL and ignore running this in Java. Probably the easiest if you are good at SQL. Then you can encapsulate this in a SPROC or trigger.
It's your choice whether this should live in your web-app or in your database. I've used both methods for executing timed SQL calls and tend to prefer the code oriented approach
The actual question could have well been how to call a function after a specific interval of time?
Take a look at :
How to call a method after some specific interval of time in Java
and
java: run a function after a specific number of seconds
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.
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