2016-01-04 14 views
8

Accessing JPA Data with RESTの例を、Personエンティティにアドレスリストを追加して拡張したいと考えました。Spring JPA REST One to Many

@Entity 
public class Person { 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private long id; 

    private String firstName; 
    private String lastName; 

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 
    private List<Address> addresses = new ArrayList<>(); 

    // get and set methods... 
} 

Addressクラスは非常にシンプルなものです::だから、私は@OneToManyアノテーションでリストaddressesを追加

@Entity 
public class Address { 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private long id; 
    private String street; 
    private String number; 
    // get and set methods... 
} 

そして最後に、私はAddressRepositoryインタフェースを追加しました:その後

public interface AddressRepository extends PagingAndSortingRepository<Address, Long> {} 

を私はいくつかのアドレスで人をPOSTしようとしました:

私が手
curl -i -X POST -H "Content-Type:application/json" -d '{ "firstName" : "Frodo", "lastName" : "Baggins", "addresses": [{"street": "somewhere", "number": 1},{"street": "anywhere", "number": 0}]}' http://localhost:8080/people 

エラーは次のとおりです。

Could not read document: Failed to convert from type [java.net.URI] to type [ws.model.Address] for value 'street'; 
nested exception is java.lang.IllegalArgumentException: Cannot resolve URI street. Is it local or remote? Only local URIs are resolvable. (through reference chain: ws.model.Person[\"addresses\"]->java.util.ArrayList[1]); 
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Failed to convert from type [java.net.URI] to type [ws.model.Address] for value 'street'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI street. Is it local or remote? Only local URIs are resolvable. (through reference chain: ws.model.Person[\"addresses\"]->java.util.ArrayList[1]) 

多くの関係に多くの、多くのものを作成して、JSONオブジェクトを投稿する適切な方法はどれ?

+0

ORMのエンティティクラスは表示されていますが、RESTに注釈が付けられているものは表示されていません。 – scottb

+0

カスタムコンバーターを使用した提案の回答を見るhttp://stackoverflow.com/questions/24781516/spring-data-rest-field-converter – dseibert

+1

@scottb両方のリポジトリのチュートリアルのように '@ RepositoryRestResource'アノテーションを使用します人、住所)。これにより、エンティティの共通RESTエンドポイントが作成されます。 –

答えて

6

は、次に、あなたの個人POSTにそのURLが返される(例えばhttp://localhost:8080/addresses/1http://localhost:8080/addresses/2)を使用し、最初の2つのアドレスをPOSTする必要がありますあなたが最初の人を保存したい場合は

curl -i -X POST -H "Content-Type:application/json" -d '{ "firstName" : "Frodo", "lastName" : "Baggins", "addresses": ["http://localhost:8080/addresses/1","http://localhost:8080/addresses/2"]}' http://localhost:8080/people 

、その後、あなたは可能性がそのアドレスを追加しますこれを行う:

curl -i -X POST -H "Content-Type:application/json" -d '{ "firstName" : "Frodo", "lastName" : "Baggins"}' http://localhost:8080/people 
curl -i -X POST -H "Content-Type:application/json" -d '{"street": "somewhere", "number": 1}' http://localhost:8080/addresses 
curl -i -X POST -H "Content-Type:application/json" -d '{"street": "anywhere", "number": 0}' http://localhost:8080/addresses 
curl -i -X PATCH -H "Content-Type: text/uri-list" -d "http://localhost:8080/addresses/1 
http://localhost:8080/addresses/2" http://localhost:8080/people/1/addresses 
+0

これは機能します(アノテーションを '@ ManyToMany'に置き換えるだけで多対多の関係にも使えます)。なぜそれがそのように機能するのか説明できますか?アプリケーションでは、私は人を追加し、次にアドレスを追加します。反対ではありません。 –

+0

このアプローチのもう一つの点は、サポートされているHTTPメソッドが 'GET'と' POST'(http://docs.spring.io/spring-data/rest/docs/current/reference/html/#_supported_http_methods)であることです。だから、もし私が既存の人に住所を追加したいのであれば、私はできません。 –

+0

@ArisF。私は、Spring Data RESTの精神で、リソースとそのURL(HATEOASアーキテクチャ(https://en.wikipedia.org/wiki/HATEOAS)を構築するためのURL)で作業するとします。最初にその人を保存してから自分のアドレスを割り当てる方法です(私はそれを編集したばかりです) –

0

あなたのレストサービスは住所の代わりに人を受け入れるべきではありませんか?

public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {} 

また、2つの異なる休憩サービスを作成しようとしている場合がありますが、それはわかりません。アドレス入力があるPersonを取る1つの休憩サービスしか持たないようにしてください。

+0

あなたは正しいです。私はコード内にそれを持っています。私はチュートリアルで言及されているので、質問でそれをスキップしました。 –

0

参照されたリポジトリをエクスポートしないことでこの問題を解決することができました。これは、インターフェースの上に注釈を追加することです。あなたの例では、それはそのようになります:

@RepositoryRestResource(exported = false) 
public interface AddressRepository extends CrudRepository<Address, Long> { 
} 

春データはまだあなたのために外部キーを伝播しないように、これは、部分的に問題を解決します。ただし、あなたの人物と住所は保持されます(所属する人物を参照することはありません)。 APIを呼び出してこれらの不足している外部キーを更新すると、@Francesco Pitzalisが言及したように、リンクされたすべてのアドレスでAPIを介して人を取得することができます。

私は助けてくれることを願っています。最後のメモ。私はHibernateが私たちのために外部キーを伝播することができないばかばかしい(基本的で必要なものとして)と考えているので、私はまだこれに取り組んでいます。どういうわけか可能です。


EDITED:確かに可能でした。以下の実装は、エンティティとその子が、Springデータ(Rest-Repositoryを公開しているため)、Hibernate 5.0.12、MySQLとストレージエンジンInnoDB(メモリにはない)に基づいたアーキテクチャのために、データベース)。

@Entity 
public class Producto implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id; 
    private String nombre; 
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) 
    @JoinColumn(name = "producto_id") 
    private List<Formato> listaFormatos; 
    //Constructor, getters and setters 
} 

https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html - これは非常に重要でした。

@Entity 
public class Formato implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id; 
    private Integer cantidad; 
    private String unidadMedida; 
    @ManyToOne 
    private Producto producto; 
    //Constructor, getters and setters 
} 

@RepositoryRestResource 
public interface ProductoRepository extends CrudRepository<Producto, Long> { 
} 

@RepositoryRestResource 
public interface FormatoRepository extends CrudRepository<Formato, Long> { 
} 

spring.datasource.url=jdbc:mysql://localhost:3306/(database name) 
spring.datasource.username=(username) 
spring.datasource.password=(password) 
spring.jpa.show-sql=true 

spring.jpa.hibernate.ddl-auto=create-drop 
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect 

これは非常に重要です。あなたは、方言を正しく設定するために、HibernateがSQL文をどこで実行しているかを知る必要があります。私の場合、テーブルのストレージエンジンはInnoDBです。次のリンクが助けになりました。 What mysql driver do I use with spring/hibernate?

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-data-jpa</artifactId> 
</dependency> 
<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-data-rest</artifactId> 
</dependency> 
<dependency> 
    <groupId>mysql</groupId> 
    <artifactId>mysql-connector-java</artifactId> 
    <scope>runtime</scope> 
</dependency> 

私が説明することができていない唯一のことは、今、私が「子」のリポジトリをエクスポートすることができ、ということです、それはまだ正常に動作します。どんなアイディアですか、みんな?