0

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>
Eddie Romanenco
  • 311
  • 4
  • 16
  • why would a `select` be transactional? – Scary Wombat Apr 17 '19 at 00:48
  • @ScaryWombat When removing the annotation I get "Could not obtain transaction-synchronized Session for current thread", so I figured I should mark it as transictional. – Eddie Romanenco Apr 17 '19 at 00:52
  • search for that error and you will get results like https://stackoverflow.com/questions/26203446/spring-hibernate-could-not-obtain-transaction-synchronized-session-for-current – Scary Wombat Apr 17 '19 at 00:53
  • @ScaryWombat I saw this page and followed some of the suggestions in it, I already have , and adding Transnational to my repository didn't change much. Is there something else I might have missed? – Eddie Romanenco Apr 17 '19 at 01:07
  • Is your `Projects` class correctly annotated? – Scary Wombat Apr 17 '19 at 01:10
  • @ScaryWombat It's autogenerated from my MySQL server by the Hibernate addon for eclipse, I've actually added the Transnational into my controller and now this exception appears in a different place during runtime - it refers to Hibernate: select technologi0_.tech as col_0_0_ from ajwt.technologies technologi0_ which is part of my getAllTechnologies methods, I've added my Technologies class and the updated service classes. Where can I possible add any more annotations? :( – Eddie Romanenco Apr 17 '19 at 01:23
  • why would `getAllTechnologies()` be `@Transactional`? Also should the sql simply be `from Technologies` ? – Scary Wombat Apr 17 '19 at 01:38
  • @ScaryWombat I want to use 'tech' in form:checkboxes, that's why I want it only to show one field (which contains the names of the technologies. – Eddie Romanenco Apr 17 '19 at 01:48
  • @ScaryWombat Where do you suggest I put the Transactional, if at all? – Eddie Romanenco Apr 17 '19 at 01:49
  • 1
    Remove the `SessionFactory` both from your code and configuration and use the `EntityManager` instead. You are using JPA not plain hibernate. – M. Deinum Apr 17 '19 at 05:48
  • @M.Deinum What Query import should I make to be able to run queries using the EntityManager? – Eddie Romanenco Apr 17 '19 at 19:55
  • @M.Deinum Well it worked. Can you post your comment as an answer so I could vote it as one? – Eddie Romanenco Apr 17 '19 at 22:59

1 Answers1

0

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.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224