javax.persistence.TransactionRequiredException (using Arquillian)

I recently encountered javax.persistence.TransactionRequiredException when I am trying to perform some cleanup, on @After method in my unit test. I am currently playing with Arquillian (and it is such a great technology to complement Java EE).

The scenario is simple.

I have an Item entity, and I have ItemService. The service provides functionality to add an item and to retrieve it. That’s about it.

In unit test, for one reason or another, one might decide to clean up, or DELETE some item. However, in this case, I would very much like to keep my service layer clean, i.e. just because I happen to need a clean up on the test-side, I should not force my service to implement it. Perhaps another way to look at it is that this is my way to reduce coupling. The user of the service should knows about the service, and the service should not know about where it is being used. One direction, so to speak.

So, to start with, here’s the code:

ItemDistributionServiceTest

package com.dwuysan.service;

import com.dwuysan.entity.Item;
import com.dwuysan.util.EntityManagerProducer;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * @author denywuy
 */
@RunWith(Arquillian.class)
public class ItemDistributionServiceTest {

    private Item item;
    @Inject
    private ItemService itemService;
    @Inject
    private EntityManager em;

    @Deployment
    public static JavaArchive createTestArchive() {
        return ShrinkWrap.create(JavaArchive.class)
                .addClass(Item.class)
                .addClass(ItemService.class)
                .addClass(EntityManagerProducer.class)
                .addAsManifestResource("test-persistence.xml", ArchivePaths.create("persistence.xml"))
                .addAsManifestResource("META-INF/beans.xml", ArchivePaths.create("beans.xml"))
                .addAsResource("ValidationMessages.properties");
    }

    @Before
    public void setup() {
        this.item = new Item();
        item.setCode("item01");
        item.setName("item01");
        item.setUom("piece");

        this.item = this.itemService.getItem(this.itemService.createItem(item));
    }

    @After
    public void cleanUp() throws Exception {
        this.em.remove(em.merge(this.item));
    }

    @Test
    public void testDistribute() throws Exception {
        /*
         * Test omitted
         */
    }
}

So, to DELETE the item, I directly use EntityManager#remove(...). However, that results in the following error:

Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 11.847 sec <<< FAILURE!
testDistribute(com.dwuysan.service.ItemDistributionServiceTest)  Time elapsed: 0.625 sec  <<< ERROR!
javax.persistence.TransactionRequiredException
	at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTxRequiredCheck(EntityManagerWrapper.java:163)
	at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTransactionScopedTxCheck(EntityManagerWrapper.java:145)
	at com.sun.enterprise.container.common.impl.EntityManagerWrapper.merge(EntityManagerWrapper.java:280)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

same as above

After further reading, I found that we need to explicitly initiates transaction. So, the code change slightly to:

ItemDistributionServiceTest

package com.dwuysan.service;

// the rest of the imports go here
import javax.annotation.Resource;
import javax.transaction.UserTransaction;

@RunWith(Arquillian.class)
public class ItemDistributionServiceTest {

    private Item item;

    // the rest of the injections go here
    @Resource
    private UserTransaction utx;

    @Deployment
    public static JavaArchive createTestArchive() {
        // same as above
    }

    @Before
    public void setup() {
        // same as above
    }

    @After
    public void cleanUp() throws Exception {
        utx.begin();
        this.em.remove(em.merge(this.item));
        utx.commit();
    }

    @Test
    public void testDistribute() throws Exception {
        /*
         * Test omitted
         */
    }
}

Reference:

Kosi2801, 2009, EntityManager throws TransactionRequiredException on merge() in JBoss JSF bean, accessed 29 March 2013.