java - 为什么之前的sql语句又被执行了?
问题描述
我对 Spring、Hibernate、JPA 和这部分发展中国家的许多其他相关方面相当陌生。我希望这是一个简单回答的愚蠢问题,因为我已经浪费了几个小时来尝试调试我的问题。
简而言之,问题在于 Hibernate(JPA?Spring?)似乎想要执行它之前已经执行过的 sql 语句,尽管我调用了 saveAndFlush。对于插入,这会导致唯一索引/主键违规。
在控制器中,我有这个功能,它需要添加或删除用户和 ProcessDefinition 之间的关系:
@Autowired
ProcessDefinitionRepository processDefinitionRepository;
@Autowired
UserRepository userRepository;
@PostMapping({"/toggleFavorite"})
@ResponseBody
public int toggleFavorite(@RequestBody ObjectNode jsonData){
// Get the data
int processID = jsonData.findValue("processID").asInt();
// Find the process definition
ProcessDefinition toggledProcessDefinition = processDefinitionRepository
.findById(processID)
.get();
// Get the current user
User currentUser = CurrentUser.get();
// Either remove or...
int result;
if(toggledProcessDefinition.isFavoriteOfCurrentUser()){
currentUser.removeFavoriteProcessDefinition(toggledProcessDefinition);
result= 0;
}
// ...add to favorites
else{
currentUser.addFavoriteProcessDefinition(toggledProcessDefinition);
result= 1;
}
// Send to database
userRepository.saveAndFlush(currentUser);
return result;
}
用户通过以下方式引用 ProcessDefinition:
@Id
private String userId="";
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_process_definition",
joinColumns= @JoinColumn(name="user_id"),
inverseJoinColumns = @JoinColumn(name="process_definition_id")
)
Set<ProcessDefinition> favoriteProcessDefinitions = new HashSet<>();
public void removeFavoriteProcessDefinition(ProcessDefinition processDefinition){
favoriteProcessDefinitions.remove(processDefinition);
}
public void addFavoriteProcessDefinition(ProcessDefinition processDefinition){
favoriteProcessDefinitions.add(processDefinition);
}
ProcessDefinition 指的是用户:
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@ManyToMany(mappedBy = "favoriteProcessDefinitions")
private Set<User> favoritingUsers = new HashSet<>();
public boolean isFavoriteOfCurrentUser(){
return CurrentUser.get().getFavoriteProcessDefinitions().contains(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProcessDefinition that = (ProcessDefinition) o;
return id == that.id;
}
@Override
public int hashCode() {
return id;
}
现在,当我导航到相关页面并单击按钮删除收藏夹时,代码按预期执行(使用调试器验证)并且 Hibernate 启动删除查询:
Hibernate:
delete
from
user_process_definition
where
user_id=?
and process_definition_id=?
返回一个结果,Ajax 调用完成。我单击另一个按钮以删除不同的 ProcessDefinition。Hibernate 再次执行上述删除查询。然后我单击按钮添加收藏夹(又一个 ProcessDefinition,它的 ID 为 3),我看到发生了这种情况:
Hibernate:
delete
from
user_process_definition
where
user_id=?
and process_definition_id=?
Hibernate:
delete
from
user_process_definition
where
user_id=?
and process_definition_id=?
Hibernate:
insert
into
user_process_definition
(user_id, process_definition_id)
values
(?, ?)
为什么又要删除了?
似乎有什么东西在保留前面的语句并再次执行它们。当我尝试添加另一个收藏夹 (ID=2) 时发生的事情进一步支持了这一点。因为现在发生的...
Hibernate:
delete
from
user_process_definition
where
user_id=?
and process_definition_id=?
Hibernate:
insert
into
user_process_definition
(user_id, process_definition_id)
values
(?, ?)
2019-08-21 16:29:08.254 WARN 8528 --- [nio-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 23505, SQLState: 23505
2019-08-21 16:29:08.254 ERROR 8528 --- [nio-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_6 ON PUBLIC.USER_PROCESS_DEFINITION(USER_ID, PROCESS_DEFINITION_ID) VALUES 3"; SQL statement:
insert into user_process_definition (user_id, process_definition_id) values (?, ?) [23505-199]
2019-08-21 16:29:08.257 INFO 8528 --- [nio-8080-exec-7] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2019-08-21 16:29:08.278 ERROR 8528 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PUBLIC.PRIMARY_KEY_6 ON PUBLIC.USER_PROCESS_DEFINITION(USER_ID, PROCESS_DEFINITION_ID) VALUES 3"; SQL statement:
insert into user_process_definition (user_id, process_definition_id) values (?, ?) [23505-199]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_6 ON PUBLIC.USER_PROCESS_DEFINITION(USER_ID, PROCESS_DEFINITION_ID) VALUES 3"; SQL statement:
insert into user_process_definition (user_id, process_definition_id) values (?, ?) [23505-199]
再次删除,但更糟糕的是,之前的插入(ID=3)再次执行!当然,这会遇到唯一索引违规。但是为什么它要再次运行前面的语句呢?
请注意,当我执行删除后(在不同的 Ajax 调用中!)重新添加收藏夹时,甚至不会将插入发送到数据库。
请注意,当我进行添加时,(在不同的 Ajax 调用中)后跟不同的添加,我总是得到一个“唯一索引或主键冲突”,其值是第一个 ProcessDefinition 的 ID。
我应该关闭还是提交某些东西,saveAndFlush 还不够吗?发生了什么事,我该如何解决?
解决方案
将以下内容添加到 application.properties:
spring.jpa.open-in-view = false
并使用save
而不是saveAndFlush
我不确定它是否会立即解决您的问题,但我相信您肯定会更接近解决方案。
另一个想法:
if(toggledProcessDefinition.getFavoriteProcessDefinitions().contains(currentUser){
toggledProcessDefinition.getFavoriteProcessDefinitions().remove(currentUser);
result= 0;
}
// ...add to favorites
else{
toggledProcessDefinition.getFavoriteProcessDefinitions().add(currentUser);
result= 1;
}
processDefinitionRepository.save(toggledProcessDefinition);
这样您就不会从两种方式加载引用。
@Transactional
如果您关闭 open-in-view,您还需要添加到您的控制器方法。
推荐阅读
- c - 为什么我在激活信标之前仍然获得 BLE RSSI 值?
- python - 处理混合数据类型
- jquery - 引导工具提示不应隐藏或消失在专注于工具提示内容上
- docker - 如何在 Docker 中使用 Chromium 运行 Canopy
- python - 检查数据库中的句子是否包含字典中的某些单词
- javascript - 在 Google Script Web 应用程序上向用户确认工作表更新
- python - 在 MAC 中创建一个仅对系统/管理员具有写入权限的文件
- python-3.x - 计算给定代码的while循环时间复杂度
- typescript - 为 Node 重用已加载的块
- c++ - #define 在 qml 代码中