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

7

12

I'm using Wildfly 10.0.0 Final, Java EE7, Maven and JPA 2.1. When I am querying my database for records it works fine and lists out the employees, but when I am trying to persist a new employee it gives me the following exception:

javax.servlet.ServletException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:671)
io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
...

I'm trying to implement this using JSF and CDI beans. I have a JTA data source, which I've configured in my persistence.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
    <persistence-unit name="MyPersistenceUnit">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>java:/EmployeesDS</jta-data-source>
        <class>com.home.entity.Employee</class>
        <properties>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hbm2ddl.auto" value="update"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
        </properties>
    </persistence-unit>
</persistence>

The CDI bean can be seen below. It is relatively simple, there is a method to list out 25 employees and another which should persist a specific employee:

@Named
@RequestScoped
public class DataFetchBean {
    @PersistenceContext
    EntityManager em;

    public List getEmployees() {
        Query query = em.createNamedQuery("findEmployees");
        query.setMaxResults(25);
        return query.getResultList();
    }

    public String getEmployeeNameById(final int id) {
        addEmployee();

        Query query = em.createNamedQuery("findEmployeeNameById");
        query.setParameter("empno", id);
        Employee employee = (Employee) query.getSingleResult();
        return employee.getFirstName() + " " + employee.getLastName();
    }

    public void addEmployee() {
        em.persist(new Employee(500000, new Date(335077446), "Josh", "Carribean", 'm', new Date(335077446)));
    }
}

The employee entity class can be found below:

@NamedQueries({
        @NamedQuery(
                name = "findEmployees",
                query = "select e from Employee e"
        ),           
        @NamedQuery(
                name = "findEmployeeNameById",
                query = "select e from Employee e where e.empNo = :empno"
        )
})
@Table(name = "employees")
public class Employee {
    @Id
    @Column(name = "emp_no")
    private int empNo;
    @Basic
    @Column(name = "birth_date")
    private Date birthDate;
    @Basic
    @Column(name = "first_name")
    private String firstName;
    @Basic
    @Column(name = "last_name")
    private String lastName;
    @Basic
    @Column(name = "gender")
    private char gender;
    @Basic
    @Column(name = "hire_date")
    private Date hireDate;

    public Employee() { }

    public int getEmpNo() {
        return empNo;
    }

    public void setEmpNo(int empNo) {
        this.empNo = empNo;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public Date getHireDate() {
        return hireDate;
    }

    public void setHireDate(Date hireDate) {
        this.hireDate = hireDate;
    }

    public Employee(int empNo, Date birthDate, String firstName, String lastName, char gender, Date hireDate) {
        this.empNo = empNo;
        this.birthDate = birthDate;
        this.firstName = firstName;
        this.lastName = lastName;
        this.gender = gender;
        this.hireDate = hireDate;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Employee employee = (Employee) o;

        if (empNo != employee.empNo) return false;
        if (gender != employee.gender) return false;
        if (birthDate != null ? !birthDate.equals(employee.birthDate) : employee.birthDate != null) return false;
        if (firstName != null ? !firstName.equals(employee.firstName) : employee.firstName != null) return false;
        if (lastName != null ? !lastName.equals(employee.lastName) : employee.lastName != null) return false;
        if (hireDate != null ? !hireDate.equals(employee.hireDate) : employee.hireDate != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = empNo;
        result = 31 * result + (birthDate != null ? birthDate.hashCode() : 0);
        result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
        result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
        result = 31 * result + (int) gender;
        result = 31 * result + (hireDate != null ? hireDate.hashCode() : 0);
        return result;
    }
}

Thanks in advance!

Dimension answered 5/3, 2016 at 0:0 Comment(0)
C
15

Basically one is in the presence of a container managed JTA aware persistence context with bean managed transactions (BMT).

Therefore, besides your EntityManager you should also inject, into your DataFetchBean, your UserTransaction, in order to begin, commit or rollback a transaction.

@Named
@RequestScoped
public class DataFetchBean {
    @PersistenceContext
    EntityManager em;

    @Resource
    private UserTransaction userTransaction;

    ...
}

Then, in your addEmployee method, you've to begin and then commit your transaction, so your changes to your employee entity can be propagated to the database.

public void addEmployee() throws Exception {
    Employee employee = new Employee(500000, new Date(335077446), "Josh", "Carribean", 'm', new Date(335077446));

    userTransaction.begin();
    em.persist(employee);
    userTransaction.commit();
}

In spite of this, you should think about migrating the database actions into an EJB, injecting it into your JSF bean, therefore delegating on the container the onus of managing the transactions, i.e. make use of CMT, instead of manually handling them. Using this approach, keep in mind you'll need to annotate the bean (or the method you're calling in the bean) with @Transactional.

Cusick answered 5/3, 2016 at 0:50 Comment(5)
This gives me " javax.resource.ResourceException: IJ000460: Error checking for a transaction" "javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection"Jonathonjonati
@StefanWendelmann, I would suggest you open a new question because with only the info provided it will be hard to understand what's happening.Pictogram
i figgured out we use a wrong approach in our application where we use MangedBeans and a manual triggered EL from the FacesContext so its not possible to inject the UserTransaction correctly. I have to work out a bigger solution to this.Jonathonjonati
Do you require to use BMT? Otherwise, you can delegate to the container the onus of the transaction management and use CMT. Another alternative, take a look at @JohnAment's answer.Pictogram
This also works inside an EJB (session or message-driven bean) if you delegate the transaction management to the bean itself with the annotations @TransactionManagement(TransactionManagementType.BEAN).Ciaracibber
M
13

An alternate way to handle this is to use the annotation @Transactional on your DataFetchBean's method addEmployee. Then you don't need the UserTransaction and can use AOP to manage the transaction.

This was a new feature added in JTA 1.2.

Minotaur answered 5/3, 2016 at 15:37 Comment(0)
O
2

add @Transactional annotation on your method it will make it "transactional"

Outwards answered 23/10, 2019 at 11:5 Comment(0)
G
1

You can see below document about handle the transacion:
Container-Managed Transactions JEE6

use Transaction Attributes based on your application

Gorga answered 8/11, 2017 at 10:29 Comment(0)
A
0

A simple approach, add @Transactional(Transactional.TxType.REQUIRED) to method

Alvin answered 14/4, 2020 at 19:29 Comment(0)
A
0

I had the same problem, the solution was

@Transactional(rollbackOn = Exception.class)

add to method called by Bean

Archie answered 23/4, 2020 at 16:17 Comment(1)
This answer might actually be helpful. In my case, I have called a service in the same transaction, which has thrown an exception and I have caught it in my code. Unfortunately, the exception caused a rollback of the overall transaction. Therefore, it was not valid, and the entity manager has thrown the error mentioned in the question.Xyster
K
0

Not entirely relevant to this example, but I ran into this issue with a different issue. My project was already using Container Managed Transactions (CMT) and received this error.

My problem was that I forgot to include the @Stateless annotation to my EJB. Adding that annotation (or another annotation to class) makes it actually container managed and provides the required transaction.

Karlakarlan answered 19/7, 2021 at 17:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.