Ho una demo dimostrativa semplice utilizzando l'architettura Spring Data REST/RestRepository. I miei due entità sono:Come creare e connettere le risorse correlate utilizzando i repository REST di Spring Data?
@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>
}
e per 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;
}
con i repository:
@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);
}
e:
// @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);
}
e il codice main() di
01.235.@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);
}
}
con questo codice, posso salvare una CSZ
da POST
ing questo JSON per http://example.com:8080/zip
:
{ "zipCode" : "28899" , "city" : "Ada", "stateProv" : "NC" }
ma se provo a salvare un Address
dal POST
ing il JSON per …/add
:
{ "streetNo" : "985" , "streetName" : "Bellingham", "plus4Zip" : 2212, "cityStateZip" : { "zipCode" : "28115" , "city" : "Mooresville", "stateProv" : "NC" } }
Ho ricevuto l'errore
{
"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\"])"
}
Ora se cambio CityStateZipRepository
per includere export=false
nell'annotazione, posso quindi salvare il Address
e CSZ
nel database. Ma a quel tempo, …/zip
non è più esposto sull'interfaccia, e facendo GET
…/addr
o …/addr/{id}
cause di questo errore:
{
"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 C'è un modo per impostare questo modello di essere in grado di POST
e GET
da questo database ? Inoltre, il JSON passato a Address
salverà una nuova istanza di CityStateZip
- quale formato ci permetterà di fare riferimento a un elemento esistente CityStateZip
?
Grazie per l'aiuto che puoi fornire - questo ci sta facendo impazzire da giorni ormai.
Ah, questo è il pezzo che mi mancava. Ho messo giù il concetto, ma non conoscevo il formato dei dati POST REST. Fare riferimento a CityStateZip in questo modo ora mi consente di salvare un indirizzo facendo riferimento a un zip esistente. Grazie! (È interessante notare che quando ottengo i dati CityStateZip, l'URI è ... zip/10; quando OTSO i dati dell'indirizzo, l'URI zip dello stesso CityStateZip compare nel risultato come ... addr/15/CityStateZip.) – LitterWalker
Quest'ultima è una [risorsa di associazione] (http://docs.spring.io/spring-data/rest/docs/current/reference/html/#repository-resources.association-resource) che esponiamo per consentire di alterare l'associazione (particolarmente importante per le to-many-associations). Se OTTIENI che dovresti ricevere un'intestazione 'Content-Location' che punta alla risorsa associata. –