Search code examples
javajpajboss-arquillian

Testing jpa entity classes - error Transaction is required


Based on an archetype i created a java ee app. There is an included arquillian test that runs fine. it just calls a method on a @Stateless bean that persists an pre-made entity.

now i added some entity with some relations and i wrote a test for them. But on peristing any entity i get

Transaction is required to perform this operation (either use a transaction or extended persistence context)

I think i need to mark the testmethod with @Transactional but it seems not to be in class path. Manually invoking the transaction on injected EntityManager yields another error. So how to correctly setup such tests and dependencies.

EDIT As Grzesiek D. suggested here are some details. this is the entity (the one thta links others):

@Entity
public class Booking implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    /**
     * internal id.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
    /**
     * Used for optimistic locking.
     */
    @Version
    @Column(name = "version")
    private int version;

    /**
     * A booking must have a project related.
     */
    @ManyToOne
    @JoinColumn(name = "project_id")
    @NotNull
    private Project project;

    /**
     * A booking must have an owner.
     */
    @ManyToOne
    @JoinColumn(name = "user_id")
    @NotNull
    private User owner;

    /**
     * A booking always has a start time.
     */
    @Column
    @NotNull
    private Timestamp start;

    /**
     * A booking always has an end time.
     */
    @Column
    @NotNull
    private Timestamp end;

    /**
     * 
     * @return true if start is befor end. false otherwise (if equal or after end).
     */
    @AssertTrue(message = "Start must before end.")
    public final boolean isStartBeforeEnd() {
        return start.compareTo(end) < 0;
    }

    /**
     * @return the id
     */
    public final Long getId() {
        return id;
    }

    /**
     * @param id
     *            the id to set
     */
    public final void setId(final Long id) {
        this.id = id;
    }

    /**
     * @return the version
     */
    public final int getVersion() {
        return version;
    }

    /**
     * @param version
     *            the version to set
     */
    public final void setVersion(final int version) {
        this.version = version;
    }

    /**
     * @return the project
     */
    public final Project getProject() {
        return project;
    }

    /**
     * @param project
     *            the project to set
     */
    public final void setProject(final Project project) {
        this.project = project;
    }

    /**
     * @return the owner
     */
    public final User getOwner() {
        return owner;
    }

    /**
     * @param owner
     *            the owner to set
     */
    public final void setOwner(final User owner) {
        this.owner = owner;
    }

    /**
     * @return the start
     */
    public final Timestamp getStart() {
        return start;
    }

    /**
     * @param start
     *            the start to set
     */
    public final void setStart(final Timestamp start) {
        this.start = start;
    }

    /**
     * @return the end
     */
    public final Timestamp getEnd() {
        return end;
    }

    /**
     * @param end
     *            the end to set
     */
    public final void setEnd(final Timestamp end) {
        this.end = end;
    }

    //hashCode, equals, toString omitted here
}

Here is the test:

@RunWith(Arquillian.class)
public class BookingTest {

    @Deployment
    public static Archive<?> createDeployment() {
        return ArquillianContainer.addClasses(Resources.class, Booking.class, Project.class, User.class);
    }

    @Inject
    private EntityManager em;

    @Test
    public void createBooking() {
        Booking booking = new Booking();
        booking.setStart(new Timestamp(0));
        booking.setEnd(new Timestamp(2));
        User user = new User();
        user.setName("Klaus");
        booking.setOwner(user);
        Project project = new Project();
        project.setName("theOne");
        project.setDescription("blub");
        booking.setProject(project);
        em.persist(booking);
        System.out.println("here");
    }

}

And here the exception:

javax.persistence.TransactionRequiredException: JBAS011469: Transaction is required to perform this operation (either use a transaction or extended persistence context)

I know it will work if i create a @Stateless bean and encapsulate the persist there but i want a direct test of entity's validation and i need a playground to evolve the data model.


Solution

  • In order to have transaction support in Arquillian tests you will need to bring in extension which enables this feature. In your case jta dependency should do the job.

    <dependency>
      <groupId>org.jboss.arquillian.extension</groupId>
      <artifactId>arquillian-transaction-jta</artifactId>
      <scope>test</scope>
    </dependency>

    In addition, if you are using JBoss, you will need to provide its JNDI for UserTranscation, so put following section in your arquillian.xml:

    <?xml version="1.0" ?>
    <arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://jboss.org/schema/arquillian" xsi:schemaLocation="http://jboss.org/schema/arquillian
        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">     
    
      <extension qualifier="transaction">
        <property name="manager">java:jboss/UserTransaction</property>
      </extension>
    
    </arquillian>
    

    This way you can use @Transactional which comes from this extension's API.