通过REST为@OneToMany返回JSON失败(以前为NullPointer)异常

时间:2014-12-13 15:01:40

标签: java json spring rest spring-boot

我使用Spring Data REST / RestRepository架构进行了简单的概念验证演示。我从两个实体开始:

@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="Address")
public class Address implements Serializable {

    public Address() {}

    @Column(name="ID", nullable=false, unique=true) 
    @Id 
    @GeneratedValue(generator="CUSTOMER_ADDRESSES_ADDRESS_ID_GENERATOR")    
    @org.hibernate.annotations.GenericGenerator(name="CUSTOMER_ADDRESSES_ADDRESS_ID_GENERATOR", strategy="native")  
    private int ID;

    //@RestResource(exported = false)
    @ManyToOne(targetEntity=domain.location.CityStateZip.class, fetch=FetchType.LAZY)   
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.PERSIST}) 
    @JoinColumns({ @JoinColumn(name="CityStateZipID", referencedColumnName="ID", nullable=false) }) 
    private domain.location.CityStateZip cityStateZip;

    @Column(name="StreetNo", nullable=true) 
    private int streetNo;

    @Column(name="StreetName", nullable=false, length=40)   
    private String streetName;

    <setters and getters ommitted>  
}

CityStateZip

@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="CityStateZip")
public class CityStateZip {

    public CityStateZip() {}

    @Column(name="ID", nullable=false, unique=true) 
    @Id 
    @GeneratedValue(generator="CUSTOMER_ADDRESSES_CITYSTATEZIP_ID_GENERATOR")   
    @org.hibernate.annotations.GenericGenerator(name="CUSTOMER_ADDRESSES_CITYSTATEZIP_ID_GENERATOR", strategy="native") 
    private int ID;

    @Column(name="ZipCode", nullable=false, length=10)  
    private String zipCode;

    @Column(name="City", nullable=false, length=24) 
    private String city;

    @Column(name="StateProv", nullable=false, length=2) 
    private String stateProv;

}

使用存储库:

@RepositoryRestResource(collectionResourceRel = "addr", path = "addr") 
public interface AddressRepository extends JpaRepository<Address, Integer> {

     List<Address> findByStreetNoAndStreetNameStartingWithIgnoreCase(@Param("stNumber") Integer streetNo, @Param("street") String streetName);
     List<Address> findByStreetNameStartingWithIgnoreCase(@Param("street") String streetName);
     List<Address> findByStreetNo(@Param("streetNo") Integer strNo);
}

// @RepositoryRestResource(collectionResourceRel = "zip", path = "zip", exported = false)
@RepositoryRestResource(collectionResourceRel = "zip", path = "zip")
public interface CityStateZipRepository extends JpaRepository<CityStateZip, Integer> {

    List<CityStateZip> findByZipCode(@Param("zipCode") String zipCode);
    List<CityStateZip> findByStateProv(@Param("stateProv") String stateProv);
    List<CityStateZip> findByCityAndStateProv(@Param("city") String city, @Param("state") String state);
}

的main()代码
@Configuration
@EnableJpaRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
// @EnableTransactionManagement
@PropertySource(value = { "file:/etc/domain.location/application.properties" })
@ComponentScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

使用此代码,一切正常。我可以保存CityStateZip并在保存地址时引用它,并且可以获取结果地址。第二步是添加另一个类:一个地址可以有0到多个相关的备注,而备注则绑定到一个地址。该实体是:

@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="Remark")
@SuppressWarnings({ "all", "unchecked" })
public class Remark implements Serializable {
    public Remark() {
    }

    @Column(name="ID", nullable=false)  
    @Id 
    @GeneratedValue(generator="CUSTOMER_ADDRESSES_REMARK_ID_GENERATOR") 
    @org.hibernate.annotations.GenericGenerator(name="CUSTOMER_ADDRESSES_REMARK_ID_GENERATOR", strategy="native")   
    private int ID;

    @ManyToOne(targetEntity=domain.location.Address.class, fetch=FetchType.LAZY)    
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.LOCK})    
    @JoinColumns({ @JoinColumn(name="LocationID", referencedColumnName="ID", nullable=false) }) 
    private domain.location.Address address_ix;

    @Column(name="Comment", nullable=false, length=255) 
    private String comment; 

    <getters, setters removed>
}

和存储库:

@RepositoryRestResource(collectionResourceRel = "remarks", path = "remarks")
public interface RemarkRepository extends JpaRepository<Remark, Integer> {
}

我将此添加到地址:

@OneToMany(mappedBy="address_ix", targetEntity=domain.location.Remark.class)    
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK}) 
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
private java.util.Set remark = new java.util.HashSet();

如果我没有任何地址实例,我可以通过执行GET ... / addr来检索公开的API。我仍然可以将Address实例保存到数据库中。但是一旦我有一个保存的实例,做一个GET ... / addr,或者试图获取特定的实例,我得到一个例外:

"timestamp": 1418423263313,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.http.converter.HttpMessageNotWritableException",
"message": "Could not write JSON: (was java.lang.NullPointerException) (through reference chain: org.springframework.hateoas.PagedResources[\"_embedded\"]->java.util.UnmodifiableMap[\"addr\"]->java.util.ArrayList[0]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: org.springframework.hateoas.PagedResources[\"_embedded\"]->java.util.UnmodifiableMap[\"addr\"]->java.util.ArrayList[0])",
"path": "/addr"

我认为我在地址中的@OneToMany关联,或者备注中的@ManyToOne,或者两者的结合都有问题。我添加了@ManyToOne,单向关联到Address没有问题。为了能够通过GET检索地址数据,我需要做什么?什么是JSON抱怨? (此时,数据库中没有Remark实例。我甚至在@OneToMany前添加了@JsonIgnore注释,但我仍然遇到错误。

1 个答案:

答案 0 :(得分:0)

事实证明问题是由于自动生成的代码造成的。声明集的声明缺少调用Set(或HashSet)类型的参数。如果我将代码更改为

@OneToMany(mappedBy="address_ix", targetEntity=domain.location.Remark.class)    
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK}) 
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
private java.util.Set<Remark> remark = new java.util.HashSet<Remark>();

代码现在可以根据需要运行。如果没有参数,(Spring或JSON)代码就不知道如何处理这个成员。