没有可用于当前线程的实际事务的 EntityManager - 无法可靠地处理“删除”调用

问题描述 投票:0回答:1

使用 Postman 的 API 请求

  • 我尝试向java后端Web服务器应用程序发送HTTP请求并执行CRUD操作。
    GET
    操作工作正常并返回正确的响应,但保存、更新和删除操作出现以下错误。
  • 请注意,我的数据库中有 3 条客户记录,我尝试发送 HTTP 请求如下;
  1. 获取http://localhost:8080/tms/api/v1/customers ---> 成功✔
  2. 获取http://localhost:8080/tms/api/v1/customers/3 ---> 成功✔
  3. 删除 http://localhost:8080/tms/api/v1/customers/3 ---> 不成功 ❌

错误日志

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层

  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);
    }

}

业务层

  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. 客户DTOMapper.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);
}

数据访问层

  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 {
}

这是我尝试过的。

我尝试过单元测试。 BO 层和 DAO 层单元测试用例按预期工作,但是当我通过使用 Postman 发送 HTTP 请求来检查 API 时,无法使用 API 执行保存、更新和删除。

  1. CustomerDAOImplTest.java ----> 测试通过! ✔
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);
    }
}

  1. CustomerBOImpl.java ----> 测试通过! ✔
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);
    }

}

pom.xml 依赖项


<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>

发展环境

  1. Java 8
  2. 雄猫9

我参考了以下问题和答案,但是我无法解决该问题。我希望得到一些帮助来解决这个问题 错误并理解错误。

  1. https://stackoverflow.com/a/32552558/12898581
  2. javax.persistence.TransactionRequiredException:没有可用于当前线程的实际事务的EntityManager
  3. https://www.wilfriedbarth.com/til/2018-03-31-spring-transactional-annotation/
java spring spring-mvc java-8
1个回答
0
投票

如果您在使用 Spring Boot 时遇到此错误,请尝试以下操作:

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = { Exception.class })
private void deleteResult(LabResInvstResult dto, List<Long> relInvstid) {
    try{
        labResultRepo.deleteById(dto.getId());          
        LabResInvstResult result = labResultRepo.save(dto);

    }catch(Exception e){
        e.printStackTrace();
    }
}

删除时,它会抛出异常“由以下原因引起:javax.persistence.TransactionRequiredException:没有可用于当前线程的实际事务的EntityManager – 无法可靠地处理‘删除’调用”

© www.soinside.com 2019 - 2024. All rights reserved.