A POST request with media type, "application/hal+json", is issued to a Spring Data Rest endpoint, /api/groupMembers. It contains the following payload:
{
"id":null,
"_links":{
"group":{"href":"http://local:8080/api/groups/7"},
"item":{"href":"http://local:8080/api/items/10689"},
"groupItem":{"href":"http://local:8080/api/items/10689"}
}
}
This is similar to the example provided in the HAL specification. All of these URIs point to existing resources and can be dereferenced in a web browser.
However, these Link Objects do not appear to be assembled correctly by Spring Data Rest on the server:
TRACE 20248 --- [nio-8080-exec-9] .w.s.m.m.a.ServletInvocableHandlerMethod : Arguments: [org.springframework.data.rest.webmvc.RootResourceInformation#15aacfd5, Resource { content: GroupMember(id=null, group=null, item=null, groupItem=null), links: [] }, org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler#1d8f0f1a, application/hal+json]
Why doesn't Spring Data Rest populate these properties (group, item, groupItem) using the Link Objects?
Update
A GET request to /api/GroupMembers/1 produces:
{
"id":1,
"_links":{
"group":{"href":"http://local:8080/api/groups/1"},
"item":{"href":"http://local:8080/api/items/689"},
"groupItem":{"href":"http://local:8080/api/items/689"}
}
}
So, Spring Data Rest can produce/serialize this JSON HAL response but cannot handle (deserialize) the same JSON HAL in a request.
Update 2
The GroupMember entity is:
#Entity
#Data
public class GroupMember {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name="groupId")
private Group group;
#ManyToOne
#JoinColumn(name="itemId")
private Item item;
#ManyToOne
#JoinColumn(name="groupItemId")
private Item groupItem;
}
I believe what you are trying to achieve is something like this:
POST /api/groupMembers
{
"id":null,
"group": "http://local:8080/api/groups/7",
"item": "http://local:8080/api/items/10689",
"groupItem": "http://local:8080/api/items/10689"
}
_links are just metadata
Related
I am using Spring data mongo with azure cosmos. My structure looks like below.
I have an Id field in my collection that is not annotated with #Id. I see both _id and id are in the DB but When I retrieve id field comes with the value is in _id.
#Document(collection = "mycollection")
class MyObject{
private String id;
...
}
public interface MyRepository extends MongoRepository<MyObject, Void> {
}
Used #Field("id") to tell spring data threat this field as is not as _id/pk field for mongo
I'm having a duplicate query when performing a simple query. The files:
SomeClass.java:
#Document(collection = "someCollection")
public class SomeClass {
private String _id;
private String someField;
//...
}
SomeClassRepository.java:
#Repository
public interface SomeClassRepository extends MongoRepository<SomeClass, String> {
}
Service.java:
#Autowired
private SomeClassRepository someClassRepository;
public SomeClass find(String id){
return someClassRepository.findOne(id);
}
application.properties:
logging.level.org.springframework.data.mongodb.core.MongoTemplate=DEBUG
Log file:
14:14:46.514 [qtp1658534033-19] DEBUG o.s.data.mongodb.core.MongoTemplate - findOne using query: { "_id" : "40c23743-afdb-45ca-9231-c467f8e8b320"} fields: null for class: class com.somepackage.SomeClass in collection: someCollection
14:14:46.534 [qtp1658534033-19] DEBUG o.s.data.mongodb.core.MongoTemplate - findOne using query: { "_id" : "40c23743-afdb-45ca-9231-c467f8e8b320"} in db.collection: someDatabase.someCollection
I also tried to:
1) use #Id annotation with a field named "someId"
2) use #Id annotation with a field named "id"
3) use a field named "id" (without #Id annotation)
Unfortunately, I always have two queries to the database.
Anyone knows how to perform a single query?
Thanks!
Its only single query that is sent to database. Your log messages are coming from two different places.
First place : doFindOne method - link; Second place :
FindOneCallback class - link
You can also confirm the logs by looking at db logs. More info here
I'm trying to implement a rest api using RepositoryRestResource and RestTemplate
It all works rather well, except for loading #DBRef's
Consider this data model:
public class Order
{
#Id
String id;
#DBRef
Customer customer;
... other stuff
}
public class Customer
{
#Id
String id;
String name;
...
}
And the following repository (similar one for customer)
#RepositoryRestResource(excerptProjection = OrderSummary.class)
public interface OrderRestRepository extends MongoRepositor<Order,String>{}
The rest api returns the following JSON:
{
"id" : 4,
**other stuff**,
"_links" : {
"self" : {
"href" : "http://localhost:12345/api/orders/4"
},
"customer" : {
"href" : "http://localhost:12345/api/orders/4/customer"
}
}
}
Which if loaded correctly by the resttemplate will create a new Order instance with customer = null
Is it possible to eagerly resolve the customer on the repository end and embed the JSON?
Eagerly resolving dependent entities in this case will raise most probably N+1 database access problem.
I don't think there is a way to do that using default Spring Data REST/Mongo repositories implementation.
Here are some alternatives:
Construct an own custom #RestController method that would access the database and construct desired output
Use Projections to populate fields from related collection, e.g.
#Projection(name = "main", types = Order.class)
public interface OrderProjection {
...
// either
#Value("#{customerRepository.findById(target.customerId)}")
Customer getCustomer();
// or
#Value("#{customerService.getById(target.customerId)}")
Customer getCustomer();
// or
CustomerProjection getCustomer();
}
#Projection(name = "main", types = Customer.class)
public interface CustomerProjection {
...
}
The customerService.getById can employ caching (e.g. using Spring #Cachable annotation) to mitigate the performance penalty of accessing the database additionally for each result set record.
Add redundancy to your data model and store copies of the Customer object fields in the Order collection on creation/update.
This kind of problem arises, in my opinion, because MongoDB doesn't support joining different document collections very well (its "$lookup" operator has significant limitations in comparison to the common SQL JOINs).
MongoDB docs also do not recommend using #DBRef fields unless joining collections hosted in distinct servers:
Unless you have a compelling reason to use DBRefs, use manual references instead.
Here's also a similar question.
I am trying to use nested Mongodb query but it does not work.
It is similar to Spring data mongodb query for subdocument field
But suggestions mentioned there does not work.
Please find my documents below.
#Document
public class Ticket {
#Id
private String id;
#DBRef
#CascadeSave
private Customer customer;
// getters and setters
}
#Document
public class Customer {
#Id
private String id;
private String firstName;
// getters and setters
}
public interface TicketRepository extends MongoRepository<Ticket, String> {
public List<Ticket> findByCustomerFirstName(String firstName);
}
I tried both findByCustomerFirstName and findByCustomer_FirstName but it does not work. Any suggestions ?
These suggestions are right it should work...
Official docs explains it as you did it:http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories.query-methods.query-property-expressions
Property expressions can refer only to a direct property of the
managed entity, as shown in the preceding example. At query creation
time you already make sure that the parsed property is a property of
the managed domain class. However, you can also define constraints by
traversing nested properties. Assume a Person has an Address with a
ZipCode. In that case a method name of
List<Person> findByAddressZipCode(ZipCode zipCode);
creates the
property traversal x.address.zipCode
Just one thing, remove #Document from Customer and try it, Mongodb didn't support join queries (I'm not sure if now it does)... so you're document should be Ticket and it must have a embbebed document Customer as a inner object and not in a different document.
I have a database that created in MongoDB using Robomongo tool. How can I update these data in Web API by its default POST, PUT, DELETE methods in ValuesControllers.cs?
Database name : StudentInfo
Collection Name : Student
{
_id : ObjectId(),
name : "lqbal",
department : "CSE",
phone : "0194949402"
}
Here is an example of the POST Action. WebApi can perform Model binding so it can take content from the body of the POST action and bind it to a c# entity - in this case the Student object.
Here is the content of the Request Body.
{
"name": "lqbal",
"department": "CSEGlobal",
"phone": "0194949402"
}
Also make sure to set the Content-Type of the Reqest to application/json so WebApi can perform the correct model binding.
The code in the POST action then has to look up the student in question in the DB. Once it retrieves the Student object, it can update the values and then save the object back to the DB.
public void Post([FromBody]Student value)
{
var student = ((MongoCollection)collection).AsQueryable<Student>().First(c => c.name == value.name);
student.department = value.department;
student.phone = value.phone;
collection.Save(student);
}
The concept for the PUT action is similar to this POST action except that a new Student object is created.
The DELETE action is straightforward as well is similar to the GET action - you only need to pass the ID of the record to delete in the query string and then just delete it in the action controller.
Here is a link to how to remove a document with the mongo c# driver
Mongo c# Driver - Remove an Existing Document