Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
什么';这是“什么?”;“正确的道路”;发送数据更改websocket事件并确保在Spring Boot中提交数据库_Spring_Spring Boot_Websocket - Fatal编程技术网

什么';这是“什么?”;“正确的道路”;发送数据更改websocket事件并确保在Spring Boot中提交数据库

什么';这是“什么?”;“正确的道路”;发送数据更改websocket事件并确保在Spring Boot中提交数据库,spring,spring-boot,websocket,Spring,Spring Boot,Websocket,注意:请阅读答案末尾,了解我实施@Nonika建议的方式 在数据插入时发送websocket事件的“正确方法”是什么 我使用的是带有SQL/JPA和非stomp WebSocket的Spring引导服务器。我需要使用“普通”WebSocket,因为我使用的Java客户端(AFAIK)不支持stomp 当我对数据库进行更改时,我需要将事件发送到客户端,因此我最终得到了如下实现: @Transactional 公共无效加法(…){ 性能实时添加(); sendEntityAddedEvent(事件数

注意:请阅读答案末尾,了解我实施@Nonika建议的方式

在数据插入时发送websocket事件的“正确方法”是什么

我使用的是带有SQL/JPA和非stomp WebSocket的Spring引导服务器。我需要使用“普通”WebSocket,因为我使用的Java客户端(AFAIK)不支持stomp

当我对数据库进行更改时,我需要将事件发送到客户端,因此我最终得到了如下实现:

@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生成
  • 已使用二级缓存,但对这些实体禁用为无
解决方案(基于@Nonika的回答):

我的解决方案包括类似于以下内容:

公共类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