JPA Error : Transaction is currently active - java

When I run my unit tests in isolation they work fine ie. (omitted the asserts)
#Test
public void testSave()
{
EntityManagerHelper emh = new EntityManagerHelper();
LevelDAO dao = new LevelDAO();
Level l = new Level();
l.setName("aname");
emh.beginTransaction();
dao.save(l);
emh.commit();
}
then running this individual test below no problem
#Test
public void testUpdate()
{
EntityManagerHelper emh = new EntityManagerHelper();
LevelDAO dao = new LevelDAO();
Level l = new Level();
l.setName("bname");
l.setLevelid(1);
emh.beginTransaction();
dao.update(l);
emh.commit();
}
When they run at same time in sequence I recieve that error - Transaction is currently active. Is there a way to allow each unit test to run only after a transaction from previous piece of work is not active? Should I be looking at Spring instead?
Update
The EntityManagerHelper gains access to the persistence context like so
emf = Persistence.createEntityManagerFactory("bw_beta");
threadLocal = new ThreadLocal<EntityManager>();
which looks like the problem
So a hacky workaround was to use define locally ie.
EntityManagerFactory factory = Persistence.createEntityManagerFactory("bw_beta");
EntityManager entityManager = factory.createEntityManager();
entityManager.getTransaction().begin();
dao.save(l);
entityManager.persist(l);
entityManager.getTransaction().commit();
Pretty sure there's a better way - maybe using Spring?

Pretty sure there's a better way - maybe using Spring?
Yes, Spring cleans it up a lot and gives you control on what you'd like to run within a transaction without polluting the actual test.
With Spring, your tests would look something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:META-INF/conf/spring/application-context.xml",
"classpath:META-INF/conf/spring/test-datasource-spring-config.xml" })
#TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
public class LevelDaoTest {
#Resource( name="levelDao" )
LevelDao levelDao;
#Test
public void shouldSaveNewLevels() {
Level l = new Level();
l.setName("aname");
levelDao.save(l);
// assert
}
#Test
public void shouldUpdateExistingLevels() {
Level l = new Level(); // or I would assume, you'd read this level back from DB, or set a proper ID, so the DAO will know to update it.. But that is besides the point
l.setName("bname");
levelDao.update(l);
// assert
}
}
Take a look at Spring Documentation under Testing => Transaction Management to get more details.
P.S. From your example:
dao.save(l);
entityManager.persist(l);
Looks really strange, as usually you would encapsulate entityManager within a DAO, so all you'd need to do is dao.save(l)

For anyone that may be having this issue here is how I resolved it. I was doing multiple saves and I kept getting this error. You do not want to begin multiple transactions without checking if it is active.
if(!entityManager.getTransaction().isActive())
entityManager.getTransaction().begin();
dao.save(l);
entityManager.persist(l);
entityManager.getTransaction().commit();
I implemented a Singleton approach to handle it.

Related

Delete data in Java Batch Job (JSR352)

While I have Java Batch jobs that read data, process it and store it in other places in the database, now I need a step to actually remove data from the database. All I need to run is a delete query via JPA.
The chunk based Reader/Processor/Writer pattern does not make sense here. But the Batchlet alternative is giving me a headache either. What did I do?
I created a Batchlet that gets invoked via CDI. At that moment it is easy to inject my JPA EntityManager. What is not easy is to run the update query. Code looks like this:
package ...;
import javax.batch.api.BatchProperty;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
#Named("CleanerBatchlet")
public class CleanerBatchlet extends AbstractBatchlet {
public static final Logger log = LogManager.getLogger(CleanerBatchlet.class);
#PersistenceContext(unitName = "...")
private EntityManager entityManager;
#Inject
#BatchProperty(name = "technologyIds")
private String technologyIds;
private void clearQueue(long technologyId) {
//EntityManager entityManager = ...getEntityManager();
//entityManager.getTransaction().begin();
Query q = entityManager.createQuery("delete from Record r where r.technologyId=:technologyId");
q.setParameter("technologyId", technologyId);
int count = q.executeUpdate();
//entityManager.getTransaction().commit();
log.debug("Deleted {} entries from queue {}", count, technologyId);
//entityManager.close();
}
#Override
public String doProcess() throws Exception {
log.debug("doProcess()");
out.println("technologyIds=" + technologyIds);
log.info("technologyIds=" + technologyIds);
try {
String[] parts = technologyIds.split(",");
for (String part: parts) {
long technologyId = Long.parseLong(part);
clearQueue(technologyId);
}
} catch (NullPointerException | NumberFormatException e) {
throw new IllegalStateException("technologyIds must be set to a string of comma-separated numbers.", e);
}
return "COMPLETED";
}
}
As you can see some lines are commented out - these are the ones I am experimenting with.
So if I run the code as-is, I get an exception telling me that the update query requires a transaction. This is regardless of which of the two persistence units in my project I use (one is configured for JTA, the other is not).
javax.persistence.TransactionRequiredException: Executing an update/delete query
It also does not matter whether I uncomment the transaction handling code begin/commit. I still get the same error that a transaction is required to run the update query.
Even when I try to circumvent CDI and JTA completely by creating my own EntityManager via the Persistence API (and close it afterwards, respectively) I do get the very same exception.
So how can I run this delete query or other update queryies from within the batch job?
I'd suggest using plain jdbc to run this delete query, with either auto commit or manual transaction commit.
During the batchlet processing, the incoming transaction is suspended. So the entity manager does not have a transaction context.
Ultimately I made it work by following this tutorial: https://dzone.com/articles/resource-local-vs-jta-transaction-types-and-payara
and going for the Classic RESOURCE_LOCAL Application pattern.
It involves injecting the nonJTA EntityManagerFactory, using that to create the entitymanager and closing it after use. Of course the transaction has to be managed manually but after all now it works.
The essential excerpt of my code looke like this:
#PersistenceUnit(unitName = "...")
private EntityManagerFactory emf;
#Inject
#BatchProperty(name = "technologyIds")
private String technologyIds;
private void clearQueue(long technologyId) {
EntityManager entityManager = emf.createEntityManager();
entityManager.getTransaction().begin();
Query q = entityManager.createQuery("delete from Record r where r.technologyId=:technologyId");
q.setParameter("technologyId", technologyId);
q.executeUpdate();
entityManager.getTransaction().commit();
entityManager.close();
}

Spring boot manage multiple transactions

I have spring boot app with multiple databases and in services I want to talk to those databases and combine info from 2 of them in the same method.
#Service
#Transactional("transactionManager1")
public class Service1 {
#Service
#Transactional("transactionManager2")
public class Service2 {
I have some methods in Service1 that also call methods from Service2 to get some data from other database.
It worked fine until I added threading. In Service1 I added CompletableFuture.supplyAsync(....) that calls method from Service1 that in turn eventually calls Service2 methods. And at some point it just throws me TransactionException.
To fix this I thought I would manually create transaction before CompletableFuture.supplyAsync(....) and commit it afterwards. When I was searching how to do that I got idea from CompletableFuture vs Spring Transactions and started writing the following code (I use PlatformTransactionManager instead):
#TimeLimiter(name = "method1")
public CompletableFuture<Void> method1Async(long arg1) {
TransactionDefinition txDef = new DefaultTransactionDefinition();
TransactionStatus txStatus = platformTransactionManager.getTransaction(txDef);
CompletableFuture<Void> x = null;
try {
x = CompletableFuture.supplyAsync(() -> {
Void y = this.method1(arg1);
platformTransactionManager.commit(txStatus);
return y;
})
.toCompletableFuture();
} catch (Exception e){
platformTransactionManager.rollback(txStatus);
}
return x;
}
But after still getting exception about transaction I realized that since I use multiple databases I probably need multiple transaction managers. But when I was trying to learn those transaction managers I didn't notice any way to tell for which database I want it.
How Do I create another transaction for transactionManager2?

Is it possible to nest transactions with TransactionManager?

I have the following code:
public ResultProcessDTO process() {
TransactionTemplate transactionTemplate = new TransactionTemplate(this.transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return transactionTemplate.execute(status -> {
...
SomeEntity entity = service.findById(id);
...
otherBean.someMethod();
...
});
}
And, in another bean:
public void someMethod() {
...
service.save(entity);
}
I need someMethod() to be REQUIRES_NEW, to perform the save and commit the transaction regardless of what happens with the rest of the process().
I've already tried #Transactional to leave this up to Spring, but the process() is triggered via KafkaListener, which leads me to have problems with lazy load entities that are fetched along the way.
For example, if I call entity.getChildList() I get a LazyInitializationException. This exception is not thrown if I use TransactionTemplate.
Any suggestions on what I'm doing wrong or how to make it work as I hope?

Same hibernate session between JUnit tests causing problems

I'm developing a web app which is based in Spring 2.5 and hibernate 3. Recently I've introduced JUnit tests and I've done some integration tests using DBUnit framework. DBUnit is supposed to update the database with an xml dataset between one test and another, and it's working well, as I've seen.
However, when I update an element in a test, hibernate seems to catch this information and even I load the element in the following test, the information is the one I've modified. If I look the DB when the execution is paused, the Data Base is properly reseted by DBUnit. So I think it can be an Hibernate problem..
Is there a way to make a tearDown between tests saying I want a new hibernate session for my spring context? By the way, I'm not using Spring annotations and I get the Spring context by code:
String[] contextLocations = new String[2];
contextLocations[0] = "WebContent/WEB-INF/applicationContext.xml";
contextLocations[1] = "src/System_V3/test/applicationContext.xml";
context = new FileSystemXmlApplicationContext(contextLocations);
DBUnit setUp:
#Before
public void setUpBeforeClass() throws Exception {
handleSetUpOperation();
}
private static void handleSetUpOperation() throws Exception {
conn = getConnection();
conn.getConnection().setAutoCommit(false);
final IDataSet data = getDataSet();
try {
DatabaseOperation.REFRESH.execute(conn, data);
} finally {
conn.close();
}
}
private static IDatabaseConnection getConnection() throws ClassNotFoundException, SQLException,
DatabaseUnitException {
Class.forName("org.gjt.mm.mysql.Driver");
return new DatabaseConnection(DriverManager.getConnection(
"jdbc:mysql://localhost:3306/web_database", "root", "pass"));
}
private static IDataSet getDataSet() throws IOException, DataSetException {
ClassLoader classLoader = TestPrueba.class.getClassLoader();
return new FlatXmlDataSetBuilder().build(classLoader
.getResourceAsStream("System_V3/test/dataset.xml"));
}
Tests are done in JUnit 4 using only #Test annotations and test class is not extending any library class.
Any suggestion?
Not sure if this is something that can help you - but just in case...
Try to use session.clear() and use it in teardown method.
Please take a look here http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/Session.html#clear()
According to spec session.clear() ->
Completely clear the session. Evict all loaded instances and cancel all pending saves, updates and deletions. Do not close open iterators or instances of ScrollableResults.
You need to execute your tests within a transaction. This can be achieved by setting the SpringJUnit4ClassRunner for your test. After this is configured you can use #Transactional annotation per test.
With this approach you can #Autowired your beans directly to your test too.
For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context-file.xml" })
public class MyTest {
#Autowired
private MyService myService;
#Transactional
#Test
private void myFirstTest() {
...
myService.executeSomething();
...
}
}
and of course, you can set the default behaviour to RollBack on your test class annotating it with #TransactionConfiguration(defaultRollback = true/false)

Combining JBehave with SpringJUnit4ClassRunner to enable transaction rollback

Essence:
How can I auto-rollback my hibernate transaction in a JUnit Test run with JBehave?
The problem seems to be that JBehave wants the SpringAnnotatedEmbedderRunner but annotating a test as #Transactional requires the SpringJUnit4ClassRunner.
I've tried to find some documentation on how to implement either rollback with SpringAnnotatedEmbedderRunner or to make JBehave work using the SpringJUnit4ClassRunner but I couldn't get either to work.
Does anyone have a (preferably simple) setup that runs JBehave storries with Spring and Hibernate and transaction auto-rollback?
Further infos about my setup so far:
Working JBehave with Spring - but not with auto-rollback:
#RunWith(SpringAnnotatedEmbedderRunner.class)
#Configure(parameterConverters = ParameterConverters.EnumConverter.class)
#UsingEmbedder(embedder = Embedder.class, generateViewAfterStories = true, ignoreFailureInStories = false, ignoreFailureInView = false)
#UsingSpring(resources = { "file:src/main/webapp/WEB-INF/test-context.xml" })
#UsingSteps
#Transactional // << won't work
#TransactionConfiguration(...) // << won't work
// both require the SpringJUnit4ClassRunner
public class DwStoryTests extends JUnitStories {
protected List<String> storyPaths() {
String searchInDirectory = CodeLocations.codeLocationFromPath("src/test/resources").getFile();
return new StoryFinder().findPaths(searchInDirectory, Arrays.asList("**/*.story"), null);
}
}
In my test steps I can #Inject everything nicely:
#Component
#Transactional // << won't work
public class PersonServiceSteps extends AbstractSmockServerTest {
#Inject
private DatabaseSetupHelper databaseSetupHelper;
#Inject
private PersonProvider personProvider;
#Given("a database in default state")
public void setupDatabase() throws SecurityException {
databaseSetupHelper.createTypes();
databaseSetupHelper.createPermission();
}
#When("the service $service is called with message $message")
public void callServiceWithMessage(String service, String message) {
sendRequestTo("/personService", withMessage("requestPersonSave.xml")).andExpect(noFault());
}
#Then("there should be a new person in the database")
public void assertNewPersonInDatabase() {
Assert.assertEquals("Service did not save person: ", personProvider.count(), 1);
}
(yes, the databaseSetupHelper methods are all transactional)
PersonProvider is basicly a wrapper around org.springframework.data.jpa.repository.support.SimpleJpaRepository. So there is access to the entityManager but taking control over the transactions (with begin/rollback) didn't work, I guess because of all the #Transactionals that are done under the hood inside that helper class.
Also I read that JBehave runs in a different context?session?something? which causes loss of controll over the transaction started by the test? Pretty confusing stuff..
edit:
Editet the above rephrasing the post to reflect my current knowledge and shortening the whole thing so that the question becomes more obvious and the setup less obstrusive.
I think you can skip the SpringAnnotatedEmbedderRunner and provide the necessary configuration to JBehave yourself. For example instead of
#UsingEmbedder(embedder = Embedder.class, generateViewAfterStories = true, ignoreFailureInStories = false, ignoreFailureInView = false)
you can do
configuredEmbedder()
.embedderControls()
.doGenerateViewAfterStories(true)
.doIgnoreFailureInStories(false)
.doIgnoreFailureInView(false);
Besides: why do you want to rollback the transaction? Typically you are using JBehave for acceptance tests, which run in a production-like environment. For example you first setup some data in the database, access it via Browser/Selenium and check for the results. For that to work the DB transaction has to be committed. Do you need to clean-up manually after your tests, which you can do in #AfterStories or #AfterScenario annotated methods.
I made it work by controlling transaction scope manually, rolling it back after each scenario. Just follow the official guide how to use Spring with JBehave and then do the trick as shown below.
#Component
public class MySteps
{
#Autowired
MyDao myDao;
#Autowired
PlatformTransactionManager transactionManager;
TransactionStatus transaction;
#BeforeScenario
public void beforeScenario() {
transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
#AfterScenario
public void afterScenario() {
if (transaction != null)
transactionManager.rollback(transaction);
}
#Given("...")
public void persistSomething() {
myDao.persist(new Foo());
}
}
I'm not familiar with JBehave, but it appears you're searching for this annotation.
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true).
You could also set defaultRollback to true in your testContext.

Categories