web-dev-qa-db-fra.com

Une transaction est requise pour effectuer cette opération (utilisez une transaction ou un contexte de persistance étendue)

J'utilise Wildfly 10.0.0 Final, Java EE7, Maven et JPA 2.1. Lorsque je recherche des enregistrements dans ma base de données, cela fonctionne bien et répertorie les employés, mais lorsque j'essaie de persister un nouvel employé, il me donne l'exception suivante:

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)
...

J'essaie d'implémenter cela en utilisant des beans JSF et CDI. J'ai une source de données JTA, que j'ai configurée dans mon fichier persistence.xml:

<?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>

Le bean CDI peut être vu ci-dessous. C'est relativement simple, il y a une méthode pour lister 25 employés et une autre qui devrait persister un employé spécifique:

@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)));
    }
}

La classe d'entité employé se trouve ci-dessous:

@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;
    }
}

Merci d'avance!

9
masm64

Fondamentalement, on est en présence d'un contexte de persistance compatible JTA géré par conteneur avec des transactions gérées par bean (BMT).

Par conséquent, en plus de votre EntityManager, vous devez également injecter, dans votre DataFetchBean, votre UserTransaction, afin de commencer, valider ou annuler une transaction.

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

    @Resource
    private UserTransaction userTransaction;

    ...
}

Ensuite, dans votre méthode addEmployee, vous devez commencer, puis valider votre transaction, afin que vos modifications apportées à votre entité d'employé puissent être propagées dans la base de données.

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();
}

Malgré cela, vous devriez penser à migrer les actions de la base de données dans un EJB, à l'injecter dans votre bean JSF, déléguant ainsi au conteneur la responsabilité de gérer les transactions, c'est-à-dire utiliser CMT, au lieu de les manipuler manuellement.

11
aribeiro

Une autre manière de gérer cela consiste à utiliser l'annotation @Transactional sur la méthode de votre DataFetchBeanaddEmployee. Vous n'avez alors pas besoin de UserTransaction et pouvez utiliser AOP pour gérer la transaction.

Il s'agit d'une nouvelle fonctionnalité ajoutée dans JTA 1.2.

6
John Ament

Vous pouvez voir ci-dessous le document sur la gestion de la transaction:
Transactions gérées par conteneur JEE6

utilisation Transaction Attributes en fonction de votre candidature

0
Alireza Alallah

ajoutez une annotation @Transactional sur votre méthode, cela la rendra "transactionnelle"

0
cyril

Une approche simple, ajoutez @Transactional(Transactional.TxType.REQUIRED) à la méthode

0
jlgranda