小型 facelet 应用程序中的 javax.persistence.TransactionRequiredException
Posted
技术标签:
【中文标题】小型 facelet 应用程序中的 javax.persistence.TransactionRequiredException【英文标题】:javax.persistence.TransactionRequiredException in small facelet application 【发布时间】:2013-05-07 17:39:30 【问题描述】:我正在尝试将一些值从一个小型 facelet 应用程序保存到 mysql 数据库,但不断收到此错误。我有一个带有 JPS 页面和一个 servlet 的相同应用程序,它在大致相同的逻辑下运行良好,这是我第一次尝试使用 facelets,所以它可能只是一些愚蠢的事情,但我会得到帮助。
谢谢
错误
javax.faces.el.EvaluationException: javax.persistence.TransactionRequiredException
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at javax.faces.component.UICommand.broadcast(UICommand.java:315)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)
Caused by: javax.persistence.TransactionRequiredException
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTxRequiredCheck(EntityManagerWrapper.java:163)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTransactionScopedTxCheck(EntityManagerWrapper.java:145)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.persist(EntityManagerWrapper.java:263)
at vecka19.controller.BookController.addBook(BookController.java:28)
at vecka19.controller.BookController$Proxy$_$$_WeldClientProxy.addBook(BookController$Proxy$_$$_WeldClientProxy.java)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at javax.el.BeanELResolver.invokeMethod(BeanELResolver.java:779)
at javax.el.BeanELResolver.invoke(BeanELResolver.java:528)
at javax.el.CompositeELResolver.invoke(CompositeELResolver.java:257)
at com.sun.el.parser.AstValue.invoke(AstValue.java:248)
at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:302)
at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:39)
at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
... 32 more
index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
<title>Vecka19</title>
</h:head>
<h:body>
<section id="bookForm">
<div>
<h:form id="bookForm">
<table>
<tr>
<td><h:outputLabel value="ID: " for="id"/></td>
<td><h:inputText id="id" value="#book.bookId"/></td>
</tr>
<tr>
<td><h:outputLabel value="TITLE: " for="title"/></td>
<td><h:inputText id="title" value="#book.title"/></td>
</tr>
<tr>
<td><h:outputLabel value="AUTHOR: " for="author"/></td>
<td><h:inputText id="author" value="#book.author"/></td>
</tr>
<tr>
<td><h:outputLabel value="PRICE: " for="price"/></td>
<td><h:inputText id="price" value="#book.price"/></td>
</tr>
<tr>
<td>
<h:commandButton value="Add" action="#bookController.addBook()" />
<h:commandButton value="Get" action="#bookController.book" />
<h:commandButton value="Edit" action="#bookController.editBook()" />
<h:commandButton value="Delete" action="#bookController.deleteBook()" />
</td>
</tr>
</table>
</h:form>
</div>
</section>
<br />
<section id="dbTable">
<div>
<table>
<th>ID</th>
<th>TITLE</th>
<th>AUTHOR</th>
<th>PRICE</th>
<c:forEach items="$bookController.books" var="book">
<tr>
<td>$book.bookId</td>
<td>$book.title</td>
<td>$book.author</td>
<td>$book.price</td>
</tr>
</c:forEach>
</table>
</div>
</section>
</h:body>
BookController.java
package vecka19.controller;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import vecka19.model.Book;
@Named
@RequestScoped
public class BookController
@Inject Book book;
@PersistenceContext
private EntityManager em;
public List getBooks()
return em.createNamedQuery("Book.findAll").getResultList();
public Book getBook()
return em.find(Book.class, book.getBookId());
public void addBook()
em.persist(book);
public void editBook()
em.merge(book);
public void deleteBook()
em.remove(getBook());
Book.java
package vecka19.model;
import java.io.Serializable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
@Entity
@Named
@RequestScoped
@Table(name = "BOOKS")
@NamedQueries(@NamedQuery(name = "Book.findAll", query = "SELECT b FROM Book b"))
public class Book implements Serializable
private static final long serialVersionUID = 1L;
@Id
@NotNull
@Column(name = "BOOK_ID")
private Integer bookId;
@Column(name = "TITLE")
private String title;
@Column(name = "AUTHOR")
private String author;
@Column(name = "PRICE")
private Integer price;
public Book()
public Book(Integer bookId)
this.bookId = bookId;
public Integer getBookId()
return bookId;
public void setBookId(Integer bookId)
this.bookId = bookId;
public String getTitle()
return title;
public void setTitle(String title)
this.title = title;
public String getAuthor()
return author;
public void setAuthor(String author)
this.author = author;
public Integer getPrice()
return price;
public void setPrice(Integer price)
this.price = price;
percistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="vecka19PU" transaction-type="JTA">
<jta-data-source>jdbc/MySQLDataSource</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
</persistence-unit>
</persistence>
【问题讨论】:
@NamedQuery 工作正常,数据库中已有的内容打印在索引页上,问题是更新数据库。 CDI bean 没有作为 EJB 提供自动事务管理。看看你到目前为止和这里的答案:***.com/questions/8763115/… 【参考方案1】:您正在滥用 CDI 托管 bean 作为业务服务。它没有事务管理的线索。您需要手动管理交易。由于这通常很痛苦,而且您显然正在使用 Glassfish,它是一个支持 EJB 的完全有价值的 Java EE 容器,您宁愿为此使用一个完全有价值的 EJB。在 EJB 中使用 EntityManager
时,容器将完全透明地管理 DB 事务。一个 EJB 方法调用算作一个事务(即,当您触发多个 DB 查询并且其中一个失败时,所有内容都将自动回滚)。
总体而言,您似乎混合了模型、控制器和服务的职责。不要让您的实体也成为托管 bean。您还绝对不应该在 Javabean getter 方法中执行业务逻辑(例如getBooks()
)。当在迭代组件中引用时,它会在每个迭代轮次中被调用。所以假设你有 100 条记录,那么 DB 将被命中 100 次。这显然是低效的。
它应该是这样的:
模型(实体):
@Entity
@Table(name = "BOOKS")
public class Book implements Serializable
// ...
控制器(支持 bean):
@Named
@RequestScoped
public class BookController
private Book book;
private List<Book> books;
@EJB
private BookService service;
@PostConstruct
public void init()
book = new Book();
books = service.list();
public void add()
service.save(book);
init();
public Book getBook()
return book;
public List<Book> getBooks()
return books;
服务(EJB):
@Stateless
public class BookService
@PersistenceContext
private EntityManager em;
public List<Book> list()
return em.createQuery("FROM Book", Book.class).getResultList();
public Book find(Integer id)
return em.find(Book.class, id);
public Integer save(Book book)
em.persist(book);
return book.getId();
public void update(Book book)
em.merge(book);
public void delete(Book book)
em.remove(em.contains(book) ? book : em.merge(book));
视图(Facelet;简化):
<h:inputText id="title" value="#bookController.book.title"/>
<h:inputText id="author" value="#bookController.book.author"/>
<h:inputText id="price" value="#bookController.book.price"/>
<h:commandButton value="Add" action="#bookController.add" />
...
<h:dataTable value="#bookController.books" var="book">
<h:column><f:facet name="header">ID</f:facet>#book.id</h:column>
<h:column><f:facet name="header">Title</f:facet>#book.title</h:column>
<h:column><f:facet name="header">Author</f:facet>#book.author</h:column>
<h:column><f:facet name="header">Price</f:facet>#book.price</h:column>
</h:dataTable>
(您的编辑和删除按钮没有任何意义,所以我删除了它们,您可能希望将它们放在数据表中)
另见:
Recommended JSF 2.0 CRUD frameworks Why JSF calls getters multiple times【讨论】:
这绝对消除了一些困惑,感谢您的出色回答,将检查这些链接。谢谢 @BalusC 因此,当使用来自@EJB
服务类的方法时,可以简单地调用 EntityManager 的方法而无需担心/额外的事务?
@LyK:这确实是答案所说的。另见***.com/questions/18369356/…【参考方案2】:
只需在您的方法上添加@Transactional
注释,例如
@Transactional // <-------------
public long setSessionState(StateEnum state, String hash)
QSession s = QSession.session;
JPAUpdateClause upd = new JPAUpdateClause(em, s);
upd.set(s.state, state).where(s.hash.eq(hash));
return upd.execute();
【讨论】:
【参考方案3】:EntityManager#persist(Object)
的 javadoc 说
投掷: TransactionRequiredException - 如果在 容器管理的实体管理器类型 PersistenceContextType.TRANSACTION 并且没有事务
在调用persist(以及其他一些方法)之前,您需要调用EntityManager.html#getTransaction()
和begin a transaction。完成后不要忘记commit
或rollback
交易。
【讨论】:
以上是关于小型 facelet 应用程序中的 javax.persistence.TransactionRequiredException的主要内容,如果未能解决你的问题,请参考以下文章
如何迭代List 并渲染JSF Facelets中的每个项目
java中的Web开发存在哪些Spring+JSF/Facelets的替代品?
访问 JSF / facelets 中的请求参数的过滤器导致错误编码