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!
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.
Une autre manière de gérer cela consiste à utiliser l'annotation @Transactional
sur la méthode de votre DataFetchBean
addEmployee
. 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.
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
ajoutez une annotation @Transactional sur votre méthode, cela la rendra "transactionnelle"
Une approche simple, ajoutez @Transactional(Transactional.TxType.REQUIRED)
à la méthode