PUT operation in spring data rest - java

I have the model Person with relations to Account and School models:
#Entity
public class Person {
#GeneratedValue
#Id
private Long id;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#RestResource(exported = false)
private Account account;
#ManyToOne(fetch = FetchType.EAGER)
private School school;
}
The idea - is that account should be editable only with user model. For the school in my requests I'm sending the url something like that:
"school": "http://localhost:8080/schools/2"
It's working ok for POST requests. I'm sending json like that:
{
"account": {
"username": "test",
"email": "testEmail"
},
"school": "http://localhost:8080/schools/2"
}
After that request new person and account are created, for school_id - I have needed value in database.
It's working ok for PATCH requests. I'm sending json like that:
{
"account": {
"username": "new test",
"email": "new testEmail"
},
"school": "http://localhost:8080/schools/1"
}
After that request needed fields are updated in database.
The issue in PUT request. I'm sending json like that:
{
"account": {
"username": "put test",
"email": "put testEmail"
},
"school": "http://localhost:8080/schools/2"
}
The result of this request:
in accounts table I have new row, old account row is not updated and
not deleted;
school is not updated.
I want to have the same behavior for PUT operations as I have for PATCH request. I need to update needed fields by PUT request. I need a help. What should I change?

Related

Hide ManyToOne field in REST API in Spring Boot

I am using Spring Boot with simple REST API on server. I have 2 resources: users and articles. Here is Article class:
#Entity(name = "article")
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String text;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
public User getUser() { // I need this method.
return user;
}
// All other setters and getters.
}
Now, if I fetch some article by its ID using REST API, response looks like:
{
"id": 5,
"text": "Content of article...",
"user": {
"id": 1,
"username": "user#email.com",
"password": "$2a$10$CbsH93d8s5NX6Gx/N5zcwemUJ7YXXjRIQAE2InW9zyHlcTh6zWrua"
}
}
How can I exclude user field from response? If I remove Article.getUser method, everything works fine and response looks like:
{
"id": 5,
"text": "Content of article..."
}
This is desired result. However, I need Article.getUser, because e. g. if someone want delete article, I need check, if author of the request is author of the article, so user cannot delete articles of other users.
You can use #JsonIgnore on like below:
#JsonIgnore
private User user;
The other way is Projection in which you have more control on what should be included in response.

Spring boot/Spring data jpa - how to update related entity?

I have following entities:
#Entity
#Table(name = "profile")
public class Profile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne(cascade = CascadeType.ALL)
private ProfileContacts profileContacts;
...
}
and
#Entity
#Table(name = "profile_contacts")
public class ProfileContacts {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "description")
private String description;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
}
I am trying to update it by sending this JSON with update to REST controller:
{
"id": 1,
"description": "an update",
"profileContacts": {
"firstName": "John",
"lastName": "Doe"
}
}
so in the end it calls
profileRepository.save(profile);
where profileRepository is instance of ProfileRepository class:
public interface ProfileRepository extends JpaRepository<Profile, Long> {
}
which is spring-data-jpa interface.
But each time after such update it updates profile table but adds new row to profile_contacts table (table which corresponds to ProfileContactsentity) instead of updating existing ones.
How can I achieve updating?
As per your JSON structure. Yes it will create new profileContacts entry for every time.
The problem every time while saving profile entity you are passing "id": 1 that means Hibernate can identify the entity by this id value (primary key) but for profileContacts mapping you are not sending the id that's why Hibernate considering it has a new entity every time.
To update your profileContacts entity make sure to pass the id of it.
Example:
{
"id": 1,
"description": "an update",
"profileContacts": {
"id" : yourEntityId
"firstName": "John",
"lastName": "Doe"
}
}
Well, that's the expected behavior.
You're not telling hibernate to update the profileContacts.
For the framework to be able to update it, you need to send the profileContact's primary key - which in your case is the ProfileContacts#id.
Something like this:
{
"id": 1,
"description": "an update",
"profileContacts": {
"id": 1
"firstName": "John",
"lastName": "Doe"
}
}
Need to specify the join column in the parent Entity.
#Entity
#Table(name = "profile")
public class Profile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne(cascade = CascadeType.ALL)
**#JoinColumn(name = "id")** //
private ProfileContacts profileContacts;
...
}
Now when you try to save Profile entity it will save the child entity also.
And also needs to include Id in jason request for child entity also
{
"id": 1,
"description": "an update",
"profileContacts": {
"id": 1,
"firstName": "John",
"lastName": "Doe"
}
}
Ok, I see the problem. As #Matheus Cirillo pointed out, you need to tell the hibernate to update the row.
Now, how do you tell the hibernate to update a row - By providing the primary key of the existing row.
But, creating an object with the primary key set is not enough. You need that entity class to be attached to the entity manager and the persistence context.
You can have something like,
//This attaches the entity to the entity manager
ProfileContacts existingProfileContacts = profileContactRepository.getOne(2);
Profile profile = new Profile();
....
....
profile.setProfileContacts(existingProfileContacts);
profileRepository.save(profile);
I hope this helps.

How to POST oneToMany relational database entries with Spring Boot

How should I structure a JSON POST, the controller on my backend, and the POJO classes?
This is for a twitter clone, so a user can have multiple tweets etc
POST
{
"tweet": "Sew one button, doesn't make u a tailor",
"user": "Alex"
}
Tweet Controller
public Tweet createTweet(#RequestBody Tweet tweet) {
return tweetRepository.save(tweet);
}
Tweet Class
#Table(name = "tweets")
public class Tweet {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(cascade = CascadeType.PERSIST)
private User user;
...
User Class
#Table(name = "custom_user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "tweet_id")
private List<Tweet> tweet = new ArrayList<>();
This is the response I'm getting
{
"id": 1,
"user": {
"id": 2,
"name": "Alex",
"tweet": []
},
"tweet": "Sew one button, doesn't make u a tailor"
}
edit:
If I do a GET on my users endpoint, this is what I get (there should be associated tweets included)
[
{
"id": 2,
"name": "Alex",
"tweet": []
}
]
Here:
{
"id": 1,
"user": {
"id": 2,
"name": "Alex",
"tweet": []
},
"tweet": "Sew one button, doesn't make u a tailor"
}
By the looks of it, your Tweet object has a tweet attribute, and your User object has an array that maps every tweet related to that user, that is currently empty on the example.
It looks to me like a problem on your bidirectional mapping between Tweet and User. Consider using the mappedBy property on bidirectional relationships.

Spring Data REST - "Can only translate enum with property information!" error when PATCH an entity containing a set of enum

I setup the enum traslation in my Spring Data REST project (Spring Boot v.1.5.6):
spring.data.rest.enable-enum-translation=true
One of the entity has an enum set property. When I try to PATCH this entity to increase a number of enum in the set - I've got an error(!):
Could not read payload!; nested exception is java.lang.IllegalStateException: Can only translate enum with property information!
If I update the enum set with the same number of enum (or less) then the entity is updated correctly.
PUT updates an enum set correctly in anyway.
If I turn off the the enum traslation then PATCH works as expected.
Does anybody know how to workaround this issue? Is this a bug?
Code
#Entity
public class User {
private String name;
#Enumerated(EnumType.STRING)
#CollectionTable(name = "roles", joinColumns = #JoinColumn(name = "user_id"))
#ElementCollection
private Set<Role> roles;
// other stuff
}
public enum Role {
ADMIN, USER, POWER_USER
}
resources/rest-messages.properties
# Enums
io.github.cepr0.enumtranslatingissue.Role.ADMIN=Admin
io.github.cepr0.enumtranslatingissue.Role.USER=User
io.github.cepr0.enumtranslatingissue.Role.POWER_USER=Power user
Error demo
GET http://localhost:8080/api/users/1
{
"name": "user1",
"roles": [
"Admin",
"User"
],
"_links": {
"self": {
"href": "http://localhost:8080/api/users/1"
},
"user": {
"href": "http://localhost:8080/api/users/1"
}
}
}
PATCH http://localhost:8080/api/users/1
{
"roles": [
"Admin",
"User",
"Power user"
]
}
{
"cause": {
"cause": null,
"message": "Can only translate enum with property information!"
},
"message": "Could not read payload!; nested exception is java.lang.IllegalStateException: Can only translate enum with property information!"
}
Demo project

Java Rest API - resource/data separation with JPA

i'm currently building a rest-api with Java-EE/Jax-RS/JPA.
I already have a working database + database model and used the eclipse option for creating jpa-entities from existing tables.
I've created some basic services and i'm curious now whether the use of jpa-associations makes sense for building a rest-api or not because it sometimes leads to a big chunk of data getting exposed. I'm also unsure about where to separate the data that is being exposed.
E.g:
table "FOLDER" has an id, name
table "FOLDER_ITEM" has an id, folder_id (fk), item_id (fk)
table "ITEM" has an id, name, itemprop_id(fk)
table "ITEM_PROP" has an id, valueA, valueB, valueC
Calling /folders/1 currently outputs:
{
"id": 1,
"name": "Folder1",
"items": [
{
"id": 1,
"name": "pencil",
"item_prop": {
"id": "1",
"valueA": "example",
"valueB": "example",
"valueC": "example"
}
},...]
}
which adds up to a lot of data if there are a lot of items connected to a folder.
So i thought it maybe simpler and cleaner to separate the data by creating a service /items/{id} which would give me only one item at a time.
But in this case i would also have to create a service for getting the items of a folder. E.g /items/?withFolderId=1
or even /folders/1/items. I see the following options:
1) use jpa-associations but mark the list of items (inside the folder-class) as json-ignored for /folders/1 and force the output of the items when calling /folders/1/items.
2) write queries on my own
in case of the latter i'm asking my self "why would i even use jpa at all?"
While beeing confused by this i might also have to say that all my tables have a foreign key to a user-id.
So i usually only want to get the folders of a specific user (the user who is currently logged in) by creating a service /users/1/folders and at this point where would i separate the data ?
I could serve my entire client from the /users/ endpoint which would lead to the same problem as above. And now we can definitely talk about a big chunk of data getting exposed, depending on how much folders a user has and how much items the folders contain.
{
"id":1
"name":"testuser"
"password":"PW"
"folders":
[{
"id": 1,
"name": "Folder1",
"items": [
{
"id": 1,
"name": "pencil",
"item_prop": {
"id": "1",
"valueA": "example",
"valueB": "example",
"valueC": "example"
}
},....]
},....]
}
I feel like i'm distracting myself a lot with this problem. Are there any suggestions or common ways to solve this problem ?
if you are using jackson library then you can mention #JsonIgnore to avoid json response. Following Example code.
#Entity
#Table(name="address")
#EqualsAndHashCode(exclude={"id","companies","clientDetails"})
#Getter
#Setter
#NoArgsConstructor
#ToString(exclude = {"companies","clientDetails"})
#JsonIgnoreProperties(ignoreUnknown = true)
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(unique=true, nullable=false)
private Integer id;
#Column(name="address_line1")
private String addressLine1;
#Column(name="address_line2")
private String addressLine2;
#Column
private String city;
#Column
private String country;
#Column(name="phone_no")
private String phoneNo;
#Column(name="postal_code")
private String postalCode;
#Column
private String state;
//bi-directional many-to-one association to ClientDetail
#OneToMany(mappedBy="addressBean",fetch=FetchType.LAZY)
#JsonIgnore
private Set<ClientDetail> clientDetails;
//bi-directional many-to-one association to Company
#OneToMany(mappedBy="address", fetch=FetchType.LAZY)
#JsonIgnore
private Set<Company> companies;
}

Categories

Resources