简体   繁体   中英

Javax @Valid @NotBlank not working properly

I have this model -

public class Product implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @NotBlank
    @Column(length = 500, nullable = false)
    private String name;

    @Column(nullable = false)
    private double buyingPrice;
    
    //getters and setters
}

These are the two controller methods responsible for edit/update product -

@Controller
@RequestMapping("/product")
@SessionAttributes("product")
public class ProductController {

    @GetMapping(value = "/edit")
    public String edit(@RequestParam("id") int id, ModelMap model) {
        Product product = productService.getById(id);
        model.put("product", product);

        return PRODUCT_EDIT_PAGE;
    }

    @PostMapping(value = "/edit")
    public String update(@Valid @ModelAttribute("product") Product product,
                         BindingResult result,
                         ModelMap model) {

        //I get `product.getName()` here
        
        if (result.hasErrors()) {

            //no matter what I set product name in the edit page
            //`BindingResult` always has this error - 
            //[Field error in object 'product' on field 'name': rejected value [null] ...];
            
            model.put("product", product);
            return PRODUCT_EDIT_PAGE;
        }

        productService.save(product);

        return "redirect:/product/list";
    }
}

This is the edit page -

<form:form method="POST" modelAttribute="product">
    <form:input type="text" path="name"/>
    <form:errors path="name"/>

    <form:input type="number" path="buyingPrice"/>
    <form:errors path="buyingPrice"/>

    <button type="submit">
</form:form>

No matter what I set product name in the edit page BindingResult always has this error -

[Field error in object 'product' on field 'name': rejected value [null]; codes [NotBlank.product.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [product.name,name]; arguments []; default message [name]]; default message [must not be blank]]

Even though I get product.getName() in the POST controller method. This weird behavior is occurring only when updating an existing product not when creating a new product.

Update 1:

I noticed while debugging that in the post method @ModelAttribute gets a hibernate proxy object of Product . A hibernate interceptor [ ByteBuddyInterceptor ] is intercepting the request. I guess this might be the issue.

这是调试屏幕截图

Update 2:

My service class -

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    public Product getById(int id) {
        return productRepository.getOne(id);
    }
}

My Repository -

public interface ProductRepository extends JpaRepository<Product, Integer> {
    List<Product> getAllByUser(User user);
}

I think you are looking up for the @NotBlank annotation, am I wrong?

Here is the difference:

  • @NotEmpty : a constrained CharSequence, Collection, Map, or Array is valid as long as it's not null and its size/length is greater than zero
  • @NotBlank : a constrained String is valid as long as it's not null and the trimmed length is greater than zero

This probably will not fix the error but I just wanted to inform you about the difference.

I suspect an issue with Hibernate, lazy proxies and storing the latter in the HttpSession .

Judging from the service method you call, productService.getById(id); , I suspect you are also calling getById (or getOne when using an older Spring Data version) which will return a lazy proxy from Hibernate. This proxy will only access the database when methods are called on it (like getName ).

As you are storing this proxy inside the HttpSession due to @SessionAttributes the original Hibernate session has already been closed (and it is a proxy). So the field will actually be empty (as the proxy doesn't really have the value, the enclosed object has).

What you should do is either use a separate DTO to overcome this issue or not use getById / getOne but rather findById to return a direct entity.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM