在eclipse和tomcat服务器中使用hibernate的spring mvc web应用程序中,我更改了几个文本字段以下拉jsp中的列表,以便可以从其自己的下拉菜单中选择每个人的性别和种族。我小心翼翼地更改了应用程序的其他级别,包括在底层数据库中设置性别和竞争的连接表,以及更改模型和存储库级别中的代码。应用程序编译,并且jsp使用每个下拉列表中所选人员的正确选定值加载,但单击提交/更新按钮会导致BindingResult.hasErrors()问题,这无法帮助我本地化问题的原因。
有人可以帮我找到处理更新失败的原因吗?
这是在控制器类中调用的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]
请注意,[Hispanic]和[Male]值会在错误消息中显示为触发错误。问题可能是,当传递id属性时,Gender和Race的name属性被传递给Spring MVC。但是如何在代码中修复此问题?
有人可以帮我解决这个问题吗?第一步是如何获得更有用的错误消息,该消息定位我的代码中触发问题的位置。
Per 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的评论,以下是控制器类中始终具有@InitBinder注释的方法。它与类似模块的控制器中的方法相同:
@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();
}
}
}
Gender.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);}
}
答案 0 :(得分:2)
您需要添加转换器或正确的编辑器。我更喜欢第一个。请参阅第6.5节。在this page获取详细信息。
您的转换器必须从数据库中获取具有给定名称的Entity并将其返回。代码将是这样的:
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>