这是我的实体:
@Entity
@Table( name = "tbl_license" )
public class License implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column( name = "id", nullable = false, columnDefinition = "serial" )
private int id;
@Column( name = "key", nullable = false, columnDefinition = "text" )
private String key;
@Column( name = "users", columnDefinition = "text" )
private String users;
@Transient
private final SimpleDateFormat dateFormat = new SimpleDateFormat( "MM/dd/yyyy" );
@Column( name = "valid_from" )
@Temporal( TemporalType.DATE )
private Date from;
@Column( name = "valid_to" )
@Temporal( TemporalType.DATE )
private Date to;
@Column( name = "volume" )
private Integer volume;
@Column( name = "SSL", columnDefinition = "bool default false" )
private boolean SSL = false;
@ManyToOne
@JoinColumn( name = "software_id", columnDefinition = "int" )
@ForeignKey( name = "tbl_software_fkey" )
private Software software;
@ManyToOne
@JoinColumn( name = "license_type_id", columnDefinition = "int" )
@ForeignKey( name = "tbl_license_type_fkey" )
private LicenseType licenseType;
@ManyToOne
@JoinColumn( name = "license_class_id", columnDefinition = "int" )
@ForeignKey( name = "tbl_license_class_fkey" )
private LicenseClass licenseClass;
@ManyToOne
@JoinColumn( name = "license_metric_id", columnDefinition = "int" )
@ForeignKey( name = "tbl_license_metric_fkey" )
private LicenseMetric licenseMetric;
@ManyToOne
@JoinColumn( name = "location_id", columnDefinition = "int" )
@ForeignKey( name = "tbl_location_fkey" )
private Location location;
@OneToMany( fetch = FetchType.LAZY, mappedBy = "id.license"/*, cascade = { CascadeType.REMOVE }, orphanRemoval = true*/ )
private List<LicenseUserAlert> alerts;
public int getId()
{
return id;
}
public void setId( int id )
{
this.id = id;
}
public String getKey()
{
return key;
}
public void setKey( String key )
{
this.key = key;
}
public String getUsers()
{
return users;
}
public void setUsers( String users )
{
this.users = users;
}
public String getFrom()
{
if ( from == null )
{
return null;
}
return dateFormat.format( from );
}
public Date getFromDate()
{
return from;
}
public void setFrom( String from )
{
try
{
this.from = dateFormat.parse( from );
}
catch ( ParseException e )
{
this.from = null;
}
}
public String getTo()
{
if ( to == null )
{
return null;
}
return dateFormat.format( to );
}
public Date getToDate()
{
return to;
}
public void setTo( String to )
{
try
{
this.to = dateFormat.parse( to );
}
catch ( ParseException e )
{
this.to = null;
}
}
@Transient
public Long getRemaining()
{
if ( from == null || to == null )
{
return null;
}
Date now = new Date();
Date fromDate = from;
Date toDate = to;
if ( fromDate.before( now ) )
{
fromDate = now;
}
return toDate.getTime() - fromDate.getTime();
}
public int getVolume()
{
return volume != null ? volume : 0;
}
public void setVolume( int volume )
{
this.volume = volume;
}
public boolean isSSL()
{
return SSL;
}
public void setSSL( boolean SSL )
{
this.SSL = SSL;
}
public Software getSoftware()
{
return software;
}
public void setSoftware( Software software )
{
this.software = software;
}
public LicenseType getLicenseType()
{
return licenseType;
}
public void setLicenseType( LicenseType licenseType )
{
this.licenseType = licenseType;
}
public LicenseClass getLicenseClass()
{
return licenseClass;
}
public void setLicenseClass( LicenseClass licenseClass )
{
this.licenseClass = licenseClass;
}
public LicenseMetric getLicenseMetric()
{
return licenseMetric;
}
public void setLicenseMetric( LicenseMetric licenseMetric )
{
this.licenseMetric = licenseMetric;
}
public Location getLocation()
{
return location;
}
public void setLocation( Location location )
{
this.location = location;
}
public List<LicenseUserAlert> getAlerts()
{
return alerts;
}
public void setAlerts( List<LicenseUserAlert> alerts )
{
this.alerts = alerts;
}
}
以下是控制器方法:
@RequestMapping( value = "/save.html", params = "json", method = RequestMethod.POST )
public @ResponseBody Map<String, ? extends Object> saveJSON( @RequestBody License license, HttpServletRequest request, HttpServletResponse response )
{
Map<String, String> validationMessages = new HashMap<String, String>();
for ( FieldError error : getFieldErrors( license ) )
{
validationMessages.put( error.getField(), error.getDefaultMessage() );
}
Set<ConstraintViolation<License>> constraintViolations = validator.validate( license );
if ( !validationMessages.isEmpty() || !constraintViolations.isEmpty() )
{
for ( ConstraintViolation<License> constraintViolation : constraintViolations )
{
String violationMessage = constraintViolation.getMessage();
try
{
violationMessage = messageSource.getMessage( constraintViolation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName() + ".license." + constraintViolation.getPropertyPath().toString(), null, null );
}
catch ( Exception e )
{
}
validationMessages.put( constraintViolation.getPropertyPath().toString(), violationMessage );
}
return Collections.singletonMap( "validationMessages", validationMessages );
}
service.save( license );
return Collections.singletonMap( "redirect", request.getContextPath() + "/index.html" );
}
@RequestMapping( value = "/save.html", method = RequestMethod.POST )
public String save( @ModelAttribute( "license" ) @Valid License license, BindingResult result, Map<String, Object> map )
{
for ( FieldError error : getFieldErrors( license ) )
{
result.addError( error );
}
if ( result.hasErrors() )
{
map.put( "softwareList", softwareService.list() );
map.put( "licenseTypeList", licenseTypeService.list() );
map.put( "licenseClassList", licenseClassService.list() );
map.put( "licenseMetricList", licenseMetricService.list() );
map.put( "locationList", locationService.list() );
return "license.form";
}
service.save( license );
return "redirect:/index.html";
}
为了完整起见,这是以下形式:
<c:url value="/licenses/save.html" var="formAction" />
<form:form modelAttribute="license" action="${formAction}" method="post">
<form:errors path="*" cssClass="error" element="div" />
<form:hidden path="id" />
<div>
<form:label path="software.id">Software*:</form:label>
<form:select path="software.id" items="${softwareList}" itemValue="id" itemLabel="title" />
</div>
<div>
<form:label path="key">License key*:</form:label>
<form:input path="key" />
</div>
<div>
<form:label path="users">Installied at:</form:label>
<form:input path="users" />
</div>
<div>
<form:label path="licenseType">Type*:</form:label>
<form:select id="licenseType" path="licenseType.id" items="${licenseTypeList}" itemValue="id" itemLabel="title" />
</div>
<div>
<span id="licenseType1" class="typeAttr">
<form:label path="volume">Volume*:</form:label>
<form:input type="number" path="volume" />
</span>
<span id="licenseType2" class="typeAttr">
<form:label path="location">Location*:</form:label>
<form:select path="location.id" items="${locationList}" itemValue="id" itemLabel="title" />
</span>
<span id="licenseType3" class="typeAttr">
<form:label path="from">Valid from*:</form:label>
<form:input type="date" path="from" />
<form:label path="to">to*:</form:label>
<form:input type="date" path="to" />
</span>
</div>
<div>
<form:label path="licenseClass">Class*:</form:label>
<form:select path="licenseClass.id" items="${licenseClassList}" itemValue="id" itemLabel="title" />
</div>
<div>
<form:label path="licenseMetric">Metric*:</form:label>
<form:select path="licenseMetric.id" items="${licenseMetricList}" itemValue="id" itemLabel="title" />
</div>
<input type="submit" value="Submit" />
</form:form>
第一种方法是ajax提交,第二种方法是后备/用于没有ajax的测试。
没有ajax工作得很好,但使用第一种方法导致400 Bad Request:
The request sent by the client was syntactically incorrect ().
它似乎与其他实体(类,指标,位置......)有关
我已经尝试为所有这些实体添加一个@RequestBody
param到saveJSON方法 - 但是没有用(结果相同)
修改
这是帖子的主体(这是一个结构良好的json对象,所以我不知道为什么它在语法上是不正确的)
{
"id":"0",
"software.id":"5",
"key":"12345",
"users":"a, b, c",
"licenseType.id":"1",
"volume":"2",
"from":"",
"to":"",
"licenseClass.id":"1",
"licenseMetric.id":"1"
}
答案 0 :(得分:2)
我认为你应该实现一个转换器,它能够通过从数据库加载来将表示id的String转换为LicenseMetric
的实例。 (以及其他实体的其他转换器)。
然后你应该用“licenseMetric”替换参数“licenseMetric.id”。
转换器草图
import org.springframework.core.convert.converter.Converter;
public class StringIdToLicenseMetricConverter implements Converter<String, LicenseMetric> {
/** The string that represents null. */
private static final String NULL_REPRESENTATION = "null";
/** Generic dao. */
@Resource
private LicenseMetricDao licenseMetricDao;
@Override
public LicenseMetric convert(final String idString) {
if (idString.equals(NULL_REPRESENTATION)) {
return null;
}
try {
int id = Integer.parse(idString);
return this.licenseMetricDao.getById(id, this.businessClass);
}
catch NumberFormatException(e) {
throw new RuntimeException("could not convert `" + idString + "` to an valid id");
}
}
}