I have implemented a 'long running task' in my plugin. It manipulates pages - deleting, moving, creating, so I understand that I need to wrap my long running task in a transaction?
I have followed the first example on this page: https://developer.atlassian.com/display/CONFDEV/Hibernate+Sessions+and+Transaction+Management+Guidelines
But I get the following error:
net.sf.hibernate.LazyInitializationException: Failed to lazily initialize a collection - no session or session was closed
at net.sf.hibernate.collection.PersistentCollection.initialize(PersistentCollection.java:209)
at net.sf.hibernate.collection.PersistentCollection.read(PersistentCollection.java:71)
at net.sf.hibernate.collection.Bag.iterator(Bag.java:256)
at com.fujitsu.VME.confluence.plugins.PageControl.PublishLongRunningTask$1.doInTransaction(PublishLongRunningTask.java:138)
My code looks like this:
@Override protected void runInternal() { Object result = transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction() { try { Space devSpace = page.getSpace(); Page homePage = devSpace.getHomePage(); List<Page> children = homePage.getChildren(); for (Page child : children) { // Do stuff with page } } catch (Exception e) { progress.setStatus("Publish failed: " + e.getMessage()); progress.setCompletedSuccessfully(false); } finally { progress.setPercentage(100); } return null; } }); }
I am getting 'page' and 'transactionTemplate' via Spring injection.
The exception occurs at the line 'for (Page child : children) {' - could someone please tell me what I might be doing wrong and how I fix it?
Steve
Community moderators have prevented the ability to post new answers.
Hi Steve,
The Confluence 'content' objects (like pages, blogs, comments and attachments) are hibernate objects and have a transaction affinity. In other words, you cannot read a Confluence object from the database in one transaction and then reference a property on it in another transaction - this appears to be what you are doing with the 'page' object that you are accessing.
Instead of passing the page object into your transaction callback, pass in the unique ID of the page and then re-retrieve the page object within your new transaction using the PageManager.
eg.
@Override protected void runInternal() { final long pageId = page.getId(); Object result = transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction() { try { Page p = pageManager.getPage(pageId); Space devSpace = p.getSpace(); Page homePage = devSpace.getHomePage(); List<Page> children = homePage.getChildren(); for (Page child : children) { // Do stuff with page } } catch (Exception e) { progress.setStatus("Publish failed: " + e.getMessage()); progress.setCompletedSuccessfully(false); } finally { progress.setPercentage(100); } return null; } }); }
Thanks, Joseph, that fixed it.
Steve
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.