我使用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注释,但我仍然遇到错误。
答案 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)代码就不知道如何处理这个成员。