【问题标题】:Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction无法提交 JPA 事务;嵌套异常是 javax.persistence.RollbackException: Error while committing the transaction
【发布时间】:2019-02-26 20:35:05
【问题描述】:

我尝试在数据库中插入用户时出错。

我做了自定义注释,用于验证密码是否与确认密码匹配,当字段不匹配时它可以工作,但是当密码匹配时我有这个错误:

这是我的代码这是我的字段匹配@Annotation:

package mereuta.marian.tennis01.annotations;


import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = FieldsValueMatchValidator.class)
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldsValueMatch {

    String message() default "Fields values don't match!";

    String field();

    String fieldMatch();

    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

    @Target({ ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @interface List {
        FieldsValueMatch[] value();
    }
}

这是字段验证器:

package mereuta.marian.tennis01.annotations;

import mereuta.marian.tennis01.model.Utilisateur;
import org.springframework.beans.BeanWrapperImpl;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class FieldsValueMatchValidator implements ConstraintValidator<FieldsValueMatch , Object> {

    private String field;
    private String fieldMatch;


    @Override
    public void initialize(FieldsValueMatch fieldsValueMatch) {
        this.field=fieldsValueMatch.field();
        this.fieldMatch=fieldsValueMatch.fieldMatch();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {


        Object fieldValue = new BeanWrapperImpl(value)
                .getPropertyValue(field);
        Object fieldMatchValue = new BeanWrapperImpl(value)
                .getPropertyValue(fieldMatch);

        if (fieldValue != null) {
            return fieldValue.equals(fieldMatchValue);
        } else {
            return fieldMatchValue == null;
        }
    }
}

这是我的实体:

@FieldsValueMatch(field = "password", fieldMatch = "confirmPassword", 
message = "Password do not match!")
@Entity(name = "utilisateurs")
public class Utilisateur {
    @Id @GeneratedValue
    @Column(name = "id_utilisateur")
    private Integer id;
    @NotNull
    @Size(min = 4, max = 255)
    @Column(name = "password")
    private String password;
    @Transient
    @NotNull
    private String confirmPassword;

这是控制器:

@PostMapping("/addUtilisateur")
    public String addUtilisateur(@Valid @ModelAttribute("utilisateur") Utilisateur utilisateur, BindingResult bindingResult, Model model) {


        if (bindingResult.hasErrors() ) {
            model.addAttribute("message", "le mot de passe ne correspond pas");
            return "utilisateur/formRegister";
        }


        utilisateurMetier.creerUtilisateur(utilisateur);

        return "utilisateur/utilisateurAjoute";


    }

最后是视图:

<div class="container">
        <form id="contact" th:action="@{addUtilisateur}" method="post" th:object="${utilisateur}">
            <h3>Créer compte</h3>


                <input placeholder="password" type="password" th:field="*{password}" tabindex="2" required/>
                <span class="text text-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span>

            </fieldset>


            <fieldset>

                <input placeholder="password" type="password" th:field="*{confirmPassword}" tabindex="2" required/>
                <span class="text text-danger" th:if="${#fields.hasErrors('confirmPassword')}"
                      th:errors="*{confirmPassword}" th:text="${message}"></span>


            </fieldset>

对于自定义注释,我在以下位置找到了一个示例:https://www.baeldung.com/spring-mvc-custom-validator

@Override
    public void creerUtilisateur(Utilisateur utilisateur) {

        Role role;
        float credit = 0;
        boolean actif = true;

        role = roleRepository.getOne(3);


        System.out.println(role);

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();


        utilisateur.setPassword(encoder.encode(utilisateur.getPassword()));

        utilisateur.setRole(role);
        utilisateur.setCredit(credit);
        utilisateur.setActif(actif);


        utilisateurRepository.save(utilisateur);
    }

提前感谢您的帮助

【问题讨论】:

  • 请发布您的类 'utilisateurMetier' 的代码,或者至少是方法 'creerUtilisateur'。如果你查看你的堆栈跟踪,你会发现 ContraintViolationException 被抛出在 'creerUtilisateur' 方法中。
  • 我说的是最后一个
  • 希望我的回答解决了你的问题?!​​

标签: spring spring-mvc spring-boot thymeleaf


【解决方案1】:

如前所述,ContraintViolationException 在“creerUtilisateur”方法中引发。因此,当两个字段(密码、确认密码)具有相同的值时,在将 Utilisateur bean 传递给 Spring MVC 控制器方法 (addUtilisateur(@Valid @ModelAttribute("utilisateur")...) 时验证它可以正常工作。稍后,您对密码进行编码并更改 Utilitsateur 的“密码”实例变量的值:

utilisateur.setPassword(encoder.encode(utilisateur.getPassword()));

现在,'password' 和 'passwordConfirm' 不再相等了!在utilisateurRepository.save(utilisateur); 中持久化此实体时,JPA 将再次对您的实体进行 bean 验证,然后再将其保存到数据库(预持久化)。当 JPA/Hibernate 触发 pre-persist、pre-update 或 pre-remove 生命周期事件时,验证会自动执行。然后抛出 ContraintViolationException!

在您的creerUtilisateur 方法中,只需为实例变量“password”和“passwordConfirm”设置编码密码,并在此确保它们仍然通过FieldsValueMatchValidator.isValid(Object value, ConstraintValidatorContext context) 中的相等性检查:

 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
 final String encodedPassword = encoder.encode(utilisateur.getPassword());
 utilisateur.setPassword(encodedPassword);
 utilisateur.setPasswordConfirm(encodedPassword);
 //...
 utilisateurRepository.save(utilisateur);

您也可以尝试自定义 JPA 的 bean 验证行为:

https://www.thoughts-on-java.org/automatically-validate-entities-with-hibernate-validator/

Disable Hibernate validation for Merge/Update

【讨论】:

    猜你喜欢
    • 2020-05-11
    • 1970-01-01
    • 2021-08-26
    • 2021-09-02
    • 1970-01-01
    • 2016-08-30
    • 2013-01-09
    • 1970-01-01
    • 2016-07-08
    相关资源
    最近更新 更多