使用Ajax提交Spring表单,其中Entity具有外部实体

时间:2012-02-13 22:39:46

标签: spring spring-3

这是我的实体:

@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"
}

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");
        }
    }
}

@See Spring Reference Chapter 6.5.1 Converter SPI