javax.persistence.TransactionRequiredException в небольшом приложении Facelet
Я пытаюсь сохранить некоторые значения в базе данных MySql из небольшого приложения Facelet, но продолжаю получать эту ошибку. У меня было то же самое приложение со страницей JPS и сервлетом, и оно работало нормально с примерно той же логикой, это мой первый раз, когда я пытаюсь работать с 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>
3 ответа
Вы злоупотребляете управляемым компонентом CDI в качестве бизнес-службы. Он не имеет никаких ключей к управлению транзакциями. Вам нужно будет вручную управлять транзакциями. Так как это обычно является проблемой, и вы, очевидно, используете Glassfish, представляющий собой полноценный контейнер Java EE, поддерживающий EJB, вам лучше использовать для этого полноценный EJB. Когда используешь EntityManager
внутри EJB контейнер будет полностью прозрачно управлять транзакциями БД. Один вызов метода EJB считается одной транзакцией (т. Е. Когда вы запускаете несколько запросов к БД и один из них завершается неудачно, все автоматически откатывается).
В целом вы, кажется, смешиваете обязанности модели, контроллера и сервиса. Не делайте вашу сущность управляемым бином. Кроме того, вы также абсолютно не должны выполнять бизнес-логику в методе получения Javabean (например, getBooks()
). При ссылке в итерационном компоненте он вызывается во время каждого раунда итерации. Итак, представьте, что у вас 100 записей, тогда БД будет поражена 100 раз. Это явно неэффективно.
Вот как это должно выглядеть:
Модель (сущность):
@Entity
@Table(name = "BOOKS")
public class Book implements Serializable {
// ...
}
Контроллер (бэк):
@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>
(Ваши кнопки редактирования и удаления не имели никакого смысла, поэтому я удалил их, возможно, вы захотите поместить их в таблицу данных)
Смотрите также:
Просто добавь @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();
}
Javadoc для EntityManager#persist(Object)
говорит
Броски: TransactionRequiredException - если вызвано на диспетчере сущностей, управляемом контейнером типа PersistenceContextType.TRANSACTION, и нет никакой транзакции
Вам нужно позвонить EntityManager.html#getTransaction()
и начните транзакцию, прежде чем вызывать persist (и некоторые другие методы). Не забудьте также commit
или же rollback
сделка, когда вы закончите.