Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javax.persistence.TransactionRequiredException in small facelet application

Tags:

java

jpa

jsf

I'm trying to persist some values to a MySql database from a small facelet application but keep getting this error. I had this same application with a JPS page and a servlet and it worked fine with roughly the same logic, this is my first time trying to work with facelets so it might just be something silly but i'd appriciate the help.

Thanks

error

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>
like image 464
Sergei Avatar asked May 13 '13 15:05

Sergei


1 Answers

You're abusing a CDI managed bean as a business service. It has no clues of transaction management. You'd need to manually manage transactions. As this is usually a pain and you're apparently using Glassfish, which is a fullworthy Java EE container supporting EJBs, you'd rather like to use a fullworthy EJB for this. When using EntityManager inside an EJB, the container will fully transparently manage DB transactions. One EJB method call counts as a single transaction (i.e. when you fire multiple DB queries and one of them fails, then everything will be automatically rolled back).

Overall, you seem to be mixing the responsibilities of the model, controller and service. Do not make your entity a managed bean as well. You should further absolutely also not perform business logic in a Javabean getter method (e.g. getBooks()). When referenced in an iterating component, it's invoked during every iteration round. So imagine that you've 100 records, then the DB will be hit 100 times. This is plain inefficient.

Here's how it should all look like:

Model (the entity):

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

Controller (the backing 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;
    }

}

Service (the 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));
    }

}

View (the Facelet; simplified):

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

(your edit and delete buttons didn't make any sense, so I removed them, you might want to put them inside the data table)

See also:

  • Recommended JSF 2.0 CRUD frameworks
  • Why JSF calls getters multiple times
like image 68
BalusC Avatar answered Nov 05 '22 17:11

BalusC