Spring Boot + WebSocket + Stomp + JPA:即使使用@Transactional注释也未插入数据

时间:2016-08-23 19:02:24

标签: hibernate jpa spring-boot stomp spring-websocket

我有一个RestController,它包含两个方法:

  1. 方法beat1,带有@MessageMapping注释,用于从客户端接收websocket消息(在STOMP协议中)
  2. 方法beat2,带有@RequestMapping注释以接收来自客户端的HTTP请求
  3. 这两个方法内部具有相同的代码:

    @RestController
    @RequestMapping("/api/member")
    @MessageMapping("member")
    public class MemberCtrl
    {
      @MessageMapping("beat")
      public Heartbeat beat1(@Payload Heartbeat heartbeat)
      {
        return memberService.beat(heartbeat);
      }
    
      @RequestMapping(value = "/beat", method = RequestMethod.POST)
      public Heartbeat beat2(@RequestBody Heartbeat heartbeat)
      {
        return memberService.beat(heartbeat);
      }
    }
    

    memberService只是一个普通的Spring服务,它包含将使用Spring JPARepository进行数据库数据插入的业务逻辑:

    @Service
    public class MemberServiceImpl implements MemberService, UserDetailsService
    {
    
        @Autowired
        private HeartbeatRepository heartbeatRepository;
    
        @Transactional
        @Override
        public Heartbeat beat(Heartbeat heartbeat)
        {
            heartbeat = heartbeatRepository.save(heartbeat);
            return heartbeat;
        }
    }
    

    至于我HeartbeatRepository的代码:

    @Repository
    public interface HeatbeatRepository extends JpaRepository<Heartbeat, Integer>
    {
    
    }
    

    这是我的问题:

    1. 当我发送带有websocket的STOMP消息时,控制器中的beat1方法会执行,但是没有数据插入到数据库表中。

    2. 当我发送http请求时,执行控制器中的beat2方法,并将Heartbeat域对象成功插入到数据库表中。

    3. 我正在实施websocket,所以我需要的是让beat1有效,我实际上根本不需要beat2。 (beat2仅用于测试)

      由于beat1beat2方法都在memberSevice中调用相同的服务方法,并且beat2有效,我认为我的MemberService和{的实现{1}}没有问题。

      作为每个HeartbeatRepositorybeat1的hibernate日志,

      1. beat2只打印SQL以获取下一个id,我没有看到SQL用于插入和事务提交:

        baat1
      2. 2. 2016-08-23 18:32:56.227 DEBUG 43874 --- [nboundChannel-4] org.hibernate.SQL : select nextval ('heartbeat_id_seq') 2016-08-23 18:32:56.227 DEBUG 43874 --- [nboundChannel-4] o.h.e.j.internal.LogicalConnectionImpl : Obtaining JDBC connection 2016-08-23 18:32:56.242 DEBUG 43874 --- [nboundChannel-4] o.h.e.j.internal.LogicalConnectionImpl : Obtained JDBC connection 2016-08-23 18:32:56.261 DEBUG 43874 --- [nboundChannel-4] org.hibernate.id.SequenceGenerator : Sequence identifier generated: BasicHolder[java.lang.Integer[20]] 2016-08-23 18:32:56.261 DEBUG 43874 --- [nboundChannel-4] o.h.e.i.AbstractSaveEventListener : Generated identifier: 20, using strategy: org.hibernate.id.SequenceHiLoGenerator 2016-08-23 18:32:56.266 DEBUG 43874 --- [nboundChannel-4] o.h.e.j.internal.LogicalConnectionImpl : Releasing JDBC connection 2016-08-23 18:32:56.266 DEBUG 43874 --- [nboundChannel-4] o.h.e.j.internal.LogicalConnectionImpl : Released JDBC connection 打印完成的insetaion SQL并提交事务,这就是它成功插入数据库表的原因:

        beat2

        那么我的代码在2016-08-23 18:20:29.937 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.t.spi.AbstractTransactionImpl : begin 2016-08-23 18:20:29.937 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.j.internal.LogicalConnectionImpl : Obtaining JDBC connection 2016-08-23 18:20:29.951 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.j.internal.LogicalConnectionImpl : Obtained JDBC connection 2016-08-23 18:20:29.951 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.t.internal.jdbc.JdbcTransaction : initial autocommit status: true 2016-08-23 18:20:29.951 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.t.internal.jdbc.JdbcTransaction : disabling autocommit 2016-08-23 18:20:29.966 DEBUG 43846 --- [nio-8989-exec-9] org.hibernate.SQL : select nextval ('heartbeat_id_seq') 2016-08-23 18:20:29.986 DEBUG 43846 --- [nio-8989-exec-9] org.hibernate.id.SequenceGenerator : Sequence identifier generated: BasicHolder[java.lang.Integer[19]] 2016-08-23 18:20:29.986 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.i.AbstractSaveEventListener : Generated identifier: 19, using strategy: org.hibernate.id.SequenceHiLoGenerator 2016-08-23 18:20:29.992 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.t.spi.AbstractTransactionImpl : committing 2016-08-23 18:20:29.993 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.i.AbstractFlushingEventListener : Processing flush-time cascades 2016-08-23 18:20:29.993 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.i.AbstractFlushingEventListener : Dirty checking collections 2016-08-23 18:20:29.995 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.i.AbstractFlushingEventListener : Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects 2016-08-23 18:20:29.995 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections 2016-08-23 18:20:29.996 DEBUG 43846 --- [nio-8989-exec-9] o.hibernate.internal.util.EntityPrinter : Listing entities: 2016-08-23 18:20:29.998 DEBUG 43846 --- [nio-8989-exec-9] o.hibernate.internal.util.EntityPrinter : com.yamk.api.domain.orm.Heartbeat{lifespanSec=0, lostedSec=0, lastHeartbeatTime=Tue Aug 23 18:19:35 UTC 2016, createdTime=Tue Aug 23 18:20:29 UTC 2016, location=POINT (121.56818389892578 25.033194472364688), id=19} 2016-08-23 18:20:30.006 DEBUG 43846 --- [nio-8989-exec-9] org.hibernate.SQL : insert into heartbeat (created_time, last_heartbeat_time, lifespan_sec, location, losted_sec, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into heartbeat (created_time, last_heartbeat_time, lifespan_sec, location, losted_sec, id) values (?, ?, ?, ?, ?, ?) 2016-08-23 18:20:30.088 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.t.internal.jdbc.JdbcTransaction : committed JDBC Connection 2016-08-23 18:20:30.088 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.t.internal.jdbc.JdbcTransaction : re-enabling autocommit 2016-08-23 18:20:30.091 DEBUG 43846 --- [nio-8989-exec-9] m.m.a.RequestResponseBodyMethodProcessor : Written [com.yamk.api.domain.orm.Heartbeat@32] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@69f8302c] 2016-08-23 18:20:30.091 DEBUG 43846 --- [nio-8989-exec-9] o.s.web.servlet.DispatcherServlet : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling 2016-08-23 18:20:30.091 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.jdbc.internal.JdbcCoordinatorImpl : HHH000420: Closing un-released batch 2016-08-23 18:20:30.091 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.j.internal.LogicalConnectionImpl : Releasing JDBC connection 2016-08-23 18:20:30.091 DEBUG 43846 --- [nio-8989-exec-9] o.h.e.j.internal.LogicalConnectionImpl : Released JDBC connection (WebSocket)和beat1(HTTP请求)上导致这种不同结果的问题是什么?

        即使在调用MessageMapping方法beat2时使用@Transactional注释并且很奇怪,Spring似乎也不会自动给我一个事务。

1 个答案:

答案 0 :(得分:2)

最后我自己找到了解决方案。 事实证明,我必须使用PlatformTransactionManager手动配置@Bean并使用JpaTransactionManager作为实现:

    @ComponentScan
    @Configuration
    @EnableSwagger2
    @EnableCaching
    @EnableJpaRepositories
    @EnableTransactionManagement
    @EnableJpaAuditing
    @SpringBootApplication
    public class App
    {
      public static void main(String[] args)
      {
          SpringApplication.run(App.class, args);
      }

      @Autowired
      private EntityManagerFactory entityManagerFactory;

      @Bean
      public PlatformTransactionManager transactionManager() 
      {
        return new JpaTransactionManager(entityManagerFactory);
      }

    }

这就是全部,然后一切正常。

但我仍然不明白为什么Spring Boot没有使用我的applicaion.properites文件自动配置PlatformTransactionManager。 也许我在application.properites中误解了某些内容或遗漏了某些内容,这就是我applicaion.properties的样子:

    # data source
    spring.datasource.driver-class-name=org.postgresql.Driver
    spring.datasource.url=jdbc:postgresql://xxx.xxx.xxx.xxxx:####/xxxx
    spring.datasource.username=my-user-name
    spring.datasource.password=my-password
    spring.datasource.validationQuery=SELECT 1
    spring.datasource.test-on-borrow=true

    # jpa / hibernate
    spring.jpa.hibernate.ddl-auto:validate
    spring.jpa.show-sql:true
    spring.jpa.properties.hibernate.dialect = org.hibernate.spatial.dialect.postgis.PostgisDialect
    spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
    logging.level.org.hibernate=DEBUG