Spring Controller并发处理相同颜色请求时重复创建数据库记录的问题求助
Spring Controller并发处理相同颜色请求时重复创建数据库记录的问题求助
大家好,我遇到了一个Spring并发场景下的数据库重复创建问题,想请各位帮忙分析解决:
我做了一个简单的POST接口,用来接收Car的JSON数据,核心逻辑是:如果请求里的颜色在数据库中已经存在,就直接复用这条颜色记录;如果不存在,就新建一条颜色记录。
控制器代码
@RequestMapping(method = RequestMethod.POST, consumes = { "application/json" }) public Car digest(@RequestBody CarFormat body){ return CarsService.digest(body.document, body.car); }
服务层核心方法
在CarsService的digest方法里,我调用了findColorOrCreate方法来处理颜色的查找或创建逻辑:
private Car findColorOrCreate(CarFormat car) { String colorReference = car.color.value; Assert.notNull(colorReference, String.format("Color reference not found from: %s.", car)); if (colorService.existsByReference(colorReference)) { return colorService.findByReference(colorReference); } return colorService.create(extractColor(car, colorReference)); }
逻辑很直白:先检查颜色是否存在,存在就返回已有记录,不存在就调用create方法新建。
并发测试代码
为了验证并发场景下的行为,我写了一个集成测试,模拟3个几乎同时发送的请求,都用同一个颜色标识"red":
@Test public void should_createOneColorRecord_whenSimultaneousCalls_Endpoint() throws InterruptedException { String sameColorReference = "red"; String json1 = "...car_id: 1, color:"+sameColorReference+"..."; String json2 = "...car_id: 2, color:"+sameColorReference+"..."; String json3 = "...car_id: 3, color:"+sameColorReference+"..."; String mimeType = "application/json"; CountDownLatch latch = new CountDownLatch(3); executeAsyncRequest("/api/", mimeType, json1, latch); executeAsyncRequest("/api/", mimeType, json2, latch); executeAsyncRequest("/api/", mimeType, json3, latch); latch.await(); assertThat(entityManager.createQuery("SELECT count(c) FROM Color c WHERE c.reference like '"+sameColorReference+"'").getSingleResult(), equalTo(Long.valueOf(1))); }
其中executeAsyncRequest方法用不同线程发送请求,模拟真实并发:
private void executeAsyncRequest(String endpoint, String mimeType, String json, CountDownLatch latch) { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(endpoint) .contentType(mimeType) .content(json); new Thread(() -> { try { this.mockMvc.perform(builder) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); latch.countDown(); } catch (Exception e) { throw new RuntimeException(e); } }).start(); }
遇到的问题
测试的预期结果是数据库中只创建1条"red"颜色记录,但实际运行后却创建了3条。我尝试过在控制器、服务层甚至接口上添加@Transactional注解,也试过@Transactional(propagation = Propagation.REQUIRES_NEW),但都没能解决重复创建的问题。
有没有朋友能帮我分析一下问题出在哪,以及该怎么解决呢?非常感谢!
备注:内容来源于stack exchange,提问作者NicoESIEA
相关产品推荐
相关产品推荐

