我的 Spring+Hibernate 服务器小程序中出现 TransactionRequiredException

I'm getting an TransactionRequiredException in my Spring+Hibernate server applet

我一直在构建一个 Spring MVC 应用程序,它使用 SpringDataJPA 和休眠,并利用 MySQL。从某个时候开始,我在主网页(名为 'main')上收到了一个 TransactionRequiredException,我无法摆脱它。你能告诉我我的配置/代码哪里出错了吗?

appconfig-data.xml

<bean id="dataSource"
    class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName"
        value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

<!-- Configure the entity manager factory bean -->
<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="com.ajwt.entities" />
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
            <prop key="hibernate.show_sql">true</prop>
        </props>
    </property>
</bean>

<!-- Configure the transaction manager bean -->
<bean id="transactionManager"
    class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory"
        ref="entityManagerFactory" />
    <property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven
    transaction-manager="transactionManager" />

<!-- Configure Spring Data JPA and set the base package of the repository 
    interfaces -->

<!-- Configure Spring Data JPA and set the base package of the repository 
    interfaces -->

<jpa:repositories base-package="com.ajwt.repositories" />

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="com.ajwt.entities" />
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
            <prop key="hibernate.show_sql">true</prop>
        </props>
    </property>
</bean>

堆栈跟踪

Hibernate: select projects0_.id as id1_2_, projects0_.description as
descript2_2_, projects0_.duedate as duedate3_2_, projects0_.grade as
grade4_2_, projects0_.pname as pname5_2_, projects0_.sid as sid6_2_,
projects0_.state as state7_2_ from ajwt.projects projects0_
אפר׳ 17, 2019 3:14:24 לפנה״צ org.apache.catalina.core.StandardWrapperValve
invoke
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/ajwt]
threw exception [Request processing failed; nested exception is
javax.persistence.TransactionRequiredException: no transaction is in
progress] with root cause
javax.persistence.TransactionRequiredException: no transaction is in
progress

控制器

@RequestMapping(value = "/", method = RequestMethod.GET)
public String defaultPage(Model model) {
return "redirect:/main";
}

@RequestMapping(value = { "/main" }, method = RequestMethod.GET)
public String welcome(Model model) {
model.addAttribute("addProject", new Projects());
model.addAttribute("projectList", projectService.getAllProjects());
model.addAttribute("availableProjectList",
projectService.getAvailableProjects());
model.addAttribute("scholarList", userService.getScholars());
model.addAttribute("technologiesList",
techService.getAllTechnologies());

return "main";
}

@RequestMapping(value = "/main", method = RequestMethod.POST)
public String addProject(@ModelAttribute("addProject") Projects project,
BindingResult bindingResult, ModelMap model) {
projectService.save(project);
return "redirect:/main";
}

项目服务

@Service("projectService")
public class ProjectServiceImpl implements ProjectService {
    @Autowired
    private ProjectRepository projectRepository;
    @Autowired
    private SessionFactory sessionFactory;

    public void save(Projects project) {
        projectRepository.save(project);
    }
    @Transactional
    @Override
    public List<Projects> getAllProjects() {
        TypedQuery<Projects> query = sessionFactory.getCurrentSession().createQuery("from Projects");
        return query.getResultList();
    }
    @Transactional
    @Override
    public List<Projects> getAvailableProjects() {
        TypedQuery<Projects> query = sessionFactory.getCurrentSession()
                .createQuery("from Projects where state = 'PROJECT_AVAIL'");
        return query.getResultList();
    }
    @Transactional
    @Override
    public List<Projects> getMyProjects(int id) {
        TypedQuery<Projects> query = sessionFactory.getCurrentSession().createQuery("from Projects");
        return query.getResultList();
    }
    @Transactional
    @Override
    public void addProject(Projects project) {
        projectRepository.save(project);
    }
}

技术服务

@Service("technologiesService")
public class TechServiceImpl implements TechService {
    @Autowired
    private ProjectRepository projectRepository;
    @Autowired
    private SessionFactory sessionFactory;
    @Override
    @Transactional
    public List<Technologies> getAllTechnologies() {
        TypedQuery<Technologies> query = sessionFactory.getCurrentSession().createQuery("select tech from Technologies");
        return query.getResultList();
    }
}

技术

package com.ajwt.entities;
// Generated 17 באפר׳ 2019, 2:45:49 by Hibernate Tools 4.3.5.Final

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

/**
 * Technologies generated by hbm2java
 */
@Entity
@Table(name = "technologies", catalog = "ajwt")
public class Technologies implements java.io.Serializable {

    private Integer id;
    private String tech;
    private Set<Projects> projectses = new HashSet<Projects>(0);

    public Technologies() {
    }

    public Technologies(String tech) {
        this.tech = tech;
    }

    public Technologies(String tech, Set<Projects> projectses) {
        this.tech = tech;
        this.projectses = projectses;
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)

    @Column(name = "id", unique = true, nullable = false)
    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "tech", nullable = false, length = 45)
    public String getTech() {
        return this.tech;
    }

    public void setTech(String tech) {
        this.tech = tech;
    }

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "project_technologies", catalog = "ajwt", joinColumns = {
            @JoinColumn(name = "tid", nullable = false, updatable = false) }, inverseJoinColumns = {
                    @JoinColumn(name = "pid", nullable = false, updatable = false) })
    public Set<Projects> getProjectses() {
        return this.projectses;
    }

    public void setProjectses(Set<Projects> projectses) {
        this.projectses = projectses;
    }

}

main.jsp

<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>

<html>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="Edward Romanenco">
<head>
<title>Administrator Console</title>
<link rel="stylesheet"
    href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
    crossorigin="anonymous">
<link href="${contextPath}/resources/css/common.css" rel="stylesheet">
</head>
<body>
    <h2>Project Management Screen</h2>
    <c:if test="${pageContext.request.userPrincipal.name != null}">
        <form id="logoutForm" method="POST" action="${contextPath}/logout">
            <input type="hidden" name="${_csrf.parameterName}"
                value="${_csrf.token}" />
        </form>
        <h6>
            Welcome ${pageContext.request.userPrincipal.name}! | <a
                onclick="document.forms['logoutForm'].submit()">Logout</a>
        </h6>
    </c:if>
    <div>
        <h3>My Projects</h3>
        <c:choose>
            <c:when test="${!empty projectList}">
                <table class="data">
                    <tr>
                        <th>Project Name</th>
                        <th>Due Date</th>
                    </tr>
                    <c:forEach items="${projectList}" var="project">
                        <tr>
                            <td>${project.pname}</td>
                            <td><c:choose>
                                    <c:when test="${project.duedate}!=null">${project.duedate}</c:when>
                                    <c:otherwise>No Due Date Yet</c:otherwise>
                                </c:choose></td>
                            <!--<td><a href="edit/${project.id}">Edit</a></td>
                            <td><a href="delete/${project.id}">Delete</a></td>-->
                        </tr>
                    </c:forEach>
                </table>
            </c:when>
            <c:otherwise>
            You don't participate in any project, look over the available projects section to start!
            </c:otherwise>
        </c:choose>
    </div>
    <div>
        <h3>Available Projects</h3>
        <c:choose>
            <c:when test="${!empty availableProjectList}">
                <table class="data">
                    <tr>
                        <th>Project Name</th>
                        <th>Due Date</th>
                    </tr>
                    <c:forEach items="${availableProjectList}" var="project">
                        <tr>
                            <td>${project.pname}</td>
                            <td>${project.duedate}</td>
                            <!--  <td><a href="join/${project.id}">join</a></td>-->
                        </tr>
                    </c:forEach>
                </table>
            </c:when>
            <c:otherwise>
    There are no available projects currently, make sure you come back soon as those are added daily!
  </c:otherwise>
        </c:choose>
    </div>
    <h3>Suggest Projects</h3>
    <div>
        <form:form method="POST" modelAttribute="addProject">
            <table>
                <spring:bind path="pname">
                    <tr>
                        <td><form:label path="pname">Project Name</form:label></td>
                        <td><form:input path="pname" /></td>
                    </tr>
                </spring:bind>
                <spring:bind path="description">
                    <tr>
                        <td><form:label path="description">Description</form:label></td>
                        <td><form:textarea path="description" rows="5" cols="25" /></td>
                    </tr>
                </spring:bind>
                <spring:bind path="sid">
                    <tr>
                        <td><form:label path="sid">Scholar:</form:label></td>
                        <td><form:select id="projectSelect" name="userId" path="sid">
                                <c:forEach var="theUser" items="${scholarList}">
                                    <form:option value="${scholarList.id}">
                                        <c:out value="${scholarList.fname} ${scholarList.lname}" />
                                    </form:option>
                                </c:forEach>
                            </form:select></td>
                    </tr>
                </spring:bind>
                <spring:bind path="technologieses">
                    <tr>
                        <td><form:label path="technologieses">Technologies:</form:label></td>
                        <td><form:checkboxes items="${technologiesList}"
                                path="technologieses" /></td>
                    </tr>
                </spring:bind>
                <tr>
                    <td colspan="2"><input type="submit" value="Submit" /></td>
                </tr>
            </table>
        </form:form>
    </div>
    <script
        src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script
        src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
        integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
        crossorigin="anonymous"></script>
</body>
</html>

问题是你们都配置了一个EntityManagerFactory和一个SessionFactory。您现在有 2 个单独的实例来管理您的依赖项。您配置的事务管理器适用于 JPA 而不是普通的 Hibernate。

不使用普通的 Hibernate,而是使用 JPA。从您的配置中删除 SessionFactory 的定义,而不是自动装配 SessionFactory 使用 JPA 提供的 EntityManager

@Service("technologiesService")
public class TechServiceImpl implements TechService {

    @Autowired
    private ProjectRepository projectRepository;

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    @Transactional
    public List<Technologies> getAllTechnologies() {
        return entityManager.createQuery("SELECT t FROM Technologies t").getResultList();
    }
}

Spring 将负责注入当前绑定 EntityManager