小型facelet应用程序中的javax.persistence.TransactionRequiredException

小型facelet应用程序中的javax.persistence.TransactionRequiredException,java,jsf,jpa,Java,Jsf,Jpa,我试图将一些值从一个小型facelet应用程序持久化到一个MySql数据库中,但不断出现这个错误。我有一个相同的应用程序,有一个JPS页面和一个servlet,它使用大致相同的逻辑工作得很好,这是我第一次尝试使用facelets,所以这可能只是一些愚蠢的事情,但我希望得到帮助 谢谢 错误 javax.faces.el.EvaluationException: javax.persistence.TransactionRequiredException at javax.faces.com

我试图将一些值从一个小型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>
<?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>
Book.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());
    }

}
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;
    }
}
persistence.xml

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

jdbc/MySQLDataSource
假的
的javadoc

抛出: TransactionRequiredException-如果在 类型的容器管理实体管理器 PersistenceContextType.TRANSACTION,但没有事务


在调用persist(以及其他一些方法)之前,需要调用和。当您完成事务时,不要忘了
提交
回滚

您正在滥用CDI管理的bean作为业务服务。它没有交易管理的线索。您需要手动管理事务。由于这通常是一个难题,而且您显然正在使用Glassfish,这是一个支持EJB的完整JavaEE容器,因此您更愿意为此使用一个完整的EJB。在EJB中使用
EntityManager
时,容器将完全透明地管理DB事务。一个EJB方法调用算作一个事务(即,当您触发多个DB查询,其中一个查询失败时,所有查询都将自动回滚)

总的来说,您似乎在混合模型、控制器和服务的职责。也不要使您的实体成为托管bean。您还应该绝对不要在Javabean getter方法中执行业务逻辑(例如,
getBooks()
)。当在迭代组件中引用时,它将在每一轮迭代中被调用。假设你有100条记录,那么数据库会被点击100次。这显然是低效的

下面是它应该是什么样子:

模型(实体):

控制器(支持bean):

@Named
@请求范围
公共类图书管理员{
私人书籍;
私人书目;
@EJB
私人图书服务;
@施工后
公共void init(){
书=新书();
books=service.list();
}
公共无效添加(){
服务。保存(书);
init();
}
公共图书getBook(){
还书;
}
公共列表getBooks(){
还书;
}
}
服务(EJB):

@无状态
公共类图书服务{
@持久上下文
私人实体管理者;
公开名单(){
返回em.createQuery(“从Book”,Book.class).getResultList();
}
公共图书查找(整数id){
返回em.find(Book.class,id);
}
公共整数保存(书本){
em.persist(书);
returnbook.getId();
}
公共作废更新(书籍){
em.merge(book);
}
公共作废删除(图书){
em.remove(em.contains(book)?book:em.merge(book));
}
}
视图(Facelet;简化):


...
ID{book.ID}
书名#{book.Title}
作者{book.Author}
价格{book.Price}
(您的编辑和删除按钮没有任何意义,因此我删除了它们,您可能希望将它们放在数据表中)

另见:

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

@Transactional//@NamedQuery工作正常,数据库中已经存在的内容会打印在索引页上,问题是更新数据库。CDI bean不作为EJB提供自动事务管理。看看你到目前为止的答案,也在这里:这肯定消除了一些困惑,感谢伟大的答案,我们将查看这些链接。感谢you@BalusC因此,当使用来自
@EJB
服务类的方法时,可以简单地调用EntityManager的方法,而无需担心/额外考虑事务?@LyK:答案的确如此。另见
@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();
}