Я получаю эту ошибку, когда пытаюсь сохранить объект 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&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 и сохранил его только на контроллере, но он по-прежнему выдает ту же ошибку.
Есть ли у вас какие-либо классы конфигурации, настраивающие TransactionManager?
Да, у меня есть. Отредактировал основной вопрос, добавив еще 2 файла для справки. Но я думаю, что это не работает, мне все равно пришлось добавить свойства базы данных в файл application.properties, иначе выдавалась ошибка «URL-адрес источника данных отсутствует»
Привет, Полли. Я могу устранить эту ошибку, добавив это свойство в application.properties Spring.jpa.properties.hibernate.allow_update_outside_transaction=true. Но знаете ли вы, почему мой файл web.xml не работает с файлом сервлета Spring- mvc-crud-demo-servlet.xml, потому что мне еще пришлось редактировать application.properties.
Я попытался воспроизвести приложение весенней загрузки с вашими XML-файлами и заставил их прочитать файлы приложения, добавив @ImportResource({"classpath:/WEB-INF/spring-mvc-crud-demo-servlet.xml"} ) в качестве аннотации к @SpringBootApplication, но без особого успеха, так как он вышел из строя из-за дублирования объявлений bean-компонентов, поэтому моя единственная теория заключается в том, что ваш файл сервлета вообще не читается, и вы используете значения по умолчанию, которые не включают настройку менеджера транзакций. . Возможно, опубликуйте свой класс приложения и свои application.properties.
Добавлены новые файлы
Я воспроизвел эту ошибку, поэтому спасибо за эти файлы! Поскольку все, что касается вашего источника данных, определено в вашем файле application.properties, я не уверен, что ваши файлы конфигурации xml вообще используются из-за отсутствия каких-либо аннотаций @ImportResource. Я думаю, что проблема в том, что sessionFactory.getCurrentSession() каким-то образом возвращает сеанс, находящийся за пределами текущего контекста транзакции, хотя в документации говорится, что этого не должно быть.






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