Ошибка Spring Boot: ни одна транзакция не выполняется

Я получаю эту ошибку, когда пытаюсь сохранить объект ChatUser из запроса /showSignUpForm. Сведения об объекте успешно сохраняются в БД, но в /processSignUpForm выдается ошибка «jakarta.persistence.TransactionRequiredException: транзакция не выполняется».

Это мой класс контроллера

@Controller
public class ChatUserController {

    @Autowired
    private ChatUserService chatUserService;
    
    @GetMapping("/showLoginForm")
    public String showLoginForm(Model theModel){
        return "login-form";
    }
    
    @GetMapping("/showSignUpForm")
    public String showSignUpForm(Model theModel) {
        theModel.addAttribute("chatUser", new ChatUser());
        return "sign-up-form";
    }
    
    @PostMapping("/processSignUpForm")
    @Transactional
    public String processSignUpForm(@ModelAttribute("chatUser") ChatUser chatUser) {
        
        System.out.println(chatUser.toString());
        chatUserService.saveChatUser(chatUser);
        return "chat-page";
    }

}

Это мой интерфейс DAO

public interface ChatUserDAO {
    
    public List<ChatUser> getchatUsers();

    public void saveChatUser(ChatUser theChatUser);
    
    public ChatUser getChatUser(int theId);

    public void deleteChatUser(int theId);

}

Реализация ДАО:

@Repository
public class ChatUserDaoImpl implements ChatUserDAO{
    
    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public List<ChatUser> getchatUsers() {
        Session currentSession = sessionFactory.getCurrentSession();
        Query<ChatUser> theQuery = currentSession.createQuery("from ChatUser", ChatUser.class);
        List<ChatUser> chatUsers = theQuery.getResultList();
        return chatUsers;
    }

    @SuppressWarnings("deprecation")
    @Override
    public void saveChatUser(ChatUser theChatUser) {
        Session currentSession = sessionFactory.getCurrentSession();
        currentSession.saveOrUpdate(theChatUser);   
    }
}

Это моя сущность ChatUser

@Entity
@Table(name = "chat_user")
public class ChatUser {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private int user_id;
    
    @Column(name = "user_name")
    private String user_name;
    
    @Column(name = "user_email")
    private String user_email;
    
    @Column(name = "user_password")
    private String user_password;
    
    
    @ManyToMany(fetch=FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE,CascadeType.DETACH,CascadeType.REFRESH })
    @JoinTable(
            name = "group_user",
            joinColumns=@JoinColumn(name = "user_id"),
            inverseJoinColumns=@JoinColumn(name = "group_id")
            )
    private List<ChatGroup> chatGroups;

    public ChatUser() {
        
    }

    public ChatUser(String user_name, String user_email, String user_password) {
        this.user_name = user_name;
        this.user_email = user_email;
        this.user_password = user_password;
    }
}

Это моя реализация класса обслуживания

@Service
public class ChatUserServiceImpl implements ChatUserService{

    @Autowired
    private ChatUserDAO chatUserDao;
    
    @Override
    @Transactional
    public List<ChatUser> getChatUsers() {
        return chatUserDao.getchatUsers();
    }

    @Override
    @Transactional
    public void saveChatUser(ChatUser theChatUser) {
        chatUserDao.saveChatUser(theChatUser);
    }
    
    @Override
    @Transactional
    public ChatUser getChatUser(int theId) {
        return chatUserDao.getChatUser(theId);
    }

    @Override
    @Transactional
    public void deleteChatUser(int theId) {
        chatUserDao.deleteChatUser(theId);
    }

}

Пожалуйста помоги. Спасибо.

хотя объект сохраняется, но ошибка все равно сохраняется.

Ошибка

веб.xml

<?xml version = "1.0" encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns = "http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation = "http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id = "WebApp_ID" version = "3.1">
  <display-name>spring-mvc-crud-demo</display-name>

  <absolute-ordering />

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring-mvc-crud-demo-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

весна-mvc-crud-demo-servlet.xml

<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
    xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context = "http://www.springframework.org/schema/context"
    xmlns:tx = "http://www.springframework.org/schema/tx"
    xmlns:mvc = "http://www.springframework.org/schema/mvc"
    xsi:schemaLocation = "
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- Add support for component scanning -->
    <context:component-scan base-package = "com.springprojects.realtimechatapp" />

    <!-- Add support for conversion, formatting and validation support -->
    <mvc:annotation-driven/>

    <!-- Define Spring MVC view resolver -->
    <bean
        class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name = "prefix" value = "/WEB-INF/views/" />
        <property name = "suffix" value = ".jsp" />
    </bean>

    <!-- Step 1: Define Database DataSource / connection pool -->
    <bean id = "myDataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method = "close">
        <property name = "driverClass" value = "com.mysql.cj.jdbc.Driver" />
        <property name = "jdbcUrl" value = "jdbc:mysql://localhost:3306/chat_db?useSSL=false&amp;serverTimezone=UTC" />
        <property name = "user" value = "root" />
        <property name = "password" value = "mysql" /> 

        <!-- these are connection pool properties for C3P0 -->
        <property name = "minPoolSize" value = "5" />
        <property name = "maxPoolSize" value = "20" />
        <property name = "maxIdleTime" value = "30000" />
    </bean>  
    
    <!-- Step 2: Setup Hibernate session factory -->
    <bean id = "sessionFactory"
        class = "org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name = "dataSource" ref = "myDataSource" />
        <property name = "packagesToScan" value = "com.springprojects.realtimechatapp" />
        <property name = "hibernateProperties">
           <props>
              <prop key = "hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
              <prop key = "hibernate.show_sql">true</prop>
           </props>
        </property>
   </bean>    

    <!-- Step 3: Setup Hibernate transaction manager -->
    <bean id = "myTransactionManager"
            class = "org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name = "sessionFactory" ref = "sessionFactory"/>
    </bean>
    
    <!-- Step 4: Enable configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager = "myTransactionManager" />

    
    <!-- Add support for reading web resources: css, images, js, etc. -->
    <mvc:resources location = "/resources/" mapping = "/resources/**"></mvc:resources>
    
    
</beans>

Основной класс приложения

package com.springprojects.realtimechatapp;

import java.awt.Desktop;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement
public class RealtimechatappApplication {

    public static void main(String[] args) {

        SpringApplication.run(RealtimechatappApplication.class, args);
        if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
            try {
                Desktop.getDesktop().browse(new URI("http://localhost:8080/"));
            } catch (IOException | URISyntaxException e) {
                e.printStackTrace();
            }
        }
    }

}

application.properties

server.port=8080
spring.application.name=realtimechatapp
spring.datasource.url=jdbc:mysql://localhost:3306/chat_db
spring.datasource.username=root
spring.datasource.password=mysql
spring.jpa.hibernate.ddl-auto=update

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp


logging.level.org.hibernate.type.descriptor.sql=trace
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.allow_update_outside_transaction=true

spring.jpa.open-in-view=true
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext

pom.xml

<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springprojects</groupId>
    <artifactId>realtimechatapp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>realtimechatapp</name>
    <description>Chat Application using Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>10.1.18</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <image>
                        <builder>paketobuildpacks/builder-jammy-base:latest</builder>
                    </image>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Я не знаю, является ли это основной причиной, и я бы подумал, что все должно быть в порядке, но у вас есть вложенные @Transactional аннотации: одна на вашем контроллере и одна на вашем ChatUserServiceImpl. Ошибка связана с вашим контроллером, поэтому, возможно, транзакция больше не активна к моменту выхода из этого метода, поскольку она была закрыта при выходе из контроллера. Что произойдет, если вы удалите @Transactional из ChatUserServiceImpl?

Polly Shaw 24.05.2024 09:56

Привет. Я удалил @Transactional из ChatUserServiceImpl и сохранил его только на контроллере, но он по-прежнему выдает ту же ошибку.

Dhananjai Saini 24.05.2024 10:37

Есть ли у вас какие-либо классы конфигурации, настраивающие TransactionManager?

Polly Shaw 24.05.2024 10:54

Да, у меня есть. Отредактировал основной вопрос, добавив еще 2 файла для справки. Но я думаю, что это не работает, мне все равно пришлось добавить свойства базы данных в файл application.properties, иначе выдавалась ошибка «URL-адрес источника данных отсутствует»

Dhananjai Saini 24.05.2024 11:28

Привет, Полли. Я могу устранить эту ошибку, добавив это свойство в application.properties Spring.jpa.properties.hibernate.allow_update_outside_transac‌​tion=true. Но знаете ли вы, почему мой файл web.xml не работает с файлом сервлета Spring- mvc-crud-demo-servlet.xml, потому что мне еще пришлось редактировать application.properties.

Dhananjai Saini 24.05.2024 14:45

Я попытался воспроизвести приложение весенней загрузки с вашими XML-файлами и заставил их прочитать файлы приложения, добавив @ImportResource({"classpath:/WEB-INF/spring-mvc-crud-demo-se‌​rvlet.xml"} ) в качестве аннотации к @SpringBootApplication, но без особого успеха, так как он вышел из строя из-за дублирования объявлений bean-компонентов, поэтому моя единственная теория заключается в том, что ваш файл сервлета вообще не читается, и вы используете значения по умолчанию, которые не включают настройку менеджера транзакций. . Возможно, опубликуйте свой класс приложения и свои application.properties.

Polly Shaw 26.05.2024 00:37

Добавлены новые файлы

Dhananjai Saini 26.05.2024 07:04

Я воспроизвел эту ошибку, поэтому спасибо за эти файлы! Поскольку все, что касается вашего источника данных, определено в вашем файле application.properties, я не уверен, что ваши файлы конфигурации xml вообще используются из-за отсутствия каких-либо аннотаций @ImportResource. Я думаю, что проблема в том, что sessionFactory.getCurrentSession() каким-то образом возвращает сеанс, находящийся за пределами текущего контекста транзакции, хотя в документации говорится, что этого не должно быть.

Polly Shaw 27.05.2024 11:28
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
1
8
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я не уверен в причине этой проблемы, но, похоже, она связана с использованием sessionFactory.getCurrentSession(), и она исчезнет, ​​если вы не реализуете ChatUserDao явно, а вместо этого опираетесь на автоматически сгенерированные реализации JPA. Для ChatUserDao объявите следующее


import org.springframework.data.jpa.repository.JpaRepository;

public interface ChatUserDao extends JpaRepository<ChatUser, Integer> {
}

Уберите ChatUserDaoImpl.

В ChatUserService замените вызовы пользовательских методов ChatUserDao стандартными методами JpaRepository:

@Service
public class ChatUserServiceImpl implements ChatUserService {

    @Autowired
    private ChatUserDao chatUserDao;

    @Override
    @Transactional
    public List<ChatUser> getChatUsers() {
        return chatUserDao.findAll();
    }

    @Override
    @Transactional
    public void saveChatUser(ChatUser theChatUser) {
        chatUserDao.save(theChatUser);
    }

    @Override
    @Transactional
    public ChatUser getChatUser(int theId) {
        return chatUserDao.getById(theId);
    }

    @Override
    @Transactional
    public void deleteChatUser(int theId) {
        chatUserDao.deleteById(theId);
    }

}

Добавление свойства spring.jpa.properties.hibernate.allow_update_outside_transaction=true, вероятно, не совсем то, что вам нужно, поскольку в конечном итоге вам могут понадобиться обновления транзакций.

Другие вопросы по теме