java - 如何查找 BindingResult 中的错误原因

标签 java spring hibernate spring-mvc

在 eclipse 和 tomcat 服务器中使用 hibernate 的 spring mvc Web 应用程序中,我更改了 jsp 中的几个文本字段以下拉列表,以便可以从自己的下拉菜单中选择一个人的性别和种族。我小心翼翼地更改了应用程序的其他级别,包括在底层数据库中设置性别和种族的连接表,以及更改模型和存储库级别中的代码。应用程序编译,并且 jsp 加载了每个下拉列表中所选人员的正确选定值,但单击提交/更新按钮会导致 BindingResult.hasErrors() 问题,这无法帮助我定位问题的原因。

有人可以帮我找出无法处理更新的原因吗?

这是在 Controller 类中调用的 processUpdatePatientForm() 方法。请注意,它会触发 System.out.println(),显示 BindingResult.hasErrors() 并返回 jsp:

@RequestMapping(value = "/patients/{patientId}/edit", method = RequestMethod.PUT)
public String processUpdatePatientForm(@Valid Patient patient, BindingResult result, SessionStatus status) {
    if (result.hasErrors()) {
        System.out.println(":::::::::::::::: in PatientController.processUpdatePatientForm() result.hasErrors() ");
        List<ObjectError> errors = result.getAllErrors();
        for(int i=0;i<result.getErrorCount();i++){System.out.println("]]]]]]] error "+i+" is: "+errors.get(i).toString());}
        return "patients/createOrUpdatePatientForm";} 
    else {
        this.clinicService.savePatient(patient);
        status.setComplete();
        return "redirect:/patients?patientID=" + patient.getId();
    }
}

返回jsp时,包含以下错误信息:

//This is printed out in my jsp below the Sex drop down list:  
Failed to convert property value of type java.lang.String to required type org.springframework.samples.knowledgemanager.model.Gender for property sex; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [org.springframework.samples.knowledgemanager.model.Gender] for property sex: no matching editors or conversion strategy found

//This is printed out in my jsp below the Race drop down list:  
Failed to convert property value of type java.lang.String to required type org.springframework.samples.knowledgemanager.model.Race for property race; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [org.springframework.samples.knowledgemanager.model.Race] for property race: no matching editors or conversion strategy found

以下是 eclipse 控制台中打印的全部内容:

Hibernate: select gender0_.id as id1_2_, gender0_.name as name2_2_ from gender gender0_ order by gender0_.name
Hibernate: select race0_.id as id1_7_, race0_.name as name2_7_ from race race0_ order by race0_.name
:::::::::::::::: in PatientController.processUpdatePatientForm() result.hasErrors() 
]]]]]]] error 0 is: Field error in object 'patient' on field 'race': rejected value [Hispanic]; codes [typeMismatch.patient.race,typeMismatch.race,typeMismatch.org.springframework.samples.knowledgemanager.model.Race,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [patient.race,race]; arguments []; default message [race]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.springframework.samples.knowledgemanager.model.Race' for property 'race'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [org.springframework.samples.knowledgemanager.model.Race] for property 'race': no matching editors or conversion strategy found]
]]]]]]] error 1 is: Field error in object 'patient' on field 'sex': rejected value [Male]; codes [typeMismatch.patient.sex,typeMismatch.sex,typeMismatch.org.springframework.samples.knowledgemanager.model.Gender,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [patient.sex,sex]; arguments []; default message [sex]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'org.springframework.samples.knowledgemanager.model.Gender' for property 'sex'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [org.springframework.samples.knowledgemanager.model.Gender] for property 'sex': no matching editors or conversion strategy found]

请注意,错误消息中显示的值 [西类牙] 和 [男性] 会触发错误。问题可能是 Gender 和 Race 的 name 属性被传递给 Spring MVC,而应该传递 id 属性。但如何在代码中解决这个问题?

有人可以帮我弄清楚这个问题的真相吗?第一步是如何获得更有用的错误消息,该消息可以找到代码中触发问题的位置。

<小时/>

编辑:

根据 Sotirios 的请求,以下是我在 jsp 中的表单:

<form:form modelAttribute="patient" method="${method}" class="form-horizontal" id="add-patient-form">
    <petclinic:inputField label="First Name" name="firstName"/>
    <petclinic:inputField label="Middle Initial" name="middleInitial"/>
    <petclinic:inputField label="Last Name" name="lastName"/>

    <div class="control-group">
        <petclinic:selectField label="Sex" name="sex" names="${genders}" size="5"/>
    </div>

    <petclinic:inputField label="Date of Birth" name="dateOfBirth"/>

    <div class="control-group">
        <petclinic:selectField label="Race" name="race" names="${races}" size="5"/>
    </div>

    <div class="form-actions">
        <c:choose>
            <c:when test="${patient['new']}">
                <button type="submit">Add Patient</button>
            </c:when>
            <c:otherwise>
                <button type="submit">Update Patient</button>
            </c:otherwise>
        </c:choose>
    </div>
</form:form>

Patient.java 类是:

@Entity
@Table(name = "patients")
public class Patient extends BaseEntity {

@OneToMany(cascade = CascadeType.ALL, mappedBy = "patient",  fetch=FetchType.EAGER)
private Set<Document> documents;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "patient",  fetch=FetchType.EAGER)
private Set<Address> addresses;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "patient",  fetch=FetchType.EAGER)
private Set<PhoneNumber> phonenumbers;

@Column(name = "first_name")
@NotEmpty
protected String firstName;

@Column(name = "middle_initial")
protected String middleInitial;

@Column(name = "last_name")
@NotEmpty
protected String lastName;

@ManyToOne
@JoinColumn(name = "sex_id")
protected Gender sex;

@Column(name = "date_of_birth")
@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
@DateTimeFormat(pattern = "yyyy/MM/dd")
protected DateTime dateOfBirth;

@ManyToOne
@JoinColumn(name = "race_id")
protected Race race;

////////////// Document methods
protected void setDocumentsInternal(Set<Document> documents) {this.documents = documents;}

public Set<Document> getFaxes() {
    Set<Document> faxes = new HashSet<Document>();
    for (Document doc : getDocumentsInternal()) {if (doc.getType().getName().equals("ScannedFaxes")) {faxes.add(doc);}}
    return faxes;
}
public Set<Document> getForms() {
    Set<Document> forms = new HashSet<Document>();
    for (Document doc : getDocumentsInternal()) {if (doc.getType().getName().equals("ScannedPatientForms")) {forms.add(doc);}}
    return forms;
}

protected Set<Document> getDocumentsInternal() {
    if (this.documents == null) {this.documents = new HashSet<Document>();}
    return this.documents;
}

public List<Document> getDocuments() {
    List<Document> sortedDocuments = new ArrayList<Document>(getDocumentsInternal());
    PropertyComparator.sort(sortedDocuments, new MutableSortDefinition("name", true, true));
    return Collections.unmodifiableList(sortedDocuments);
}

public void addDocument(Document doc) {
    getDocumentsInternal().add(doc);
    doc.setPatient(this);
}

public Document getDocument(String name) {return getDocument(name, false);}

/** Return the Document with the given name, or null if none found for this Patient.
 * @param name to test
 * @return true if document name is already in use
 */
public Document getDocument(String name, boolean ignoreNew) {
    name = name.toLowerCase();
    for (Document doc : getDocumentsInternal()) {
        if (!ignoreNew || !doc.isNew()) {
            String compName = doc.getName();
            compName = compName.toLowerCase();
            if (compName.equals(name)) {
                return doc;
            }
        }
    }
    return null;
}
//////////// Address methods
protected void setAddressesInternal(Set<Address> addresses) {this.addresses = addresses;}

protected Set<Address> getAddressesInternal() {
    if (this.addresses == null) {this.addresses = new HashSet<Address>();}
    return this.addresses;
}

public List<Address> getAddresses() {
    List<Address> sortedAddresses = new ArrayList<Address>(getAddressesInternal());
    PropertyComparator.sort(sortedAddresses, new MutableSortDefinition("address", true, true));
    return Collections.unmodifiableList(sortedAddresses);
}

public void addAddress(Address addr) {
    getAddressesInternal().add(addr);
    addr.setPatient(this);
}

public Address getAddress(String address) {return getAddress(address, false);}

/** Return the Address with the given name, or null if none found for this Patient.
 * @param name to test
 * @return true if document name is already in use
 */
public Address getAddress(String addr, boolean ignoreNew) {
    addr = addr.toLowerCase();
    for (Address address1 : getAddressesInternal()) {
        if (!ignoreNew || !address1.isNew()) {
            String compName = address1.getAddress();
            compName = compName.toLowerCase();
            if (compName.equals(addr)) {
                return address1;
            }
        }
    }
    return null;
}
//////////// PhoneNumber methods
protected void setPhoneNumbersInternal(Set<PhoneNumber> phonenumbers) {this.phonenumbers = phonenumbers;}

protected Set<PhoneNumber> getPhoneNumbersInternal() {
    if (this.phonenumbers == null) {this.phonenumbers = new HashSet<PhoneNumber>();}
    return this.phonenumbers;
}

public List<PhoneNumber> getPhoneNumbers() {
    List<PhoneNumber> sortedPhoneNumbers = new ArrayList<PhoneNumber>(getPhoneNumbersInternal());
    PropertyComparator.sort(sortedPhoneNumbers, new MutableSortDefinition("phonenumber", true, true));
    return Collections.unmodifiableList(sortedPhoneNumbers);
}

public void addPhoneNumber(PhoneNumber pn) {
    getPhoneNumbersInternal().add(pn);
    pn.setPatient(this);
}

public PhoneNumber getPhoneNumber(String pn) {return getPhoneNumber(pn, false);}

/** Return the PhoneNumber with the given name, or null if none found for this Patient.
 * @param name to test
 * @return true if phone number is already in use
 */
public PhoneNumber getPhoneNumber(String pn, boolean ignoreNew) {
    pn = pn.toLowerCase();
    for (PhoneNumber number : getPhoneNumbersInternal()) {
        if (!ignoreNew || !number.isNew()) {
            String compName = number.getPhonenumber();
            compName = compName.toLowerCase();
            if (compName.equals(pn)) {
                return number;
            }
        }
    }
    return null;
}

public String getFirstName(){return this.firstName;}
public void setFirstName(String firstName){this.firstName = firstName;}

public String getMiddleInitial() {return this.middleInitial;}
public void setMiddleInitial(String middleinitial) {this.middleInitial = middleinitial;}

public String getLastName() {return this.lastName;}
public void setLastName(String lastName) {this.lastName = lastName;}

public Gender getSex() {return this.sex;}
public void setSex(Gender sex) {this.sex = sex;}

public void setDateOfBirth(DateTime birthDate){this.dateOfBirth = birthDate;}
public DateTime getDateOfBirth(){return this.dateOfBirth;}

public Race getRace() {return this.race;}
public void setRace(Race race) {this.race = race;}

@Override
public String toString() {
    return new ToStringCreator(this)
            .append("id", this.getId())
            .append("new", this.isNew())
            .append("lastName", this.getLastName())
            .append("firstName", this.getFirstName())
            .append("middleinitial", this.getMiddleInitial())
            .append("dateofbirth", this.dateOfBirth)
            .toString();
}
}
<小时/>

第二次编辑:

根据 Alexey 的评论,以下是 Controller 类中始终具有 @InitBinder 注释的方法。它与类似模块的 Controller 中的方法相同,其工作原理:

@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {dataBinder.setDisallowedFields("id");}
<小时/>

第三次编辑:

PatientController.java:

@Controller
@SessionAttributes(types = Patient.class)
public class PatientController {

private final ClinicService clinicService;

@Autowired
public PatientController(ClinicService clinicService) {this.clinicService = clinicService;}

@ModelAttribute("genders")
public Collection<Gender> populateGenders() {return this.clinicService.findGenders();}

@ModelAttribute("races")
public Collection<Race> populateRaces() {return this.clinicService.findRaces();}

@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {dataBinder.setDisallowedFields("id");}

@RequestMapping(value = "/patients/new", method = RequestMethod.GET)
public String initCreationForm(Map<String, Object> model) {
    Patient patient = new Patient();
    model.put("patient", patient);
    return "patients/createOrUpdatePatientForm";
}

@RequestMapping(value = "/patients/new", method = RequestMethod.POST)
public String processCreationForm(@Valid Patient patient, BindingResult result, SessionStatus status) {
    if (result.hasErrors()) {return "patients/createOrUpdatePatientForm";}
    else {
        this.clinicService.savePatient(patient);
        status.setComplete();
        return "redirect:/patients?patientID=" + patient.getId();
    }
}

@RequestMapping(value = "/patients", method = RequestMethod.GET)
public String processFindForm(@RequestParam("patientID") String patientId, Patient patient, BindingResult result, Map<String, Object> model) {
    Collection<Patient> results = this.clinicService.findPatientByLastName("");
    model.put("selections", results);
    int patntId = Integer.parseInt(patientId);
    Patient sel_patient = this.clinicService.findPatientById(patntId);//I added this
    model.put("sel_patient",sel_patient);
    return "patients/patientsList";
}

@RequestMapping(value = "/patients/{patientId}/edit", method = RequestMethod.GET)
public String initUpdatePatientForm(@PathVariable("patientId") int patientId, Model model) {
    Patient patient = this.clinicService.findPatientById(patientId);
    model.addAttribute(patient);
    return "patients/createOrUpdatePatientForm";
}

@RequestMapping(value = "/patients/{patientId}/edit", method = RequestMethod.PUT)
public String processUpdatePatientForm(@Valid Patient patient, BindingResult result, SessionStatus status) {
    if (result.hasErrors()) {
        System.out.println(":::::::::::::::: in PatientController.processUpdatePatientForm() result.hasErrors() ");
        List<ObjectError> errors = result.getAllErrors();
        for(int i=0;i<result.getErrorCount();i++){System.out.println("]]]]]]] error "+i+" is: "+errors.get(i).toString());}
        return "patients/createOrUpdatePatientForm";} 
    else {
        this.clinicService.savePatient(patient);
        status.setComplete();
        return "redirect:/patients?patientID=" + patient.getId();
    }
}
}  
<小时/>

第四次编辑:

性别.java

@Entity
@Table(name = "gender")
public class Gender extends NamedEntity {}

NamedEntity.java:

@MappedSuperclass
public class NamedEntity extends BaseEntity {

@Column(name = "name")
private String name;

public void setName(String name) {this.name = name;}

public String getName() {return this.name;}

@Override
public String toString() {return this.getName();}

}

BaseEntity.java:

@MappedSuperclass
public class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;

public void setId(Integer id) {this.id = id;}

public Integer getId() {return id;}

public boolean isNew() {return (this.id == null);}

}

最佳答案

您需要添加转换器或适当的编辑器。我更喜欢第一个。请参阅第 6.5 节。上this page了解详情。

您的转换器必须从数据库中获取具有给定名称的实体并返回它。代码会是这样的:

class StringToGender implements Converter<String, Gender> {
  @Autowired
  private GenderRepository repository;

  public Gender convert(String name) {
    return repository.getGenderByName(name);
  }
}

在您的应用程序上下文 xml 中(如果您使用 xml):

    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="org.example.StringToGender"/>
        </set>
    </property>

关于java - 如何查找 BindingResult 中的错误原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20792988/

相关文章:

java - 如何在java中使用 key 接口(interface)作为 key ?

java - android中如何获取drawable图片路径?

java - Spring Boot 冷启动

java - 是否可以在 Spring Data Rest 中使用和组合 WHERE 子句?

java - 有人可以解释一下这个 hibernate 方法的作用吗?

java - 如何更改 Javafx 默认应用程序图像/图标/缩略图

java - 如何在不手动转换为 JSON 的情况下使用 Jersey Client 发布 Pojo?

java - 使用 Spring 的多阶段形式

java - 使用Spring批量选择

mysql - 我应该如何设计这个数据库?