JsonIgnore dynamically If it is nested? - spring

I have hibernate models like following:
Ticket Model:
#Entity
#Table(name = "ticket")
public class TicketModel {
private BigInteger id;
private UserModel user;
#Id
#Column(name = "id", nullable = false)
#SequenceGenerator(name = "seqTicket", sequenceName = "ticket_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqTicket")
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", referencedColumnName = "id")
public UserModel getUser() {
return user;
}
public void setUser(UserModel user) {
this.user = user;
}
}
User Model:
#Entity
#Table(name = "user")
public class UserModel {
private BigInteger id;
private String name;
private CompanyModel company;
#Id
#Column(name = "id", nullable = false)
#SequenceGenerator(name = "seqUser", sequenceName = "user_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqUser")
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id", referencedColumnName = "id")
public CompanyModel getUser() {
return company;
}
public void setUser(CompanyModel company) {
this.company = company;
}
}
Company Model:
#Entity
#Table(name = "company")
public class UserModel {
private BigInteger id;
private String name;
#Id
#Column(name = "id", nullable = false)
#SequenceGenerator(name = "seqCompany", sequenceName = "company_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqCompany")
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now, what i want to do is json ignoring user's company in my rest controller if I am returning ticket object. But if I am returning User object I dont want to ignore company relation.
For example:
in ticket controller's return it should be a json like :
{
"id":1,
"user":{
"id":3,
"name":"something"
}
}
but in user controller's return it should be like:
{
"id":3,
"name":"something",
"company":{
"id":5,
"name":"something else"
}
}
So shortly, I want to ignore relations if it is in second dimension.
Is that possible?

Related

Not null reference a null or transient value

So i am trying to achieve oneToone relationship between two entity classes.First class is a customer entity class which have two foreign keys buyer_id and seller_id.So what i want initially is that when the user fills the initial credentials in the website the buyer_id and seller_id field should be null and after the user fills the required information for the buyer or seller i will update the row of the corresponding customer and add the buyer_id and seller_id.But when i try to create a customer entry i am getting this error that buyer_id cannot be null?
This is my customer table
#Entity
#Table(name = "Customer")
public class Customer {
public enum Status{
ACTIVE,
IN_ACTIVE
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#OneToOne(fetch = FetchType.LAZY,optional = true,cascade=CascadeType.ALL)
#JoinColumn(name = "seller_id",nullable = true,referencedColumnName = "id",updatable = true)
#Basic(optional = true)
private Seller seller_id;
#OneToOne(fetch=FetchType.LAZY,optional = true,cascade=CascadeType.ALL)
#JoinColumn(name = "buyer_id", nullable = true,referencedColumnName="id",updatable = true)
#Basic(optional = true)
private Buyer buyer_id;
#OneToOne(fetch=FetchType.LAZY,optional = false,cascade = CascadeType.ALL)
#JoinColumn(name = "user_id",nullable = false,unique = true,referencedColumnName = "id")
private User user_id;
public Buyer getBuyer_id() {
return buyer_id;
}
public void setBuyer_id(Buyer buyer_id) {
this.buyer_id = buyer_id;
}
#Column(name = "Name")
String name;
#Enumerated(EnumType.STRING)
#Column(name = "Status")
private Status status;
public Customer(String name,Status status){
this.name=name;
this.status = status;
}
public Customer(){
}
public Seller getSeller_id() {
return seller_id;
}
public void setSeller_id(Seller seller_id) {
this.seller_id = seller_id;
}
public User getUser_id() {
return user_id;
}
public void setUser_id(User user_id) {
this.user_id = user_id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User getUser() {
return user_id;
}
public void setUser(User user) {
this.user_id = user;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
This is my buyer table
#Entity
#Table(name="Buyer")
public class Buyer {
#Id
#Column(name = "id") private long id;
#Column(name = "GSTIN")
String GSTIN;
#Column(name = "Legal_Document")
#Lob
private byte[] legalDocument;
#OneToOne(fetch=FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "buyer_id")
#JsonIgnore
private Customer customer;
#Column(name = "Authorized_person_name")
String authorized_person_name;
#Column(name = "Authorized_person_email")
String authorized_person_email;
}
This is my seller table
#Entity
#Table(name = "Seller")
public class Seller {
#Id
#Nullable
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "GSTIN")
private String GSTIN;
#Column(name = "GST_Document")
#Lob
private byte[] gst_document;
#OneToOne(fetch=FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "seller_id")
#JsonIgnore
private Customer customer;
// #OneToOne(fetch = FetchType.LAZY,
// cascade = CascadeType.ALL,
// mappedBy = "sellerId")
// #JsonIgnore
// private PickupAddress pickupAddress;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
public String getGSTIN() {
return GSTIN;
}
public void setGSTIN(String GSTIN) {
this.GSTIN = GSTIN;
}
public byte[] getGst_document() {
return gst_document;
}
public void setGst_document(byte[] gst_document) {
this.gst_document = gst_document;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

POST request with Many-to-many relationship in Spring Data REST

I'm trying to add a movie to MySQL database, here's my database schema:
Movie(id, name)
Genre(id, name)
Movie_genre(id_movie, id_genre)
And here's my model classes:
Movie.ts
public class Movie {
private Short id;
private String name;
private List<MovieGenre> movieGenres;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
#Column(name = "name", nullable = false, length = 100)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "movie")
public List<MovieGenre> getMovieGenres() {
return movieGenres;
}
public void setMovieGenres(List<MovieGenre> movieGenres) {
this.movieGenres = movieGenres;
}
}
Genre.ts
public class Genre {
private Short id;
private String name;
private List<MovieGenre> movieGenres;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
#Column(name = "name", nullable = false, length = 15)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "genre")
#JsonIgnore
public List<MovieGenre> getMovieGenres() {
return movieGenres;
}
public void setMovieGenres(List<MovieGenre> movieGenres) {
this.movieGenres = movieGenres;
}
}
MovieGenre.ts ( that model stands for the generated table )
public class MovieGenre {
private MovieGenrePK id;
private Movie movie;
private Genre genre;
#EmbeddedId
#JsonIgnore
public MovieGenrePK getId() {
return id;
}
public void setId(MovieGenrePK id) {
this.id = id;
}
#MapsId("movieId")
#ManyToOne
#JoinColumn(name = "movie_id", referencedColumnName = "id", nullable = false)
#JsonIgnore
public Movie getMovie() {
return movie;
}
public void setMovie(Movie movie) {
this.movie = movie;
}
#MapsId("genreId")
#ManyToOne
#JoinColumn(name = "genre_id", referencedColumnName = "id", nullable = false)
public Genre getGenre() {
return genre;
}
public void setGenre(Genre genre) {
this.genre = genre;
}
}
and because we have composite key in the last model, we need a class for that :
public class MovieGenrePK implements Serializable {
private Short movieId;
private Short genreId;
#Column(name = "movie_id", nullable = false)
public Short getMovieId() {
return movieId;
}
public void setMovieId(Short movieId) {
this.movieId = movieId;
}
#Column(name = "genre_id", nullable = false)
public Short getGenreId() {
return genreId;
}
public void setGenreId(Short genreId) {
this.genreId = genreId;
}
}
So I'm trying to add a movie with genres by making a post request, first i made a post request for adding a movie and another one for adding a genre, that's works fine, now i need to associate a genre to a movie.
I tried the following:
I made a POST request to the following endpoint: http://localhost:8080/api/movieGenres with application/json header and with the following body:
{
"movie": "http://localhost:8080/api/movies/6",
"genre": "http://localhost:8080/api/genres/1"
}
but i got the error:
{
"timestamp": "2018-08-22T21:10:30.830+0000",
"status": 500,
"error": "Internal Server Error",
"message": "NullPointerException occurred while calling setter of com.movies.mmdbapi.model.MovieGenrePK.genreId; nested exception is org.hibernate.PropertyAccessException: NullPointerException occurred while calling setter of com.movies.mmdbapi.model.MovieGenrePK.genreId",
"path": "/api/movieGenres"
}
You have to instatiate MovieGenrePK, change:
public class MovieGenre {
private MovieGenrePK id;
}
to
public class MovieGenre {
private MovieGenrePK id = new MovieGenrePK();
}

#Id Not Mapped For Specific Entity In A Spring-Boot Controller

I have two entities Employee and Department and each have a Spring Web #RestController annotated class with update methods i.e. Http PUT.
For some strange reason (and likely a blindingly obvious solution) whenever the PUT is called for the Employee class, the ID in the JSON payload is NOT mapped to the id class of the Employee entity but it works perfectly for the Department entity.
Employee class:
Entity
#Table(name = "EMPLOYEE")
public class Employee implements Serializable, Identity<Long>, Deleted
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "NAME")
private String name;
#ManyToOne
#Where(clause = "is_deleted = false")
private Department department;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "EMP_QUAL", joinColumns = #JoinColumn(name = "EMPLOYEE_ID"), inverseJoinColumns = #JoinColumn(name = "QUALIFICATION_ID"))
#WhereJoinTable(clause = "IS_DELETED = false")
#SQLDelete(sql = "UPDATE `EMP_QUAL` SET IS_DELETED = true where EMPLOYEE_ID = ? and QUALIFICATION_ID = ? and IS_DELETED = False")
private Set<Qualification> qualifications;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "EMP_PROJ", joinColumns = #JoinColumn(name = "emp_id"), inverseJoinColumns = #JoinColumn(name = "proj_id"))
#Where(clause = "is_deleted = false")
private Set<Project> projects;
#JsonIgnore
#Column(name = "is_deleted", nullable = false)
private Boolean isDeleted = false;
#Override
public Long getId()
{
return this.id;
}
#Override
public void setId(final Long id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(final String name)
{
this.name = name;
}
public Set<Project> getProjects()
{
return projects;
}
public void setProjects(final Set<Project> projects)
{
this.projects = projects;
}
public Department getDepartment()
{
return department;
}
public void setDepartment(final Department department)
{
this.department = department;
}
public Set<Qualification> getQualifications()
{
return qualifications;
}
public void setQualifications(final Set<Qualification> qualifications)
{
this.qualifications = qualifications;
}
public Boolean isDeleted()
{
return isDeleted;
}
public void setDeleted(final Boolean deleted)
{
isDeleted = deleted;
}
}
Department class:
#Entity
#Table(name = "DEPARTMENT")
public class Department implements Serializable, Identity<Long>, Deleted
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.EAGER)
#WhereJoinTable(clause = "is_deleted = false")
#JoinTable(name = "DEPARTMENT_EMPLOYEE", joinColumns = {#JoinColumn(name = "department_id")},
inverseJoinColumns = {#JoinColumn(name = "employee_id")})
#SQLDelete(sql = "UPDATE DEPARTMENT_EMPLOYEE SET is_deleted = true where department_id = ? and employee_id = ? and is_deleted = false")
private Set<Employee> departmentMembers;
#Column(name = "is_deleted", nullable = false)
private Boolean isDeleted;
#Override
public Long getId()
{
return this.id;
}
#Override
public void setId(final Long id)
{
this.id = id;
}
#Override
public Boolean isDeleted()
{
return this.isDeleted;
}
#Override
public void setDeleted(final Boolean isDeleted)
{
this.isDeleted = isDeleted;
}
public String getName()
{
return name;
}
public void setName(final String name)
{
this.name = name;
}
public Set<Employee> getDepartmentMembers()
{
return departmentMembers;
}
public void setDepartmentMembers(final Set<Employee> departmentMembers)
{
this.departmentMembers = departmentMembers;
}
}
When call PUT /employees/{id}:
Calling PUT /departments/{id}:
As you can see in the screenshots of the debugger the id field of Department is populated while it is null in Employee. I'm testing this with Swagger and I am setting the ID in the payload. I don't have any specific Jackson configuration set I just use Spring boot's default but I cannot work out why only in Employee the id field is never mapped.
Employee body:
{
"id":1,
"name": "New Name"
}
Department body:
{
"id":2,
"name": "chemistry",
"deleted":false
}
The issue was due to a Jackson annotation #JsonIdentityInfo on another entity Project which is has a relationship with Employee:
#Entity
#Table(name = "PROJECT")
public class Project implements Serializable, Identity<Long>, Deleted
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "EMP_PROJ", joinColumns = #JoinColumn(name = "proj_id"), inverseJoinColumns = #JoinColumn(name = "emp_id"))
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id")
#Where(clause = "is_deleted = false")
private Set<Employee> employees;

JPA repository null pointer exception for many to one mapping with composite primary key

Post class
one to many mapping
Composite primary key using id
I am getting null pointer exception when I make get request for getting comments
#Entity
#Table(name = "posts")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Size(max = 100)
#Column(unique = true)
private String title;
#NotNull
#Size(max = 250)
private String description;
#NotNull
#Lob
private String content;
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "posted_at")
private Date postedAt = new Date();
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_updated_at")
private Date lastUpdatedAt = new Date();
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "post")
private Set<Comment> comments = new HashSet<>();
public Post() {
}
public Post(String title, String description, String content) {
this.title = title;
this.description = description;
this.content = content;
}
//getters and setters
}
Comment class
many to one mapping with composite primary keys using #Idclass
#Entity
#IdClass(CommentId.class)
#Table(name = "comments")
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Lob
private String text;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "post_id", nullable = false)
private Post post;
public Comment() {
}
public Comment(String text) {
this.text = text;
}
//getters and setters
}
Id class
CommentId
public class CommentId implements Serializable {
private static final long serialVersionUID = 1L;
private Post post;
private Long id;
public CommentId(Post post, Long id) {
super();
this.post = post;
this.id = id;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result+ ((post == null) ? 0 : post.hashCode());
result = prime * result ;
return result;
}
public boolean equals(Object object) {
if (object instanceof CommentId) {
CommentId pk = (CommentId)object;
return id.equals(pk.id) && post == pk.post;
} else {
return false;
}
}
//getters and setters
}
repositories
PostRepository
CommentRepository
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
#Repository
public interface CommentRepository extends JpaRepository<Comment, Long>
{
}
Controller class get request and I am using mysql database
#RestController
#RequestMapping("/demo")
public class Controller {
#Autowired
PostRepository ps;
CommentRepository cs;
#GetMapping("/post")
public List<Post> getAll(){
return ps.findAll();
}
#GetMapping("/comment")
public List<Comment> getAllcom(){
return cs.findAll();
}
}

Spring Data rest how to perform CRUD on #manytomany relation ,composite table with extra column

I am unable to perform CRUD via json POST from restful client Postman on Composite table having extra column .I am using Spring boot ,spring data rest and spring JPA.
I have 3 tables in data base
-user
-competency
-user_competency (join/composite table with extra column)
Here are my classes
User
#Entity
#Table(name = "\"user\"", schema = "public")
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "userId")
public class User implements java.io.Serializable {
private Long userId;
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
public Long getUserId() {
return this.userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
private Set<UserCompetency> userCompetencies = new HashSet<UserCompetency>(0);
#OneToMany(fetch = FetchType.EAGER,cascade = {CascadeType.ALL}, mappedBy = "user")
public Set<UserCompetency> getUserCompetencies() {
return this.userCompetencies;
}
public void setUserCompetencies(Set<UserCompetency> userCompetencies) {
this.userCompetencies = userCompetencies;
}
}
Competency
#Entity
#Table(name = "competency", schema = "public")
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "competencyId")
public class Competency implements java.io.Serializable {
private Long competencyId;
private Set<UserCompetency> userCompetencies = new HashSet<UserCompetency>(0);
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "competency_id", unique = true, nullable = false)
public Long getCompetencyId() {
return this.competencyId;
}
public void setCompetencyId(Long competencyId) {
this.competencyId = competencyId;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "competency")
public Set<UserCompetency> getUserCompetencies() {
return this.userCompetencies;
}
public void setUserCompetencies(Set<UserCompetency> userCompetencies) {
this.userCompetencies = userCompetencies;
}
}
UserCompetency
#Entity
#Table(name = "user_competency", schema = "public")
#JsonIdentityInfo(
generator =ObjectIdGenerators.IntSequenceGenerator.class,
property = "id")
public class UserCompetency implements java.io.Serializable {
private UserCompetencyId id;
private Level level;
private User user;
private Competency competency;
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "competencyId", column = #Column(name = "competency_id", nullable = false)),
#AttributeOverride(name = "userId", column = #Column(name = "user_id", nullable = false)) })
public UserCompetencyId getId() {
return this.id;
}
public void setId(UserCompetencyId id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "level_id")
public Level getLevel() {
return this.level;
}
public void setLevel(Level level) {
this.level = level;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false)
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
#ManyToOne(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
#JoinColumn(name = "competency_id", nullable = false, insertable = false, updatable = false)
public Competency getCompetency() {
return this.competency;
}
public void setCompetency(Competency competency) {
this.competency = competency;
}
}
UserCompetencyId
#Embeddable
public class UserCompetencyId implements java.io.Serializable {
private Long competencyId;
private Long userId;
public UserCompetencyId() {
}
public UserCompetencyId(Long competencyId, Long userId) {
this.competencyId = competencyId;
this.userId = userId;
}
#Column(name = "competency_id", nullable = false)
public Long getCompetencyId() {
return this.competencyId;
}
public void setCompetencyId(Long competencyId) {
this.competencyId = competencyId;
}
#Column(name = "user_id", nullable = false)
public Long getUserId() {
return this.userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof UserCompetencyId))
return false;
UserCompetencyId castOther = (UserCompetencyId) other;
return (this.getCompetencyId() == castOther.getCompetencyId()) && (this.getUserId() == castOther.getUserId());
}
}
Suppose i have already record in User and Competency tables and i want to assocaite both i am trying to post like this ,but it give me error of 405 Method Not Allowed.
help required ,what should be structure of json to be posted User will already exist and competency will might exist or new can be added and associated with existing user.
With this code I was able to post a new relation:
UserCompetency.class
#Entity
#Table(name = "user_competency")
#IdClass(UserCompetencyId.class)
public class UserCompetency implements java.io.Serializable {
#Id #ManyToOne
#JoinColumn(name = "competency_id", nullable = false, insertable = false, updatable = false)
private Competency competency;
#Id #ManyToOne
#JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false)
private User user;
UserCompetencyId.class
public class UserCompetencyId implements java.io.Serializable {
private Long competency;
private Long user;
public UserCompetencyId() {
}
public UserCompetencyId(Long competency, Long user) {
this.competency = competency;
this.user = user;
}
UserCompetencyRepository.class
public interface UserCompetencyRepository extends JpaRepository<UserCompetency, UserCompetencyId> {
}
POST http://localhost:8080/userCompetencies
{
"competency": "/competencies/2"
, "user": "/user/4"
}
Apparently there seems to be no "natural/easy" way to get what you want. But there is a promissing project for integrating embeddables by extending the serialization process: https://github.com/gregturn/embeddable-spring-data-rest
UserCompetencyIdJacksonModule, UserCompetencyIdSerializer, ..
Then you should be able PATCH (not POST) your JSON from above.

Resources