Spring Boot getting empty _embedded array for related entity - spring

Using Spring Boot I'm Having the following abbreviated structure of entities:
#Entity
#Table(name = "item")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false)
protected Long id;
...
}
#Entity
#Table(name = "book")
public class Book extends Item implements Serializable {
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "item_author", joinColumns = #JoinColumn(name = "item_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "author_id", referencedColumnName = "id"))
private Set<Author> authors;
}
#Entity
#Table(name = "author")
public class Author implements Serializable {
#Id
#GeneratedValue
private Long id;
#ManyToMany(mappedBy="authors")
private List<Book> books = new ArrayList<Book>();
private String name;
}
My DAOs are just plain simple RestResource interfaces for all entities, like:
#RestResource(path="items", rel="items")
public interface ItemDao extends CrudRepository<Item, Long> {
}
When I query an entity by id, it is all good
GET > http://localhost:8080/shelfventory/authors/1
{
"name" : "Jhonny Cash",
"used" : true,
"_links" : {
"self" : {
"href" : "http://localhost:8080/shelfventory/authors/1"
},
"author" : {
"href" : "http://localhost:8080/shelfventory/authors/1"
},
"books" : {
"href" : "http://localhost:8080/shelfventory/authors/1/books"
}
}
}
But when I try to follow the links for a related object I just get an empty embedded:
GET > http://localhost:8080/shelfventory/authors/1/books
{
"_embedded" : {
"books" : [ ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/shelfventory/authors/1/books"
}
}
}
What am I doing wrong, how to solve it?

Consider adding these two properties to your application.properties to keep your #Entity and schema in sync:
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=true

Related

Expose Association in Entity via Rest in Spring Boot

I'm new in Spring Boot and a would like to add an association to the HTTP-Request.
Example:
// Transaction.java
#Entity
public class Transaction extends RepresentationModel<Transaction> {
// id;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Tag> tags;
}
// Tag.java
#Entity
public class Tag extends RepresentationModel<Transaction> {
// id;
// name..., and so on
}
Now I have two Repositories:
public interface TagRepository extends JpaRepository<Tag, Long> {
}
public interface TransactionRepository extends JpaRepository<Transaction, Long> {
}
When calling GET http://localhost:8080/transactions
I get:
{
"_embedded": {
"transactions": [
{
"date": "2021-01-01T01:01:11.000+00:00",
"and so on": "more fields" // but no "tags": [...]
"_links": {
"self": {"href": "http://localhost:8080/transactions/1"},
"tags": {"href": "http://localhost:8080/transactions/1/tags"}
}
}
]
},
"_links": {...},
"page": {...}
}
Is there any way to Modify the default Spring Endpoints to return an array of tags?
I would like to bypass create a custom Controller for this, because i need some parameters and filter methods.
The reason I need this function is, that I have a page which will load about 1k transactions and I don't want do make n+1 requests on GET /transactions/n/tags
Thanks for Help.
You need to add the join table annotation like this:
// Transaction.java
#Entity
public class Transaction extends RepresentationModel<Transaction> {
// id;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(
name = "tag_transaction",
joinColumns = #JoinColumn(name = "transaction_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "id"))
private List<Tag> tags;
}
// Tag.java
#Entity
public class Tag extends RepresentationModel<Transaction> {
// id;
// name..., and so on
}

Ignore property in nested object of the same type with Spring Boot

I have one class which is entity and use the same class as a property:
#Entity
public class Employee {
private String name;
#OneToOne
#JoinColumn(name = "supervisor_id", referencedColumnName = "id")
private Employee supervisor;
//getters and setters
}
I want to get the supervisor of an employee, but not the supervisor of the supervisor. Can I manage this somehow?
{
"name": "PersonName",
"supervisor": {
"name": "Supervisor name",
"supervisor": null // i don't want this one
}
}
In the end I just used nested classes for both - dto and entity with fields needed.
True that I duplicated properties of the classes, but at least it's clean and simple.
Just ignore the nulls in the Json. The following works for me:
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import javax.persistence.*;
#Getter
#Setter
#ToString
#Entity
#JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "supervisor_id", referencedColumnName = "id")
private Employee supervisor;
}
Here's the test
#Test
public void test2() throws Exception {
Employee employee = makeEmployee("employee 1");
Employee supervisor1 = makeEmployee("supervisor 1");
employee.setSupervisor(supervisor1);
Employee save = employeeRepository.save(employee);
System.out.println(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(save));
}
Here's my test output:
{
"id" : 1,
"name" : "employee 1",
"supervisor" : {
"id" : 2,
"name" : "supervisor 1"
}
}
Make sure you use the correct JsonSerialize org.codehaus.jackson.map.annotate.JsonSerialize not com.fasterxml.jackson.databind.annotation.JsonSerialize

Links with list of entities are getting rendered for any link instead of proper page in Spring boot application

I am relatively new to the Spring boot application. This has been working fine lately until I introduced a new entity as follows:
#Entity
#Table(name = "student_resource_access")
public class StudentResourceAccess extends implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private StudentResourceAccessPK studentResourceAccessPK;
#MapsId("studentId")
#ManyToOne
#JoinColumn(name = "student_id", referencedColumnName = "id")
private Student student;
#MapsId("userId")
#OneToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
#Enumerated(EnumType.STRING)
#Column(name = "role")
private UserRole role;
}
Associated PK class is as follows:
#Embeddable
public class StudentResourceAccessPK implements Serializable {
private String studentId;
private String userId;
#Column(name = "external_entity_type")
#Enumerated(EnumType.STRING)
private ExternalEntityType externalEntityType;
}
Now after making this change, when I am launching my Spring boot application, any link I would like to browse, I am seeing very weird content just like below:
{
"_links" : {
"entity1" : {
"href" : "https://example.com/entity1{?page,size,sort}",
"templated" : true
},
"entity2" : {
"href" : "https://example.com/entity2{?page,size,sort}",
"templated" : true
},
...
"studentResourceAccess" : {
"href" : "https://example.com/studentResourceAccess{?page,size,sort}",
"templated" : true
},
..
I don't have any idea, why is this coming and how should I fix this? Could anyone please help here? Thanks.
EDIT:
I have spring-data-rest in the dependency tree for a long time. This has been working fine. Suddenly with the above change, as I mentioned, it started behaving this way. No idea, why is this happening.

How to fix update process in Spring Boot (One-to-Many , One-to-One) via Postman?

I have a problem about updating the movie.
I wrote a function that is named for "update" in MovieService.
Here is that function which is shown below.
public void update(Long id,Movie movie) {
boolean isUpdatingEmployee = (movie.getId() == id);
if (isUpdatingEmployee) {
Movie existingMovie = movieRepository.findById(movie.getId()).get();
existingMovie.setId(id);
existingMovie.setName(movie.getName());
existingMovie.setRating(movie.getRating());
existingMovie.setDirector(movie.getDirector());
existingMovie.setGenres(movie.getGenres());
existingMovie.setCreatedAt(movie.getCreatedAt());
movieRepository.save(existingMovie);
}
}
When ı try to update a movie after saving it, I got this kind of JSON result and that's why the update process cannot be done.
http://localhost:8082/api/v1/movie/update/1
Body Request
{
"name": "MovieC",
"genres": [
{
"name" : "Adventure"
},
{
"name" : "Action"
}
],
"createdAt": "2021-04-28",
"rating" : 9,
"director" : {
"name" : "Director 2"
}
}
The result of JSON after updating the process.
{
"id": null,
"name": "MovieC",
"genres": [
{
"id": null,
"name": "Action"
},
{
"id": null,
"name": "Adventure"
}
],
"rating": 9.0,
"createdAt": "2021-04-28",
"director": {
"id": null,
"name": "Director 2"
}
}
Here is my Movie entity which is shown below.
#Entity
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
public class Movie implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#JsonManagedReference
#OneToMany(mappedBy="movie",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<Genre> genres;
private Double rating;
private LocalDate createdAt;
#ManyToOne(cascade=CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn
private Director director;
}
Here is my Director entity which is shown below.
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties({"movies"})
public class Director implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NonNull
private String name;
#OneToMany(mappedBy="director",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<Movie> movies;
}
Here is my Genre entity which is shown below.
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties({"movie"})
public class Genre implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NonNull
private String name;
#JsonBackReference
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn
private Movie movie;
}
Here is my sample project link : Project Link
How can I fix it?
As per your code, this is your request:
http://localhost:8082/api/v1/movie/update/1
{
"name": "MovieC",
"genres": [
{
"name" : "Adventure"
},
{
"name" : "Action"
}
],
"createdAt": "2021-04-28",
"rating" : 9,
"director" : {
"name" : "Director 2"
}
}
Now consider this snippet from your code:
public void update(Long id,Movie movie) {
boolean isUpdatingEmployee = (movie.getId() == id);
if (isUpdatingEmployee) {
...
Your id will be 1 as you've set this in your path variable.
However, movie.getId() will be null since I don't see it in your RequestBody.
And so:
isUpdatingEmployee = (movie.getId() == id)`
isUpdatingEmployee = ( null == 1)
isUpdatingEmployee = false
this will always give you false so I don't think this will enter in your update logic.
I think the problem because you are returning the same object movie you passed in the body of the post method in the controller - https://github.com/Rapter1990/springboothazelcast/blob/3157f354a628d418cccb99cfdbd188f594c24e9c/src/main/java/com/springboot/hazelcast/controller/MovieController.java#L64
You should rewrite it to something like this:
#PostMapping("/save")
public Movie saveMovie(#RequestBody Movie movie) throws ParseException {
LOG.info("MovieController | Saving Movie.");
return movieService.save(movie);
}
Here is the link to CRUDRepository javadocs:
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html#save-S-

Exposing field of child object in Spring hateoas

I have two entities defined:
#Entity
public class VideoPost {
private #Id
#GeneratedValue(strategy= GenerationType.IDENTITY) Long id;
private String videoTitle;
private #ManyToOne #JoinColumn(name = "VideoPost_Id") User uploader;
private boolean isPublished = false;
//....
}
#Entity
public class User {
private #Id #GeneratedValue(strategy=GenerationType.IDENTITY) Long id;
private String userName;
private Date registrationDate;
#OneToMany(mappedBy = "uploader", cascade = CascadeType.ALL) private List<VideoPost> videoPosts;
//...
}
I have following JSON response to the call to /api/videoposts:
"_embedded" : {
"videoPosts" : [ {
"videoTitle" : "test video 1",
"uploadDate" : "2017-06-03T11:44:02.012+0000",
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/videoPosts/1"
},
"videoPost" : {
"href" : "http://localhost:8080/api/videoPosts/1"
},
"uploader" : {
"href" : "http://localhost:8080/api/videoPosts/1/uploader"
}
}
} ]
I would like to expose the uploader name inside this response directly. I.e.
"uploader" : {
"userName": theName
"href" : "http://localhost:8080/api/videoPosts/1/uploader"
}
How could I achieve this?
You can check Projections where you can customize you objects the way you want whether you want to show the whole nested attributes or some of them or even hide these nested objects

Resources