Ich habe eine einfache Proof-of-Concept-Demo mit Spring Data REST/RestRepository-Architektur. Meine beiden Einheiten sind:Wie können Sie verwandte Ressourcen mithilfe von Spring Data REST-Repositorys erstellen und verbinden?
@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>
}
und für CityStateZip
:
@Entity
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;
}
mit Repositories:
@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);
}
und:
// @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);
}
und main() Code von
@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);
}
}
mit diesem Code kann ich ein CSZ
durch POST
ing dieses JSON http://example.com:8080/zip
speichern:
{ "zipCode" : "28899" , "city" : "Ada", "stateProv" : "NC" }
aber wenn ich versuche, eine Address
durch POST
ing die JSON …/add
zu speichern:
{ "streetNo" : "985" , "streetName" : "Bellingham", "plus4Zip" : 2212, "cityStateZip" : { "zipCode" : "28115" , "city" : "Mooresville", "stateProv" : "NC" } }
Ich bekomme den Fehler
{
"cause": {
"cause": {
"cause": null,
"message": "Template must not be null or empty!"
},
"message": "Template must not be null or empty! (through reference chain: domain.location.Address[\"cityStateZip\"])"
},
"message": "Could not read JSON: Template must not be null or empty! (through reference chain: domain.location.Address[\"cityStateZip\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Template must not be null or empty! (through reference chain: domain.location.Address[\"cityStateZip\"])"
}
Jetzt, wenn ich CityStateZipRepository
ändern, um export=false
in die Anmerkung einzuschließen, kann ich dann die Address
und CSZ
in der Datenbank speichern. Aber zu dieser Zeit wird …/zip
nicht mehr auf der Oberfläche freigelegt und dabei GET
…/addr
oder …/addr/{id}
Ursachen dieser Fehler:
{
"timestamp": 1417728145384,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.http.converter.HttpMessageNotWritableException",
"message": "Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)) (through reference chain: org.springframework.hateoas.PagedResources[\"_embedded\"]->java.util.UnmodifiableMap[\"addr\"]->java.util.ArrayList[0]->org.springframework.hateoas.Resource[\"content\"]->domain.location.Address[\"cityStateZip\"]->domain.location.CityStateZip_$$_jvst4e0_0[\"handler\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)) (through reference chain: org.springframework.hateoas.PagedResources[\"_embedded\"]->java.util.UnmodifiableMap[\"addr\"]->java.util.ArrayList[0]->org.springframework.hateoas.Resource[\"content\"]->domain.location.Address[\"cityStateZip\"]->domain.location.CityStateZip_$$_jvst4e0_0[\"handler\"])",
"path": "/addr"
}
Isa es eine Möglichkeit, dieses Modell zu gründen zu können POST
und GET
aus dieser Datenbank ? Außerdem speichert der JSON, der an Address
übergeben wurde, eine neue Instanz von CityStateZip
- welches Format ermöglicht es uns, auf ein vorhandenes Element CityStateZip
zu verweisen?
Vielen Dank für jede Hilfe, die Sie zur Verfügung stellen können - das macht uns seit Tagen verrückt.
Ah, das ist das Stück, das ich vermisste. Ich hatte das Konzept, aber wusste nicht das Format der REST POST Daten. Wenn ich so auf CityStateZip zugreife, kann ich jetzt eine Adresse speichern, die auf eine bestehende Zip-Datei verweist. Vielen Dank! (Es ist interessant, dass, wenn ich CityStateZip-Daten abrufe, der URI ... zip/10 ist; wenn ich Adressdaten abrufe, erscheint der Zip-URI desselben CityStateZip im Ergebnis als ... addr/15/CityStateZip.) – LitterWalker
Letzteres ist eine [Assoziationsressource] (http://docs.spring.io/spring-data/rest/docs/current/reference/html/#repository-resources.association-resource), die wir zur Verfügung stellen, um die Assoziation zu ändern (besonders wichtig für Zu-viele-Assoziationen). Wenn Sie GET erhalten, sollten Sie einen Header "Content-Location" erhalten, der auf die zugehörige Ressource verweist. –