Problems with the relationship of one to many - java

I have problems with the relationship of one to many, to relate I just want to use the user ID I do not want to create a new one, I put my code
#PostMapping("/createFavorite")
public ResponseEntity<Favorite> createFavorite(#RequestBody Favorite favorite) {
return new ResponseEntity<Favorite>(userServiceImpl.createFavorite(favorite), HttpStatus.OK);
}
my entity Favorite is
public class Favorite implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "element")
private String element;
#ManyToOne
#JoinColumn(name = "user_fk")
User user;
in swagger i get this
{
"element": "string",
"user": {
"email": "string",
"id": 0,
"life": "suspend",
"name": "string",
"password": "string"
}
}
i do not want to create new user,
The user is already created, I just need to use your ID to reference it

You need to create a new user or retrieve any existing user and set in favorite object. If in favorite.user.id is present, it won't create a new user.
Even you should set Cascade to CascadeType.DETACH to avoid creation or updation of User object from favorite object. It will throw error if favorite.user.id is null instead of creating a new user object.
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "user_fk")
User user;

Your entity structure is fine. You have to retrieve the user from UserRepository and assign it in the Favorite entity. IMHO, please introduce FavoriteDTO class. So entity models will not be exposed.
FavoriteDTO.java
public class FavoriteDTO implements Serializable {
private long id;
private String element;
long userId;
//getters and setters
}
Your service implementation should check the user availability and then assign it to the Favorite entity.
ServiceImpl.java
public void createFavorite(FavoriteDTO favoriteDTO) {
Optional<User> optional = userRepository.findById(favoriteDTO.getUserId());
if(optional.isPresent()) {
Favorite favorite = new Favorite();
favorite.setUser(optional.get());
favoriteRepository.save(favorite);
}
}
Your updated swagger request json like below
{
"element": "string",
"userId": long
}

You need to retrieve your existing user and put it there as the favorite.user object (assign it). That way, it will work.
I am not very clear with your swagger specification. Are you using some kind of json schema?

Related

Self join to JSON in Spring Boot rest

I have an Account object containing a OneToMany relation with Beneficiary object and this relationship is bi-directional so I have a ManyToOne relation in the Beneficiary Object with Account Object
public class Account {
#Id
#GeneratedValue
private Long id;
private String name;
private String number;
//Other fields
#OneToMany(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private List<Beneficiary> beneficiaries = new ArrayList<>();
}
public class Beneficiary {
#Id
#GeneratedValue
private Long id;
//Other fields
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "account_id")
#OnDelete(action = OnDeleteAction.CASCADE)
private Account account;
}
In the JSON response, I need the Account information containing the list of Beneficiaries and for each Beneficiary I just need the Account name and Account number. Is it possible to serialize it somehow so that I get response in this fashion? Or do I need to modify my entity structures?
Sample Account Response -
{
"id": 123,
"name": "Name1",
"number": "111111",
"beneficiaries": [
{
"id": 1,
"account": {
"name": "Name2",
"number": "222222"
}
},
{
"id": 2,
"account": {
"name": "Name3",
"number": "333333"
}
}
]
}
You are not supposed to serialize your JPA objects. Instead, you need to define domain objects. These are objects are the ones to be serialize and exposed to the business. This abstraction decouples your REST or SOAP or whatever interface with your JPA layer.
I would create a domain class for your account class. Call it AccountDTO or something like that. Any object being returned from your JPA repositories need to be mapped to this DTO objects and bubbled up to the services layer. Then your DTO is the class which models your business needs. In there you can just put the accounts and the beneficiaries names.
DTO stands for Data Transfer Objects. These are the ones supposed to be serialized and sent between systems.
One idea would be to use a custom serializer.
You would have to write a custom serializer, similar to this:
public class NestedAccountSerializer extends StdSerializer<Account> {
public NestedAccountSerializer() {
this(null);
}
public NestedAccountSerializer(Class<Account> t) {
super(t);
}
#Override
public void serialize(Account account, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeObject(new AccountView(account.getName(), account.getNumber()));
}
private static class AccountView {
#JsonProperty
private final String name;
#JsonProperty
private final String number;
AccountView(String name, String number) {
this.name = name;
this.number = number;
}
}
}
And then use it like this in your Beneficiary class:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "account_id")
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonSerialize(using = NestedAccountSerializer.class)
private Account account;
Please, let me know if it helped.

Parent Entity is not populated in #OneToMany. Hibernate Bidirectional

hi newbie to Hibernate,
My entity classes
#Entity
public class User {
#Id
#GeneratedValue//How to restrcit by passing id from response
#JsonIgnore
private Integer userId;
#OneToMany(mappedBy = "user")
private List<PostEntity> postEntity;
}
#Entity
#Table(name="post")
public class PostEntity {
#Id
#GeneratedValue
#JsonIgnore
#ApiModelProperty(required=false)
private Integer id;
#ManyToOne(fetch=FetchType.LAZY)
private User user;
}
When I fetch User its populating Post entity as well.
URI: http://localhost:8080/jpa/users
[
{
"name": "Abdul",
"birthDate": "2018-07-25T01:29:51.895+0000",
"postEntity": [
{
"description": "My Fourth Post"
},
{
"description": "My First Post"
},
{
"description": "My Second Post"
}
]
},
{
"name": "Anji",
"birthDate": "2018-07-25T01:29:51.903+0000",
"postEntity": []
},
{
"name": "Naren",
"birthDate": "2018-07-25T01:29:51.903+0000",
"postEntity": []
}
]
but this is not the case in reverse. When I fetch post its skipping User entity.
URI: localhost:8080/jpa/users/101/posts/11001
Response:
{
"description": "My First Post"
}
Why its not populating user information in the above JSON response.
Fetching Methods:
User:
#GetMapping("/jpa/users")
public List<User> retAll(){
return userRepository.findAll();
}
Post:
#GetMapping("/jpa/users/{uid}/posts/{pid}")
public Resource<PostEntity> postE(#PathVariable int uid,#PathVariable int pid) {
Optional<PostEntity> post = postRepository.findById(pid);
if (!post.isPresent()) {
throw new UserNotFoundException("POst");
}
PostEntity ePost = post.get();
Resource<PostEntity> resource = new Resource<PostEntity>(ePost);
return resource;
}
Please help.
That is actually the intended way REST is supposed to work.
GET at /users : all users
GET at /users/1 : information of user 1 and all its children
GET at /users/1/posts : all posts of user 1
GET at /users/1/posts/10 : information of post 10 and all its children from user 1
As you're calling /users/101/posts/11001, the endpoint will give you the information of one post (id 11001) from one user (id 101).
There are two common ways to get the parent information:
The fastest way would be to just call /users and filter for your desired post in the frontend.
The right way would be changing the model of your post (PostEntity.java) to contain its "parent" User object, so when you make a REST call for a post, the user object gets populated.
Further reading:
https://softwareengineering.stackexchange.com/questions/274998/nested-rest-urls-and-parent-id-which-is-better-design
Maybe it's a good idea to read some REST best practices:
https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9
Try to use FetchType
#Entity
public class User {
#Id
#GeneratedValue//How to restrcit by passing id from response
#JsonIgnore
private Integer userId;
#OneToMany(mappedBy = "user", fetch=FetchType.EAGER)
private List<PostEntity> postEntity;
}
Beware of the performance.

JPA all referenced relations are retrieved (ManyToMany - ManyToOne - OneToMany)

Working on this 'twitter' application where a user can have posts #OneToMany and can have followers #ManyToMany.
While retrieving a user all it's posts and followers get retrieved as well.
This is all correct but it's also retrieving every 'poster' for each post (which is the user itself) and for each follower, all it's posts and followers.
I can't figure out how to limit this to the user itself.
User
#Entity
#Table(name = "User")
#NamedQueries({
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
#NamedQuery(
name = "User.auth",
query = "SELECT u FROM User u WHERE u.username = :username AND u.password = :password"
)
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true, nullable = false)
private String username;
#Column(nullable = false)
private String password;
#ManyToMany
#OneToMany(mappedBy = "poster", cascade = CascadeType.ALL)
private List<Post> posts = new ArrayList<>();
#JoinTable(name = "Followers",
joinColumns = {
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
},
inverseJoinColumns = {
#JoinColumn(name = "FOLLOWER_ID", referencedColumnName = "ID")
}
)
private List<User> followers = new ArrayList<>();
.... constructor, getters and setters
Post
#Entity
#Table(name = "Post")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
#ManyToOne
private User poster;
.... constructor, getters and setters
Outcome I get vs what I want
{
"id": 1,
"username": "jim",
"posts": [
{
"id": 1,
"content": "Post 1 by jim",
"poster": {
// ^ this is the user itself (I don't need this one)
"id": 1,
"username": "jim",
"posts": [
// keeps recurse
]
}
}
],
"followers": [
{
"id": 2,
"username": "follower1",
"posts": [
{
"id": 4,
"content": "Post 2 by follower 1",
"poster": {
// ^ this is the follower itself (I don't need this one)
"id": 2,
"username": "follower1",
"posts": [
// Same issue
]
}
}
],
"followers": [], // <-- I don't need this one either
}
]
}
Well it's pretty clear that fetching one user fill keeps fetching all it's relations which are recursive.
Is this a designer's fault or can this be ignored/limited?
Note: I am using Gson to serialise objects to JSON format
Update
Tried to use:
#ManyToOne(fetch = FetchType.LAZY)
private User poster;
Which works but still gets the following extra prop in JSONso not sure if this is a neath solution:
"_persistence_poster_vh": {
"sourceAttributeName": "poster",
"isInstantiated": false,
"row": {
"Post.ID": 3,
"Post.CONTENT": "Post 3 by jim",
"Post.DATETIME": "2018-01-22",
"Post.POSTER_ID": 1
},
"isCoordinatedWithProperty": false
}
And
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
...
)
private List<User> followers = new ArrayList<>();
Which still returns all followers (which I want!) I just don't want the followers.followers and followers.posts..
Best guess: it’s not actually fetching these objects until you try to dereference them.
Be default, JPA will eager fetch #OneToOne and #OneToMany relations, but not #ManyToOne or #ManyToMany. What happens is that when you reference these fields, it will then go and fetch the actual contents of the list.
How can you tell this is happening? Check the list’s class using getFollowers().getClass()
What you see won’t be a LinkedList or an ArrayList but a class from your JPA provider, probably with “Lazy” somewhere in the name. When you call Size or Get on the list, it will perform the fetch.
You can set OneToOne and OneToMany relations to be lazy as well, and use EntityGraphs to determine what entities you want to eagerly fetch as well. JPA has a number of gotchas like this.
I’ve seen GSON mentioned, and just a warning: it won’t be aware of the lazy loading lists, so you MUST tell It to avoid the properties you don’t want it to follow.
Typically with JSON marshaling, you’ll want it to ignore the parent object, so in Post, User should be ignored for example. Additionally links to same types should typically be ignored (followers) or else mapped specially, such that it doesn’t Marshall the entire object, but only produces an array of usernames. You can tell it to ignore the actual followers field, and have it marshal a getter which returns an array of usernames to implement this.
You can specify fetch=FetchType.LAZY in the annotation you don't want to fetch immediately. The downside is, that if you need the data later you have to access it in the scope of the still open session.
There are two ways to handle this -
You can either use #JsonIgnoreProperties(ignoreUnknown=true) anotation on attributes you want to skip while serializing the object.
Or you change your FetchType to FetchType.LAZY so that you can get the required data on need basis while preparing your JSON , rather than getting all records at once.

Spring-data-rest jacksonHttpMessageConverter doesn't convert nested entity [duplicate]

My spring-data-rest integration test fails for a simple json request. Consider the below jpa models
Order.java
public class Order {
#Id #GeneratedValue//
private Long id;
#ManyToOne(fetch = FetchType.LAZY)//
private Person creator;
private String type;
public Order(Person creator) {
this.creator = creator;
}
// getters and setters
}
Person.java
ic class Person {
#Id #GeneratedValue private Long id;
#Description("A person's first name") //
private String firstName;
#Description("A person's last name") //
private String lastName;
#Description("A person's siblings") //
#ManyToMany //
private List<Person> siblings = new ArrayList<Person>();
#ManyToOne //
private Person father;
#Description("Timestamp this person object was created") //
private Date created;
#JsonIgnore //
private int age;
private int height, weight;
private Gender gender;
// ... getters and setters
}
In my test I created a person by using personRepository and inited order by passing person
Person creator = new Person();
creator.setFirstName("Joe");
creator.setLastName("Keith");
created.setCreated(new Date());
created.setAge("30");
creator = personRepository.save(creator);
Order order = new Order(creator);
String orderJson = new ObjectMapper().writeValueAsString(order);
mockMvc.perform(post("/orders").content(orderJson).andDoPrint());
Order is created but creator is not associated with the order. Also I want to pass request body as a json object. In this my json object should contain creator as follows
{
"type": "1",
"creator": {
"id": 1,
"firstName": "Joe",
"lastName": "Keith",
"age": 30
}
}
If I send request body with the following json, the call works fine
{
"type": "1",
"creator": "http://localhost/people/1"
}
But I don't want to send the second json. Any idea how to solve the issue. Because already my client is consuming the server response by sending first json. Now I migrated my server to use spring-data-rest. After that all my client code is not working.
How to solve this?
You are correctly associating order with the creator, however the Person is not associated with the orders. You are missing the List<Order> orders field in Person class. Add this, add annotations, add methods for adding order to person and then before sending JSON you should call something like this:
creator.addOrder(order);
order.setCreator(cretr);
Did you try using cascade = CascadeType.ALL in #ManyToOne annotation
public class Order {
#Id #GeneratedValue//
private Long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)//
private Person creator;
private String type;
public Order(Person creator) {
this.creator = creator;
}
// getters and setters
}
Both your Order and Person classes should implement Serializable to properly break them down into and rebuild them from JSON.
There are some ways to solve your problem, but I want give you a hint. You just can save only "id" of your person and get the person by "id" from your database, when you need this.
It solves your problem and it also saves the memory.
I believe you need to do two things to get this work.
Handle the deserialization properly. As you expect Jackson to populate the nested Person object via the constructor you need to annotate this with #JsonCreator. See here:
http://www.cowtowncoder.com/blog/archives/2011/07/entry_457.html
One of more powerful features of Jackson is its ability to use arbitrary >constructors for creating POJO instances, by indicating constructor to use with
#JsonCreator annotation
...........................................
Property-based creators are typically used to pass one or more
obligatory parameters into constructor (either directly or via factory
method). If a property is not found from JSON, null is passed instead
(or, in case of primitives, so-called default value; 0 for ints and so
on).
See also here on why Jackson may not be able to automatically work this out.
https://stackoverflow.com/a/22013603/1356423
Update your JPA mappings. If the associated Person is now populated correctly by the Jackson deserializer then by adding the necessary JPA cascade options to the relationship then both instances should be persisted.
I think then the following should work as expected:
public class Order {
#Id
#GeneratedValue(...)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = cascadeType.ALL)
private Person creator;
private String type;
#JsonCreator
public Order(#JsonProperty("creator") Person creator) {
this.creator = creator;
}
}

JAX-RS RESTful API working with JSON representations and linking resources

Sometimes it's confusing how I should link resources within a RESTful API, consider for example the entities:
Profile (Users can create business profiles with address, details, etc..)
Plan (Already persisted in app's DB, created by administrators)
The request to create a Profile looks like:
POST /profiles
{
"name": "Business name",
"address": "The address",
"phone": "0000000000"
}
Now it is required that a Profile belongs to a Pricing Plan. So is it a good idea to do POST request like this with JSON?
POST /profiles
{
"name": "Business name",
"address": "The address",
"phone": "0000000000"
"plan": {
"id": 1
}
}
and then load the plan by the provided id and associate it with the profile being created:
#POST
#Path("/profiles")
public Response createProfile(Profile profile) {
// load plan resource from DB
Plan plan = em.find(Plan.class, profile.getPlan().getId())
// associate
profile.setPlan(plan);
// persist profile
em.perist(profile);
}
The Profile entity:
#Entity
public class Profile implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "plan_id", nullable = false)
private Plan plan;
private String name
...
// getters and setters
}
The Plan entity:
#Entity
public class Plan implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(nullable = false)
private String name;
#NotNull
#Column(nullable = false, columnDefinition = "text")
private String description;
#NotNull
#Column(nullable = false, precision = 8, scale = 2)
private BigDecimal price;
#NotNull
#Column(nullable = false)
private Integer days;
#OneToMany(fetch = FetchType.LAZY, mappedBy="plan", cascade = CascadeType.ALL)
private List<Profile> profiles;
...
}
In other words i am asking what I should pass to the request body in order to link a reference entity.
I would like to believe that something like this is more reasonable:
POST /plans/1/profiles
but according to the REST and JSON semantics what would be the best option?
I can also think of other ways such as providing the Plan id as a query param:
POST /profiles?planId=1
I would say you need to do the following:
Create profile with
POST: /profile
Assign plan with
PUT: /profile/<profile_id>
{
"name": <optional>,
"plan_id": <optional>,
...
}
First thing is you separate create and update (POST/PUT). Another is you state profile ID for update in URL.
You can set parameters you need to update in PUT request body and update only parameters which are set. Think it's fine with REST concept.

Categories