小面片应用程序中的javax.persistence.TransactionRequiredException

我试图从一个小的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

     Vecka19   

ID TITLE AUTHOR PRICE
${book.bookId} ${book.title} ${book.author} ${book.price}

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

    jdbc/MySQLDataSource false    

您滥用CDI托管bean作为业务服务。 它没有交易管理的线索。 您需要手动管理交易。 由于这通常是一种痛苦,并且您显然正在使用Glassfish,它是一个支持EJB的全面Java EE容器,因此您更愿意使用一个值得一提的EJB。 在EJB中使用EntityManager ,容器将完全透明地管理数据库事务。 一个EJB方法调用计为单个事务(即当您触发多个数据库查询并且其中一个失败时,所有内容都将自动回滚)。

总的来说,您似乎在混合模型,控制器和服务的职责。 不要将您的实体设置为托管bean。 您还应该绝对不在Javabean getter方法中执行业务逻辑(例如getBooks() )。 在迭代组件中引用时,它会在每次迭代循环期间调用。 所以想象你有100条记录,然后数据库会被击中100次。 这显然效率低下。

这是它应该如何看起来像:

模型(实体):

 @Entity @Table(name = "BOOKS") public class Book implements Serializable { // ... } 

控制器(支持bean):

 @Named @RequestScoped public class BookController { private Book book; private List 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 getBooks() { return books; } } 

服务(EJB):

 @Stateless public class BookService { @PersistenceContext private EntityManager em; public List 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;简化):

     ...  ID#{book.id} Title#{book.title} Author#{book.author} Price#{book.price}  

(你的编辑和删除按钮没有任何意义,所以我删除它们,你可能想把它们放在数据表中)

也可以看看:

  • 推荐的JSF 2.0 CRUD框架
  • 为什么JSF多次调用getter

只需在方法上添加@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(); } 

EntityManager#persist(Object)的javadoc说

抛出: TransactionRequiredException – 如果在PersistenceContextType.TRANSACTION类型的容器管理的实体管理器上调用,并且没有事务

您需要调用EntityManager.html#getTransaction()并在调用persist(以及其他一些方法)之前开始事务 。 完成后不要忘记commitrollback事务。