PUBLICAR una asociación de sub-recursos @OneToMany en Spring Data REST

103

Actualmente tengo una aplicación Spring Boot que usa Spring Data REST. Tengo una entidad del dominio Postque tiene la @OneToManyrelación de dominio a otra entidad, Comment. Estas clases están estructuradas de la siguiente manera:

Post.java:

@Entity
public class Post {

    @Id
    @GeneratedValue
    private long id;
    private String author;
    private String content;
    private String title;

    @OneToMany
    private List<Comment> comments;

    // Standard getters and setters...
}

Comentario.java:

@Entity
public class Comment {

    @Id
    @GeneratedValue
    private long id;
    private String author;
    private String content;

    @ManyToOne
    private Post post;

    // Standard getters and setters...
}

Sus repositorios Spring Data REST JPA son implementaciones básicas de CrudRepository:

PostRepository.java:

public interface PostRepository extends CrudRepository<Post, Long> { }

CommentRepository.java:

public interface CommentRepository extends CrudRepository<Comment, Long> { }

El punto de entrada de la aplicación es una aplicación Spring Boot estándar y simple. Todo está configurado stock.

Application.java

@Configuration
@EnableJpaRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application {

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

Todo parece funcionar correctamente. Cuando ejecuto la aplicación, todo parece funcionar correctamente. Puedo PUBLICAR un nuevo objeto de publicación para que me http://localhost:8080/postsguste:

Cuerpo: {"author":"testAuthor", "title":"test", "content":"hello world"}

Resultado en http://localhost:8080/posts/1:

{
    "author": "testAuthor",
    "content": "hello world",
    "title": "test",
    "_links": {
        "self": {
            "href": "http://localhost:8080/posts/1"
        },
        "comments": {
            "href": "http://localhost:8080/posts/1/comments"
        }
    }
}

Sin embargo, cuando realizo un GET en http://localhost:8080/posts/1/comments, obtengo un objeto vacío {}devuelto, y si intento PUBLICAR un comentario en el mismo URI, obtengo un Método HTTP 405 No permitido.

¿Cuál es la forma correcta de crear un Commentrecurso y asociarlo a este Post? Me gustaría evitar PUBLICAR directamente a http://localhost:8080/commentssi es posible.

ccampo
fuente
9
7 días después y todavía sin suerte. Si alguien sabe cómo hacer que este comportamiento funcione, hágamelo saber. ¡Gracias!
ccampo
¿estás usando @RepositoryRestResource o un controlador? Sería útil ver ese código también.
Magnus Lassi
Estoy usando el descanso de datos de arranque de Spring, funcionó para mí http://stackoverflow.com/questions/37902946/add-item-to-the-collection-with-foreign-key-via-rest-call
Taimur

Respuestas:

47

Primero debe publicar el comentario y, mientras publica el comentario, puede crear una entidad de publicaciones de asociación.

Debería verse como a continuación:

http://{server:port}/comment METHOD:POST

{"author":"abc","content":"PQROHSFHFSHOFSHOSF", "post":"http://{server:port}/post/1"}

y funcionará perfectamente bien.

Chetan Kokil
fuente
2
Esto funcionó para mí. Solo asegúrese de que author.postse pueda escribir (por ejemplo, con un establecedor o una @JsonValueanotación)
Scheffield
1
¿Debería esto funcionar también con una solicitud de parche como al mover el comentario de una publicación a otra?
aycanadal
2
Este sería mi enfoque (ampliamente) preferido, pero no parece funcionar para mí. :( Crea el comentario, pero no crea la fila en la tabla de resolución (POST_COMMENTS). ¿Alguna sugerencia sobre cómo resolverlo?
banncee
3
¿Cuál sería el enfoque para un escenario, por ejemplo, con entidades de Lugar y Dirección, donde un lugar debe tener una Dirección y una dirección DEBE estar asociada con un Lugar? Quiero decir ... ¿para evitar crear una dirección huérfana que nunca pueda asignarse a nada? Tal vez me equivoque, pero la aplicación cliente NUNCA DEBE ser responsable de mantener la coherencia dentro de la base de datos. No puedo confiar en que la aplicación cliente cree una dirección y luego definitivamente asigne a un lugar. ¿Hay alguna manera de PUBLICAR el sub-recurso (en este caso la entidad Dirección) con la creación del recurso real para evitar inconsistencias?
apostrophedottilde
2
Intento hacer esto ( ver aquí ) pero por alguna razón solo se crea el recurso, no la asociación.
displayname
55

Suponiendo que ya ha descubierto el URI de la publicación y, por lo tanto, el URI del recurso de asociación (que se considera $association_urien el siguiente), generalmente se siguen estos pasos:

  1. Descubra el recurso de colección gestionando comentarios:

    curl -X GET http://localhost:8080
    
    200 OK
    { _links : {
        comments : { href : "…" },
        posts :  { href : "…" }
      }
    }
  2. Siga el commentsenlace y POSTsus datos hasta el recurso:

    curl -X POST -H "Content-Type: application/json" $url 
    {  // your payload // … }
    
    201 Created
    Location: $comment_url
  3. Asigne el comentario a la publicación emitiendo un PUTal URI de la asociación.

    curl -X PUT -H "Content-Type: text/uri-list" $association_url
    $comment_url
    
    204 No Content

Tenga en cuenta que en el último paso, de acuerdo con la especificación de text/uri-list, puede enviar varios URI que identifiquen comentarios separados por un salto de línea para asignar varios comentarios a la vez.

Algunas notas más sobre las decisiones generales de diseño. Un ejemplo de publicación / comentario suele ser un gran ejemplo para un agregado, lo que significa que evitaría la referencia inversa de la Commenta la Posty también evitaría la CommentRepositorycompletamente. Si los comentarios no tienen un ciclo de vida por sí mismos (lo que generalmente no tienen en una relación de estilo de composición), prefiere que los comentarios se procesen en línea directamente y todo el proceso de agregar y eliminar comentarios se puede manejar usando Parche JSON . Spring Data REST ha agregado soporte para eso en el último candidato de lanzamiento para la próxima versión 2.2.

Oliver Drotbohm
fuente
4
Interesado aquí de los votantes abajo, cuál fue el motivo de las votaciones;).
Oliver Drotbohm
3
No estoy seguro acerca de los votantes en contra ... ¡Ni siquiera tengo la reputación de hacerlo! La razón por la que no necesariamente me gusta poner comentarios en línea con las publicaciones es porque considera el escenario (poco probable) en el que tengo miles de comentarios para una sola publicación. Me gustaría poder paginar la colección de comentarios en lugar de obtener el grupo completo cada vez que quiero acceder al contenido de la publicación.
ccampo
25
La forma más intuitiva para mí de publicar un comentario es hacer una POST en localhost: 8080 / posts / 1 / comments . ¿No es la forma más sencilla y significativa de hacerlo? Y al mismo tiempo, aún debería poder tener un repositorio de comentarios dedicado. ¿Es la primavera o el estándar HAL lo que no permite esto?
aycanadal
4
@OliverGierke ¿Sigue siendo esta la forma recomendada / única de hacer esto? ¿Qué pasa si el niño no acepta valores NULL ( @JoinColumn(nullable=false))? No sería posible POSTAR primero al hijo y luego PONER / PATCHAR la asociación principal.
JW Lim
2
¿Hay alguna guía para usar la API creada con Spring Data Rest? Lo busqué en Google durante 2 horas y no encontré nada. ¡Gracias!
Skeeve
2

Hay 2 tipos de asociación y composición cartográfica. En caso de asociación, usamos el concepto de tabla de unión como

Empleado - 1 a n-> Departamento

Por lo tanto, se crearán 3 tablas en el caso de Asociación Empleado, Departamento, Empleado_Departamento

Solo necesita crear el EmployeeRepository en su código. Aparte de ese mapeo debería ser así:

class EmployeeEntity{

@OnetoMany(CascadeType.ALL)
   private List<Department> depts {

   }

}

La entidad del departamento no contendrá ningún mapeo para la clave forign ... así que ahora, cuando intente la solicitud POST para agregar Empleado con Departamento en una sola solicitud json, se agregará ...

Naveen Goyal
fuente
1

Enfrenté el mismo escenario y tuve que eliminar la clase de repositorio para la entidad secundaria, ya que he usado una asignación de uno a muchos y extrajo datos a través de la entidad principal. Ahora obtengo la respuesta completa con datos.

Selva
fuente
1
Esto de lo que hablas se puede hacer fácilmente con proyecciones
kboom
0

Para el mapeo oneToMany, simplemente haga un POJO para esa clase que desea mapear, y una anotación @OneToMany, e internamente lo mapeará a ese ID de tabla.

Además, debe implementar la interfaz serializable a la clase que está recuperando los datos.

Soleado chaurasia
fuente