在Spring Data中进行验证会抛出ConstraintViolationException异常。

huangapple go评论71阅读模式
英文:

Validation in Spring Data throws ConstraintViolationException

问题

我试图基于Spring Data为我的服务实现验证。在线文档表示,我只需要使用javax.validation的注解,一切都应该正常工作。

我所指的正常工作是,如果验证失败,它会抛出一个MethodArgumentNotValidException异常,可以捕获并处理(这应该是Spring自动完成的)。

然而,在我的项目中,我无法实现这一点,每当我实现验证时,会抛出一个位于TransactionSystemException内部的ConstraintViolationException异常。

以下是一个最小示例:

实体:

@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Foo {
  @Id
  UUID id = UUID.randomUUID();

  @NotBlank
  String name;
}

仓库:

public interface FooRepository extends PagingAndSortingRepository<Foo, UUID> {

}

控制器:

@RepositoryRestController
public class FooController {

  @Autowired
  FooRepository fooRepository;

  @PostMapping(value = "/foos", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE})
  public @ResponseBody ResponseEntity<?> postFoo(HttpServletRequest request, @Valid @RequestBody Foo foo,
      PersistentEntityResourceAssembler resourceAssembler) {
    Foo savedFoo = fooRepository.save(foo);

    return new ResponseEntity<>(resourceAssembler.toFullResource(savedFoo), HttpStatus.CREATED);
  }
}

以及运行代码的测试代码:

@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@AutoConfigureDataJpa
class FooTest {
  @Autowired
  MockMvc mockMvc;

  @Test
  void testPost() throws Exception {
    Foo foo = new Foo();
    assertNull(foo.getName());

    mockMvc.perform(
        post("/foos")
            .contentType(MediaType.APPLICATION_JSON)
            .content(new ObjectMapper().writeValueAsString(foo)))
        .andDo(print())
        .andExpect(status().isBadRequest());
  }
}

抛出的异常如下:

Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [Foo] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
	ConstraintViolationImpl{interpolatedMessage='darf nicht leer sein', propertyPath=name, rootBeanClass=class Foo, messageTemplate='{javax.validation.constraints.NotBlank.message}'}
]
	at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140)
	at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80)
	at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:227)
	at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:100)
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
	at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478)
英文:

I'm trying to implement a validation for my service based on Spring Data. The documentation online says, thet i would only need the annotations of javax.validation and everything should work fine.
By fine I mean that if the validation failes it throws a MethodArgumentNotValidException that can be catched and handled (which should spring do by magic).

However I'm not able to achieve this on my project, whenever I implement the validation a ConstraintViolationException inside a TransactionSystemException is thrown instead.

Here is a minimal example:

Entity:

@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Foo {
  @Id
  UUID id = UUID.randomUUID();

  @NotBlank
  String name;
}

Repository:

public interface FooRepository extends PagingAndSortingRepository&lt;Foo, UUID&gt; {

}

Controller:

@RepositoryRestController
public class FooController {

  @Autowired
  FooRepository fooRepository;

  @PostMapping(value = &quot;/foos&quot;, consumes = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE})
  public @ResponseBody ResponseEntity&lt;?&gt; postFoo(HttpServletRequest request, @Valid @RequestBody Foo foo,
      PersistentEntityResourceAssembler resourceAssembler) {
    Foo savedFoo = fooRepository.save(foo);

    return new ResponseEntity&lt;&gt;(resourceAssembler.toFullResource(savedFoo), HttpStatus.CREATED);
  }
}

And the test code with which I run the code:

@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@AutoConfigureDataJpa
class FooTest {
  @Autowired
  MockMvc mockMvc;

  @Test
  void testPost() throws Exception {
    Foo foo = new Foo();
    assertNull(foo.getName());

    mockMvc.perform(
        post(&quot;/foos&quot;)
            .contentType(MediaType.APPLICATION_JSON)
            .content(new ObjectMapper().writeValueAsString(foo)))
        .andDo(print())
        .andExpect(status().isBadRequest());
  }
}

The Exception thrown is the following:

Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [Foo] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
	ConstraintViolationImpl{interpolatedMessage=&#39;darf nicht leer sein&#39;, propertyPath=name, rootBeanClass=class Foo, messageTemplate=&#39;{javax.validation.constraints.NotBlank.message}&#39;}
]
	at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140)
	at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80)
	at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:227)
	at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:100)
	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
	at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478)

答案1

得分: 0

ConstraintViolationException与验证无关。这是因为每次调用testPost()方法时都保存了相同的foo对象。每次保存foo时,请使用不同的id。id是主键,如果保存多个具有相同id或主键的对象,将会触发ConstraintViolationException。

英文:

ConstraintViolationException is not related to validation. It is because you are saving same foo object everytime you calling test method testPost(). Everytime you are saving foo use different id. Id is the primary key and if you save more than one object with same id or primary key you will get ConstraintViolationException.

答案2

得分: 0

我现在已经找到了一个解决方案,但我还不太理解它。我将进一步调查这个问题,并在这里分享我的结果,如果有人可以向我解释,请随意评论。

我通过使用@RestController而不是@RepositoryRestController对我的控制器进行了注释来解决了这个问题。

https://jira.spring.io/browse/DATAREST-593

这个解决方案对我有用:https://stackoverflow.com/a/44304198/4567795

英文:

I have now found a solution, but I do not really understand it yet. I will investigate this further and share my results here, if anyone can explain it to me, feel free to comment.

I solved it by annotating my Controller with @RestController instead of @RepositoryRestController.

https://jira.spring.io/browse/DATAREST-593

This soultion works for me: https://stackoverflow.com/a/44304198/4567795

huangapple
  • 本文由 发表于 2020年10月7日 15:17:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/64239087.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定