Abrufen von javax.persistence.TransactionRequiredException in einem vom Container verwalteten Entity Manager
Frage
Hallo, ich habe ein einfaches erstellt JSF
+ JPA
Anwendung.Meine Web-App besteht aus einem Entity
, A ManagedBean
und einige JSF
Seiten.Sein Zweck besteht darin, ein Objekt in der MySQL-Datenbank zu erstellen und zu speichern.Ich bekomme javax.persistence.TransactionRequiredException
jedes Mal, wenn ich die Entität in der beibehalten möchte PersistenceContxt
.Allerdings beim Injizieren der UserTransaction
in die Controller-Klasse funktioniert es problemlos, aber ich verstehe nicht warum?Weil es angeblich nicht hinzugefügt werden muss UserTransaction
weil es Container-verwaltet ist.Liege ich falsch oder was?Oder gibt es andere Probleme in meinen Codes?
Hier ist mein ManagedBean-Code:
@ManagedBean
@SessionScoped
public class Controller {
@PersistenceContext
private EntityManager em;
private Book book;
public Controller() {}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
public String createBook() {
book = new Book();
return "create";
}
public String showResponse() {
em.persist(book);
return "response";
}
}
Hier ist das create.xhtml
Facette:
...
<h:form>
<h:panelGrid columns="2">
<h:outputText>Title</h:outputText>
<h:inputText value="#{controller.book.title}" />
</h:panelGrid>
</h:commandButton action="#{controller.showResponse()}" value="Response" />
...
und das ist das Persistence.xml
Inhalt:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="JPATestPU" transaction-type="JTA">
<jta-data-source>jdbc/mysql</jta-data-source>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
</properties>
</persistence-unit>
</persistence>
Lösung
Transaktionen werden automatisch nur für Methoden in EJB erstellt, nicht für verwaltete Beans.Wenn Sie sich im JEE7-Container befinden, können Sie versuchen, @Transactional zu Ihrer vom Controller verwalteten Bean hinzuzufügen. Andernfalls müssen Sie UserTransation zum Verwalten der Transaktion verwenden.
Hier ist ein Teil des Codes, den ich verwendet habe:
@Transactional
public class PersonService {
@PersistenceContext
private EntityManager em;
public void savePerson(Person person) {
em.persist(person);
}
Und ich rufe es vom Servlet aus auf:
@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
@Inject
PersonService service;
....
service.savePerson(p);
System.out.println("Person saved");
Ich kann in der Datenbank sehen, dass es gespeichert wurde, und in den Protokollen sehe ich folgende Meldungen:
INFO: Managed bean with Transactional annotation and TxType of REQUIRED called outside a transaction context. Beginning a transaction...
INFO: Person saved
Wie Sie sehen, wird eine neue Transaktion erstellt und das Objekt gespeichert
Andere Tipps
Nachdem ich mir den Codeausschnitt angesehen habe.Ich denke, das ist eher ein Designproblem als ein technisches Problem.Ich schlage vor, dass Sie Ihre Bewerbung wie folgt strukturieren sollten:1.Datenbankschicht – MySql.2.Entities-Paket – enthält Entity-Objekte, die direkt Ihren Datenbanktabellen zugeordnet sind.3.Data Access Objects Package – enthält EJBs (z. B.@Singleton, @Stateless, @Stateful).4.Controller-Paket – enthält das Servlet und/oder die verwalteten Beans, auf die die Präsentationsschicht über EL zugreift (z. B.@Named, @ConversionScoped, @Managedbean usw.) 5.Präsentationsschicht – JSF-Seiten (z. B.index.xhtml usw.)
Es ist wichtig, Ihren Code auf diese Weise zu strukturieren, da Transaktionen, Sicherheit und andere Dienste automatisch für EJBs bereitgestellt werden, in denen die Persistenz durchgeführt werden sollte.Das heißt, der Verweis auf den EntityManager (über @PersistenceContext) sollte in EJBs erfolgen, nicht in den Managed Beans-Objekten.Die Objekte im Controller sollten @Inject (@EJB)-Annotationen verwenden injizieren Abhängigkeiten.
Sofern Ihr Vorgang nicht darin besteht, aus einer Datenbank zu lesen, benötigen Sie lediglich die Annotation „@Transactional“ über Ihrer Methode.Sie können es auch über Ihrer Klasse definieren.Aber es ist keine richtige Architektur.Zumindest müssen Sie eine weitere Klasse erstellen und diese in Ihre Bean einfügen.
Wenn Sie JavaEE verwenden, sollten Sie EJB verwenden.Es ist ganz einfach und Sie müssen sich nicht um die Transaktion kümmern.Der Container kann das Ganze steuern.