什么';这是“什么?”;“正确的道路”;发送数据更改websocket事件并确保在Spring Boot中提交数据库
注意:请阅读答案末尾,了解我实施@Nonika建议的方式 在数据插入时发送websocket事件的“正确方法”是什么 我使用的是带有SQL/JPA和非stomp WebSocket的Spring引导服务器。我需要使用“普通”WebSocket,因为我使用的Java客户端(AFAIK)不支持stomp 当我对数据库进行更改时,我需要将事件发送到客户端,因此我最终得到了如下实现:什么';这是“什么?”;“正确的道路”;发送数据更改websocket事件并确保在Spring Boot中提交数据库,spring,spring-boot,websocket,Spring,Spring Boot,Websocket,注意:请阅读答案末尾,了解我实施@Nonika建议的方式 在数据插入时发送websocket事件的“正确方法”是什么 我使用的是带有SQL/JPA和非stomp WebSocket的Spring引导服务器。我需要使用“普通”WebSocket,因为我使用的Java客户端(AFAIK)不支持stomp 当我对数据库进行更改时,我需要将事件发送到客户端,因此我最终得到了如下实现: @Transactional 公共无效加法(…){ 性能实时添加(); sendEntityAddedEvent(事件数
@Transactional
公共无效加法(…){
性能实时添加();
sendEntityAddedEvent(事件数据);
}
@交易的
public void sendentialaddedevent(字符串事件数据){
TransactionSynchronizationManager.registerSynchronization(新TransactionSynchronizationAdapter()){
@凌驾
public void afterCommit(){
sendEntityAddedEventAsync(事件数据);
}
});
}
@异步的
public void sendEntityAddedEventAsync(字符串事件数据){
//websocket会话是否正在发送。。。
}
这很有效。如果我只调用sendEntityAddedEventAsync
,它也适用于真实场景,但在单元测试中失败,因为事件将在事务提交之前到达。同样,当单元测试在事件失败后调用实体列表时
这感觉像是一个黑客,不应该在这里。有没有更好的方法来确保提交?
我尝试了多种替代方法,但问题是它们通常可以运行10次单元测试,但每隔一段时间就会失败一次。这是不能接受的
我尝试了多种方法来解决这个问题,例如不同的事务注释和拆分方法以适应它们。例如读取未提交、不支持(强制提交)等。所有情况下都不起作用,我找不到一个权威性的答案来回答这个(可能是常见的)用例,而不是STOMP(这是非常不同的)
编辑
我最初的一次尝试是这样的:
//这不应该在事务中
公共无效加法(…){
性能实时添加();
sendEntityAddedEvent(事件数据);
}
@交易的
公共无效性能实时添加(…){
//....
}
@异步的
public void sendEntityAddedEventAsync(字符串事件数据){
//websocket会话是否正在发送。。。
}
这里的假设是,当调用sendEntityAddedEventAsync
时,数据可能已经在数据库中。这不是为了几毫秒
其他一些细节:
- 测试环境基于h2(最初我错误地编写了hsql)
- 项目由JHipster生成
- 已使用二级缓存,但对这些实体禁用为无
公共类WebEvent扩展了ApplicationEvent{
私有ServerEventDAO事件;
公共WebEvent(对象源、ServerEventDAO事件){
超级(来源);
this.event=事件;
}
公共服务器eventdao getEvent(){
返回事件;
}
}
@交易的
公共无效加法(…){
性能实时添加();
applicationEventPublisher.publishEvent(新WebEvent(this,evtDao));
}
@异步的
@TransactionalEventListener
public void sendEntityAddedEventAsync(WebEventData){
//websocket会话是否正在发送。。。
}
这有效地保证了在发送事件之前正确提交数据,并异步运行以启动。非常好而且简单。Spring对
@Async
和@Transactional
都使用了AdviceMode.PROXY
,引自:
默认值为AdviceMode.PROXY。请注意,代理模式允许
仅用于通过代理拦截调用。市内电话
同一类不能通过这种方式被拦截;上的异步注释
由于Spring的
拦截器甚至不适用于这种运行时场景。暂时
更先进的拦截模式,考虑将此切换为
AdviceMode.ASPECTJ
此规则对于几乎所有需要代理操作的spring注释都是通用的
在第一个示例中,在addEntity(..)和performActualEntityAdding(..)上都有@Transactional注释。我想您可以从另一个类调用addEntity
,因此@Transactional可以正常工作。此场景中的流程可以在此流程中描述
// -> N1 transaction starts
addEntity(){
performActualEntityAdding()//-> we are still in transaction N1
sendEntityAddedEvent() // -> call to this @Async is a class local call, so this advice is ignored. But if this was an async call this would not work either.
}
//N1 transaction commits;
这就是测试失败的原因。它获取一个事件,表示数据库中有更改,但由于事务尚未提交,因此没有任何更改
情景2
当您没有@Transactional addEntity(..)
时,performacityadding
的第二个事务不会启动,因为也有本地调用
选项:
- 您可以使用一些中间件类来调用这些方法来触发 弹簧拦截器
- 你可以用
如果您使用的是Spring5.0,那么就有一个方便的
@TransactionalEventListener(phase=TransactionPhase.AFTER\u COMMIT)
try
和catch
检查数据是否已提交,如果未提交,则应抛出PersistenceException
的实现,如果您正在使用hibernate
,则可能在保存后实现侦听器。然后,基于您的问题是软件开发人员自己的问题,没有“正确的方法”。请求从客户端异步发生。如果没有官方认可的方法来实现这样的用例,我会感到惊讶。到目前为止,我知道没有官方认可的方法来实现这样的用例,使用STOMP