4

API Requests using Postman

  • I tried to send HTTP requests to the java backend web server application and perform CRUD operations. GET operations are working fine and gave back the proper response but save, update and delete operations gave the following error.
  • Please note that there are three customer records in my database and I tried to send HTTP requests as follows;
  1. GET http://localhost:8080/tms/api/v1/customers ---> Successful ✔
  2. GET http://localhost:8080/tms/api/v1/customers/3 ---> Successful ✔
  3. DELETE http://localhost:8080/tms/api/v1/customers/3 ---> Unsuccessful ❌

Error Log

Customer ID: 2
02-Feb-2022 09:59:11.271 SEVERE [http-nio-8080-exec-2] com.myapp.web.api.exceptionhandler.AppWideExceptionHandler.globalExceptionHandler null
    javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
        at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:295)
        at com.sun.proxy.$Proxy80.remove(Unknown Source)
        at com.myapp.web.dal.CrudDAOImpl.delete(CrudDAOImpl.java:67)
        at com.myapp.web.business.custom.impl.CustomerBOImpl.deleteCustomer(CustomerBOImpl.java:79)
        at com.myapp.web.api.CustomerController.deleteCustomer(CustomerController.java:116)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
        at org.springframework.web.servlet.FrameworkServlet.doDelete(FrameworkServlet.java:931)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:359)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1735)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

API Layer

  1. CustomerController.java
package com.myapp.web.api;

import com.myapp.web.api.util.ApiUtil;
import com.myapp.web.business.custom.CustomerBO;
import com.myapp.web.dto.CustomerDTO;
import com.myapp.web.exception.IdFormatException;
import com.myapp.web.exception.RecordNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@CrossOrigin(origins = "http://localhost:8080")
@RequestMapping("/api/v1/customers")
@RestController
public class CustomerController {

    @Autowired
    private CustomerBO customerBO;

    /**
     * Get all customers list.
     *
     * @return List<CustomerDTO> customersList.
     */
    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public List<CustomerDTO> getAllCustomers() throws Exception {
        return customerBO.getAllCustomers();
    }

    /**
     * Get customer by customer ID.
     *
     * @return CustomerDTO customer object.
     * @throws IdFormatException       if the ID is not an Integer.
     * @throws RecordNotFoundException if matching customer record not found,
     */
    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE,
            value = "/{id:\\d}")
    public CustomerDTO getCustomerByID(@PathVariable(name = "id") String id) throws Exception {
        System.out.println("CustomerID: " + id);

        Integer customerID = ApiUtil.getIntegerId(id);

        CustomerDTO customer = customerBO.getCustomerByID(customerID);
        System.out.println("Customer Result: " + customer);

        /* If customer not found. */
        if (customer == null) throw new RecordNotFoundException();
        return customer;
    }

    /**
     * Delete customer by Customer ID.
     */
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @DeleteMapping(value = "/{id:\\d}")
    public void deleteCustomer(@PathVariable String id) throws Exception {
        Integer customerID = ApiUtil.getIntegerId(id);
        System.out.println("Customer ID: " + customerID);
        customerBO.deleteCustomer(customerID);
    }

}

Business Layer

  1. SuperBO.java
package com.myapp.web.business;

public interface SuperBO {
}
  1. CustomerBOImpl.java
package com.myapp.web.business.custom.impl;

import com.myapp.web.business.custom.CustomerBO;
import com.myapp.web.business.custom.util.mapper.CustomerDTOMapper;
import com.myapp.web.dal.custom.CustomerDAO;
import com.myapp.web.dto.CustomerDTO;
import com.myapp.web.entity.Customer;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@NoArgsConstructor
@Transactional
@Service
public class CustomerBOImpl implements CustomerBO {

    @Autowired
    private CustomerDAO customerDAO;

    @Autowired
    private CustomerDTOMapper mapper;

    @Override
    public void deleteCustomer(int customerID) throws Exception {
        /* delete. */
        this.customerDAO.delete(customerID);
    }

    @Transactional(readOnly = true)
    @Override
    public CustomerDTO getCustomerByID(int customerID) throws Exception {
        /* get customer by customer ID. */
        return this.mapper.getCustomerDTO(this.customerDAO.get(customerID));
    }

    @Transactional(readOnly = true)
    @Override
    public List<CustomerDTO> getAllCustomers() throws Exception {
        /* get all customers. */
        return this.mapper.getCustomerDTOs(this.customerDAO.getAll());
    }
}
  1. CustomerDTOMapper.java
package com.myapp.web.business.custom.util.mapper;

import com.myapp.web.dto.CustomerDTO;
import com.myapp.web.entity.Customer;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring")
public abstract class CustomerDTOMapper {

    /*  -------------------- Customer  -------------------- */
    public abstract Customer getCustomer(CustomerDTO customerDTO);

    public abstract CustomerDTO getCustomerDTO(Customer customer);

    public abstract List<CustomerDTO> getCustomerDTOs(List<Customer> customerList);
}

Data Access Layer

  1. SuperDAO.java
package com.myapp.web.dal;

import javax.persistence.EntityManager;

public interface SuperDAO {
    EntityManager getEntityManager();
}

  1. CrudDAO.java
package com.myapp.web.dal;

import com.myapp.web.entity.SuperEntity;

import java.io.Serializable;
import java.util.List;

public interface CrudDAO<T extends SuperEntity<Serializable>, PK extends Serializable> extends SuperDAO {

    /**
     * saving an entity and return the entity.
     *
     * @return T SuperEntity. */
    T save(T entity) throws Exception;

    void update(T entity) throws Exception;

    void delete(PK key) throws Exception;

    T get(PK key) throws Exception;

    List<T> getAll() throws Exception;
}
  1. CrudDAOImpl.java
package com.myapp.web.dal;

import com.myapp.web.entity.SuperEntity;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

public class CrudDAOImpl<T extends SuperEntity<Serializable>, K extends Serializable> implements CrudDAO<T, K> {

    private final Class<T> entityClass;
    @PersistenceContext
    private EntityManager entityManager;

    public CrudDAOImpl() {
        this.entityClass =
                (Class<T>) (((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments()[0]);
    }

    /**
     * This method is used to pass the EntityManager to the lower level classes that extend the CrudDAOImpl class.
     */
    public EntityManager getEntityManager() {
        return this.entityManager;
    }

    @Override
    public void delete(K key) throws Exception {
        this.entityManager.remove(key);
    }

    @Override
    public T get(K key) throws Exception {
        return this.entityManager.find(this.entityClass, key);
    }

    @Override
    public List<T> getAll() throws Exception {
        List<T> resultList = (List<T>) this.entityManager.
                createQuery("SELECT e FROM " + this.entityClass.getName() + " e").getResultList();
        return resultList;
    }
}
  1. CustomerDAO.java
package com.myapp.web.dal.custom;

import com.myapp.web.dal.CrudDAO;
import com.myapp.web.entity.Customer;

public interface CustomerDAO extends CrudDAO<Customer, Integer> {
}
  1. CustomerDAOImpl.java
package com.myapp.web.dal.custom.impl;

import com.myapp.web.dal.CrudDAOImpl;
import com.myapp.web.dal.custom.CustomerDAO;
import com.myapp.web.entity.Customer;
import org.springframework.stereotype.Repository;

@Repository
public class CustomerDAOImpl extends CrudDAOImpl<Customer, Integer> implements CustomerDAO {
}

Here's what I have tried.

I tried unit testing. BO layer and DAO layer unit test case worked as intended but when I check the API by sending HTTP request using Postman, save, update and delete cannot be performed using the API.

  1. CustomerDAOImplTest.java ----> TEST PASSED ! ✔
package com.myapp.web.dal.custom.impl;

import com.myapp.web.WebAppConfig;
import com.myapp.web.WebRootConfig;
import com.myapp.web.dal.custom.CustomerDAO;
import com.myapp.web.entity.Customer;
import com.myapp.web.entity.enumeration.GenderTypes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {WebRootConfig.class, WebAppConfig.class})
public class CustomerDAOImplTest {

    @Autowired
    private CustomerDAO customerDAO;

    @Test
    public void checkCustomerDAO() {
        assertNotNull(customerDAO);
    }

    @Transactional
    @Test
    public void getCustomerByID() throws Exception {
        assertNotNull(customerDAO);

        Customer customer = this.customerDAO.get(10);
        System.out.println("GET Customer Entity: " + customer);
        assertNotNull(customer);
    }

    @Test
    public void getAllCustomers() throws Exception {
        List<Customer> customersList = this.customerDAO.getAll();
        assertEquals(2, customersList.size());
    }


    @Transactional
    @Test
    public void deleteCustomer() throws Exception {
        assertNotNull(this.customerDAO);

        /* delete */
        this.customerDAO.delete(3);

        Customer customerFromDB = this.customerDAO.get(10);
        assertNull(customerFromDB);
    }
}

CustomerDAOImplTest-delete

  1. CustomerBOImpl.java ----> TEST PASSED ! ✔
package com.myapp.web.business.custom.impl;

import com.myapp.web.WebAppConfig;
import com.myapp.web.WebRootConfig;
import com.myapp.web.business.custom.CustomerBO;
import com.myapp.web.dto.CustomerDTO;
import com.myapp.web.entity.enumeration.GenderTypes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {WebRootConfig.class, WebAppConfig.class})
public class CustomerBOImplTest {

    @Autowired
    private CustomerBO customerBO;

    @Test
    public void checkCustomerBO() {
        assertNotNull(customerBO);
    }

    //    @Transactional
    @Test
    public void deleteCustomer() throws Exception {
        assertNotNull(this.customerBO);

        /* delete */
        this.customerBO.deleteCustomer(3);

        CustomerDTO customerDTOFromDB = this.customerBO.getCustomerByID(10);
        assertNull(customerDTOFromDB);
    }

}

CustomerBOImplTest-delete

pom.xml dependencies


<dependencies>

    <!-- Servlet-API -->
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>

    <!-- MySQL connector -->
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.27</version>
        <scope>compile</scope>
    </dependency>

    <!-- SLF4J for logging-->
    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-jdk14</artifactId>
        <version>2.0.0-alpha1</version>
        <scope>compile</scope>
    </dependency>

    <!-- Junit4 for unit testing -->
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>

    <!-- Apache Commons Validator -->
    <!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
    <dependency>
        <groupId>commons-validator</groupId>
        <artifactId>commons-validator</artifactId>
        <version>1.7</version>
    </dependency>


    <!-- mapstruct -->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>

    <!-- lombok  -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
        <scope>provided</scope>
    </dependency>

    <!-- Hibernate -->
    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.6.3.Final</version>
    </dependency>


    <!-- Apache Commons Codecs -->
    <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.15</version>
    </dependency>


    <!-- Spring Data Access ==================================================================== -->

    <!--Spring ORM -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.3.14</version>
    </dependency>

    <!-- Apache Commons DBCP -->
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>2.9.0</version>
    </dependency>

    <!-- Spring Data Access - Spring AOP - Aspectj ========================================== -->
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.9.7</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.7</version>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring Web MVC =================================================================== -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.15</version>
    </dependency>

    <!-- JACKSON DATABIND -->
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.1</version>
    </dependency>

    <!--Jackson Datatype-->
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>2.13.1</version>
    </dependency>


    <!--Spring TestContext Framework-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.15</version>
        <scope>test</scope>
    </dependency>

</dependencies>

Development Enviroment

  1. Java 8
  2. Tomcat 9

I refer to these following questions and answers but, I could not fix the issue. I would appreciate some help to fix the error and, understand the error.

  1. https://stackoverflow.com/a/32552558/12898581
  2. javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread
  3. https://www.wilfriedbarth.com/til/2018-03-31-spring-transactional-annotation/
Dhanusha_Perera07
  • 3,347
  • 5
  • 14
  • 22

0 Answers0