I've been building a Spring MVC application that uses SpringDataJPA and hibernate, and leverages MySQL. From some point on I am getting an TransactionRequiredException on my main web-page (named 'main'), which I wasn't able to get rid of. Can you show me where my configuration / code went wrong?
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>
Stack trace
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
Controller
@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";
}
Projects Service
@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);
}
}
Technologies Service
@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();
}
}
Technologies
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>
The problem is that you both have configured an EntityManagerFactory
and a SessionFactory
. You have now 2 seperate instances managing your dependencies. The transaction manager you configured is for JPA and not for plain Hibernate.
Instead of using plain Hibernate, use JPA. Remove the definition of the SessionFactory
from your configuration and instead of autowiring the SessionFactory
use the EntityManager
provided by JPA.
@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 will take care of injecting the current bound EntityManager
.