如何在CRNK(JSON-API)中实现子资源关系且不使用顶级仓库
Got it, let's walk through how to build sub-resource relationships with CRNK while making sure everything stays fully compliant with the JSON-API spec. We'll use your queue + historical time properties example to keep things concrete, no extra business fluff.
Start by creating your core Queue resource and its sub-resource (let's call it QueueHistory for clarity) with CRNK's annotations. This lays the foundation for JSON-API's resource structure.
// Queue.java @JsonApiResource(type = "queues") public class Queue { @JsonApiId private Long id; private String name; private String description; // Define the TO_MANY relationship to QueueHistory @JsonApiRelation(opposite = "queue", kind = RelationKind.TO_MANY) private List<QueueHistory> history; // Getters and setters omitted for brevity } // QueueHistory.java @JsonApiResource(type = "queue-histories") public class QueueHistory { @JsonApiId private Long id; private LocalDateTime timestamp; private String eventType; // e.g., "CREATED", "UPDATED" // Define the inverse TO_ONE relationship back to Queue @JsonApiRelation(opposite = "history", kind = RelationKind.TO_ONE) private Queue queue; // Getters and setters omitted for brevity }
Key note: The type values ("queues", "queue-histories") must be pluralized and consistent—this is a strict JSON-API requirement.
CRNK relies on repositories to handle data access and relationship resolution. Create repositories for both resources, extending CRNK's base repository classes. If you're using JPA, you can leverage JpaRepositoryWrapper to avoid boilerplate.
// QueueRepository.java public class QueueRepository extends JpaRepositoryWrapper<Queue, Long> { public QueueRepository(EntityManager em) { super(Queue.class, em); } } // QueueHistoryRepository.java public class QueueHistoryRepository extends JpaRepositoryWrapper<QueueHistory, Long> { public QueueHistoryRepository(EntityManager em) { super(QueueHistory.class, em); } // Optional: Add a method to fetch history for a specific queue (CRNK can auto-generate this, but explicit is okay) public List<QueueHistory> findByQueue(Queue queue) { CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); CriteriaQuery<QueueHistory> query = cb.createQuery(QueueHistory.class); Root<QueueHistory> root = query.from(QueueHistory.class); query.where(cb.equal(root.get("queue"), queue)); return getEntityManager().createQuery(query).getResultList(); } }
Once your setup is done, test the endpoints to ensure they adhere to JSON-API standards:
- Fetch a queue with its history: Send
GET /queues/1?include=history
The response will include a top-levelincludedarray with all relatedqueue-historiesresources, which is exactly what JSON-API requires for compound documents. - Fetch only the queue's history: Send
GET /queues/1/history
This returns a JSON-API document withdataas an array ofqueue-historiesresources, no extra fluff. - Update a relationship: Use
PATCH /queues/1/relationships/historywith a JSON-API compliant payload to add/remove history entries—CRNK handles the relationship persistence automatically if your repository supports it.
- Always specify the
oppositeattribute in@JsonApiRelation—this ensures JSON-API's bidirectional relationship rules are followed. - Use the correct
RelationKind(TO_ONE/TO_MANY) to match your relationship cardinality; this affects how CRNK serializes the response. - Never skip the
@JsonApiIdannotation—JSON-API requires every resource to have a uniqueidfield.
内容的提问来源于stack exchange,提问作者DaveJohnston

