Bidirectional OneToOne relationship in JPA-RS not traversable - jpa

I use the simple entities below for exploring jpa-rs:
#Entity
public class Employee {
#Id
#GeneratedValue
private int id;
private String name;
#OneToOne
private Address address;
}
#Entity
public class Address {
#Id
#GeneratedValue
private int id;
private String street;
private String city;
#OneToOne(mappedBy = "address")
private Employee employee;
}
Creating an Employee entity with an Address relation works like a charm:
{
"name": "John Doe",
"address": {
"street": "Street No. 1",
"city": "A City"
}
}
But i cannot travers the resulting objects by the _links, like HATEOAS proposes.
The provided students example in the Eclipselink wiki (http://wiki.eclipse.org/EclipseLink/Examples) allows this easily.
As far as i understand, i should be able to do the following:
fetch the employee with id 1: GET url/entity/Employee/1: {
"id": 1,
"name": "John Doe",
"_relationships": [
{
"_link": {
"href": "url/entity/Employee/1/address",
"rel": "address"
}
}
],
"address": {
"_link": {
"href": "url/entity/Address/2",
"method": "GET",
"rel": "self"
}
},
"staff": []
}
fetch the address of the employee: GET url/entity/Address/2:{
"city": "A City",
"id": 2,
"street": "Street No. 1",
"_relationships": [
{
"_link": {
"href": "url/entity/Address/2/employee",
"rel": "employee"
}
}
]
}
Now lets get back to the employee from here: GET url/entity/Address/2/employee -> http status 400
In the mentioned example i can flawlessly navigate using all the links for the relationship.
p.s.: I'd like to tag this JPA-RS, but i don't have enough reputation to create a new tag.

Related

How to get only single embedded document of an array field in spring data mongodb?

I'm trying to get single field using query. I want to exclude embedded field("necessary") in collection. I'm using springdata and mongodb now.
this is query to get spec without necessary field.
#GetMapping("/selectSpec/{spec_id}") // Select Spec without necessary In Inventory by Spec_id
public Inventory selectSpec(#PathVariable String spec_id){
Query query = new Query();
query.addCriteria(Criteria.where("spec").elemMatch(Criteria.where("_id").is(spec_id)));
query.fields().exclude("spec.necessary").exclude("category").exclude("name").exclude("inventory_id");
return mongoOperations.findOne(query, Inventory.class);
}
I expected query return only one which spec_is is same as I request on postman but it doesn't
{
"spec": [
{
"spec_id": "5",
"parent_id": "2",
"material": "plastic",
"size": "L"
},
{
"spec_id": "6",
"parent_id": "2",
"material": "wood",
"size": "XL"
}
]
}
I expect when I request to get spec_id is 5
{
"spec": [
{
"spec_id": "5",
"parent_id": "2",
"material": "plastic",
"size": "L"
}
this is my inventory collection.
#Document(collection = "inventory")
public class Inventory {
#Id
#Field("inventory_id")
private String inventory_id;
#Field("name")
private String name;
#Field("category")
private List<String> category;
#Field("spec")
private List<Spec> spec;
I tried many ways but I'm still strugging with this problem about weak. please guide, Thank you in advance.

ADD self link for item in Custom ResponseHandler

I have the following ResponseHandler to override a Data Rest Response Handlers. Instead of returning all data, the handler returns only data for the owner (logged in user- currently hardcoded).
package com.osde.prepo.controller;
import com.osde.prepo.entity.Company;
import com.osde.prepo.repository.CompanyRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.hateoas.CollectionModel;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
/**
* customer controller to override some Data Rest Response Handlers
*/
#RepositoryRestController
public class CompanyController {
Logger logger = LoggerFactory.getLogger(CompanyController.class);
private final CompanyRepository companyRepository;
#Autowired
public CompanyController(CompanyRepository repo) {
companyRepository = repo;
}
#RequestMapping(method = RequestMethod.GET, value = "/companies")
public #ResponseBody
ResponseEntity<?> getAllCompaniesForCurrentUser() {
logger.info("custom implementation for get called!!");
List<Company> companies = new ArrayList<>();
companies = companyRepository.findByOwnerId("google-oauth2|107634743108791790006");
// convert to HATEOAS
CollectionModel<Company> resources = CollectionModel.of(companies);
resources.add(linkTo(methodOn(CompanyController.class)
.getAllCompaniesForCurrentUser())
.withSelfRel());
return ResponseEntity.ok(resources);
}
}
I get the following response, when consuming the API:
{
"_embedded": {
"companies": [
{
"ownerId": "google-oauth2|107634743108791790006",
"name": "Company 1",
"city": "Ort 1",
"country": "Germany",
"profile": "We are 1 ...",
"logoUrl": null
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/companies"
}
}
}
The original API has the following response (including self-links to the items) How do I have to change my code to get this response (paging is not of interest so far!)?
{
"_embedded": {
"companies": [
{
"ownerId": "google-oauth2|107634743108791790006",
"name": "Company 1",
"city": "Ort 1",
"country": "Germany",
"profile": "We are 1 ...",
"logoUrl": null,
"_links": {
"self": {
"href": "http://localhost:8080/api/companies/1"
},
"company": {
"href": "http://localhost:8080/api/companies/1"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/companies/"
},
"profile": {
"href": "http://localhost:8080/api/profile/companies"
},
"search": {
"href": "http://localhost:8080/api/companies/search"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
Here is a sample code to achieve this. I have assumed that your Entity has a getter for id as getId().It can be changed as per the code.
There is a need to extend this RepresentationModel on the entity class to get the add method.
eg
public class Company extends RepresentationModel<Company> implements Serializable {
#RequestMapping(method = RequestMethod.GET, value = "/companies")
public #ResponseBody
ResponseEntity<?> getAllCompaniesForCurrentUser() {
logger.info("custom implementation for get called!!");
List<Company> companies = companyRepository.findByOwnerId("google-oauth2|107634743108791790006");
for (Company company : companies) {
Link selfLink = linkTo(methodOn(CompanyController.class)
.getCompaniesById(company.getId())).withSelfRel();
company.add(selfLink);
}
Link link = linkTo(methodOn(CompanyController.class).getAllCompaniesForCurrentUser()).withSelfRel();
CollectionModel<Company> result = CollectionModel.of(companies, link);
return ResponseEntity.ok().body(companies);
}
There is an another way to do this as well. This won't require any change on the Entity class.
import org.springframework.hateoas.EntityModel;
#RequestMapping(method = RequestMethod.GET, value = "/companies")
public #ResponseBody
ResponseEntity<?> getAllCompaniesForCurrentUser() {
logger.info("custom implementation for get called!!");
List<EntityModel<Company>> companies = companyRepository
.findByOwnerId("google-oauth2|107634743108791790006")
.stream()
.map(this::generateLinks)
.collect(Collectors.toList());
CollectionModel<EntityModel<Company>> resource = CollectionModel.of(companies);
resource.add(entityLinks.linkToCollectionResource(Company.class));
resource.add(entityLinks.linksToSearchResources(Company.class));
return ResponseEntity.ok().body(resource);
}
private EntityModel<Company> generateLinks(Company company) {
EntityModel<Company> resource = EntityModel.of(company);
resource.add(entityLinks.linkToItemResource(Company.class, company.getId()).withSelfRel());
resource.add(entityLinks.linkToCollectionResource(Company.class));
resource.add(entityLinks.linksToSearchResources(Company.class));
return resource;
}
That points to the correct direction!
Now I get the following output:
[
{
"companyId": 1,
"ownerId": "google-oauth2|107634743108791790006",
"name": "Company 1",
"city": "Ort 1",
"country": "Germany",
"profile": "We are 1 ...",
"logoUrl": null,
"links": [
{
"rel": "self",
"href": "http://localhost:8080/api/companies/1"
},
{
"rel": "companies",
"href": "http://localhost:8080/api/companies{?page,size,sort}"
}
]
}
]
How to adjust the following aspects:
link to companies should be on the collection-level and just be http://localhost:8080/api/companies
rename links to _links

How to combine child entities in to single _embedded array when using InheritanceType.JOINED from JPA and Spring Data Rest within Spring Boot?

I am using Spring Data JPA and Spring Data Rest with Spring Boot 1.5.10. I have three classes annotated with #Entity: Message, AMessage, and BMessage (Figure 2, 3, 4). Classes AMessage and BMessage extend Message class (Figure 1). I have one MessageRepository that is exposed via Spring Data Rest. When I make a request to get all messages at http://host:port/messages, I get a response that contains two separate arrays under _embedded object (one for AMessage and one for BMessage) even though I am retrieving data from the /messages endpoint (Figure 5). I only want to retrieve columns from the Message Entity. How can this be achieved?
I have uploaded my code to github.com
Figure 1: Hierarchy
Message
|
----------------------
| |
AMessage BMessage
Figure 2: Message Class (Parent)
import javax.persistence.*;
#Entity
#Table(name = "MSG")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "TYPE")
public class Message {
#Id #GeneratedValue
private Long id;
private String messageColumn1;
#Column(name = "TYPE", updatable = false, insertable = false)
private String messageType;
public String getMessageColumn1() {
return messageColumn1;
}
public void setMessageColumn1(String messageColumn1) {
this.messageColumn1 = messageColumn1;
}
public String getMessageType() {
return messageType;
}
}
Figure 3: AMessage Class (Child)
import javax.persistence.*;
#Entity
#Table(name = "MSG_A")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue("A")
public class AMessage extends Message {
private String messageAColumn1;
public String getMessageAColumn1() {
return messageAColumn1;
}
public void setMessageAColumn1(String messageAColumn1) {
this.messageAColumn1 = messageAColumn1;
}
}
Figure 4: BMessage Class (Child)
import javax.persistence.*;
#Entity
#Table(name = "MSG_B")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue("B")
public class BMessage extends Message {
private String messageBColumn1;
public String getMessageBColumn1() {
return messageBColumn1;
}
public void setMessageBColumn1(String messageBColumn1) {
this.messageBColumn1 = messageBColumn1;
}
}
Figure 5: Get messages response (aMessages and bMessages separated)
http://localhost:8080/messages
{
"_embedded": {
"aMessages": [
{
"messageColumn1": "MColumn1",
"messageType": "A",
"messageAColumn1": "AColumn1",
"_links": {
"self": {
"href": "http://localhost:8080/aMessage/1"
},
"aMessage": {
"href": "http://localhost:8080/aMessage/1"
}
}
}
],
"bMessages": [
{
"messageColumn1": "MColumn1",
"messageType": "B",
"messageBColumn1": "BColumn1",
"_links": {
"self": {
"href": "http://localhost:8080/bMessage/2"
},
"bMessage": {
"href": "http://localhost:8080/bMessage/2"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/messages{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile/messages"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
Resolved! Created new repositories for AMessage and BMessage. Annotated each repository with #RepositoryRestResource(collectionResourceRel = "messages"). This caused the messages to be combined into a single array under the _embedded object, when performing a GET on http://localhost:8080/messages
{
"_embedded": {
"messages": [
{
"messageColumn1": "MColumn1",
"messageType": "A",
"messageAColumn1": "AColumn1",
"_links": {
"self": {
"href": "http://localhost:8080/aMessages/1"
},
"aMessage": {
"href": "http://localhost:8080/aMessages/1"
}
}
},
{
"messageColumn1": "MColumn1",
"messageType": "B",
"messageBColumn1": "BColumn1",
"_links": {
"self": {
"href": "http://localhost:8080/bMessages/2"
},
"aMessages": {
"href": "http://localhost:8080/bMessages/2"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/messages{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile/messages"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}

How to retrieve sub array of a json element?

Below is the Employee class
public class Employee {
public List<Department> departments;
public List<Association> association;
....
}
The data is successfully inserted after population for the above class.
MongoCollection.insert(entity);
On retrieving Employee object via Jongo. It returns only the Employee and not departments.
MongoCollection.findOne("{name: '"+name+"'}").as(Employee.class);
sample json file.
{
"_id": ObjectId("577a32894309be1f540cde3e"),
"active": false,
"departments": [
{
"_id": ObjectId("577a32894309be1f540cde3f"),
"description": "sales",
"name": "sales"
}
],
"email": "dinesh.rajul#gmail.com",
"firstName": "dinesh",
"lastName": "regjuk",
"name": "admin",
"password": "hsCIFfVifdOLG3vOUF7jF3x7bEiRCItm7FPiYAVdMW"
}
Could you please help me how to query or get the sub array values ("departments") of the json as well? Currently the list returns null when we say employee.getDeparements()
Thanks

DataContract class for Facebook 'Like' Graph API

So I'm trying to decode the json returned by the Facebook Graph API for user likes into objects using the DataContractJsonSerializer. I've tried creating a couple different [DataContract] classes now but with no luck.
What is the correct way to create a [DataContract] class for this data.
{
"data": [
{
"name": "Earlyups",
"category": "Recreation/sports",
"id": "170410902986097",
"created_time": "2012-04-09T03:34:11+0000"
},
{
"name": "pictureline inc.",
"category": "Local business",
"id": "28861182380",
"created_time": "2012-03-28T02:02:24+0000"
},
{
"name": "Jackson Hole Mountain Resort",
"category": "Sports/recreation/activities",
"id": "24718308041",
"created_time": "2012-03-20T16:10:45+0000"
}
],
"paging": {
"next": "next_page_request_url"
}
}
haha, wow as soon as I posted I saw what I was doing wrong. In case anyone else is interested.
[DataContract]
internal class LikePage
{
[DataMember]
internal Like[] data;
[DataMember]
internal Paging paging;
}
[DataContract]
internal class Like
{
[DataMember]
internal string name;
[DataMember]
internal string category;
[DataMember]
internal string id;
[DataMember]
internal string created_time;
}
[DataContract]
internal class Paging
{
[DataMember]
internal string next;
}