#Putmapping in SpringBoot delete relationship Object - spring-boot

I am using #Putmapping to update an object. When I look at the logs it has deleted a relationship. I don't know why, but I need help to understand. My code is below.
I have an object Employee, which has a relationship with Project. I am updating an Employee, but it deletes record from the relationship table.
#PutMapping(path = "/{id}", consumes="application/json")
#ResponseStatus(HttpStatus.OK)
public Employee update(#RequestBody Employee emp) {
return empService.save(emp);
}
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "employee_seq")
#SequenceGenerator(name = "employee_seq", sequenceName = "employee_seq",
allocationSize = 1,initialValue=1)
private long employeeId;
private String firstName;
private String lastName;
private String email;
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST},
fetch = FetchType.LAZY)
#JoinTable(name = "project_employee", joinColumns = #JoinColumn(name="employee_id"),
inverseJoinColumns = #JoinColumn(name="project_id"))
#JsonIgnore
private List<Project> projects;
public Employee() {
}
}
#Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "project_seq")
#SequenceGenerator(name = "project_seq", sequenceName = "project_seq",
allocationSize = 1,initialValue=1)
private long projectId;
private String name;
private String stage; //NONSTARTED, COMPLETED, INPROGRESS
private String description;
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST},
fetch = FetchType.LAZY)
#JoinTable(name = "project_employee", joinColumns = #JoinColumn(name="project_id"),
inverseJoinColumns = #JoinColumn(name="employee_id"))
#JsonIgnore
private List<Employee> empList;
public Project() {
}
}

Related

Spring context indexer causes issues with hibernate entity mapping

I have a project that is split between multiple modules, each module is imported into the main one as a maven dependency.
Persistence entities can be located at any of the projects but under the same package.
I have been trying to improve the startup time of the application by using the spring-context-indexer but it seems to cause an issue with detecting entities.
My #EntityScan is configured like this:
#EntityScan(basePackages = {"com.botscrew", "com.botscrew.demoadminpanel.entity.jpa","com.botscrew.admin.entity"})
The strange thing is that error looks like this
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on com.botscrew.admin.entity.Bot.amioWhatsAppConfigs references an unknown entity: com.botscrew.admin.entity.services.configs.AmioWhatsAppConfigs
Essentially both entities are located under the same package but Bot entity was resolved but AmioWhatsAppConfigs was not.
The application starts perfectly fine without spring indexer.
I am using spring boot 2.2.1.RELEASE
Entities classes:
#Getter
#Setter
#Builder
#Entity
#ToString(of = {"id", "name"})
#AllArgsConstructor
#Table(name = "admin_bot")
#DiscriminatorValue("Bot")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Bot {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Convert(converter = EmojiConverter.class)
private String name;
private Integer timezone;
private String greetingText;
#Column(columnDefinition = "tinyint(1) default 1")
private Boolean active;
#Column(unique = true, updatable = false, nullable = false)
private String publicIdentifier;
#OneToOne(fetch = FetchType.LAZY)
private PersistentMenuEntity persistentMenuEntity;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "widget_id")
private Widget widget;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private MessengerConfigs messengerConfigs;
//TODO FetchType.LAZY
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private AmioWhatsAppConfigs amioWhatsAppConfigs;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private TwilioConfigs twilioConfigs;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private DialogflowConfigs dialogflowConfigs;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private ChatbaseConfig chatbaseConfig;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private SupportSettings supportSettings;
#OneToMany
private Set<Tag> tags;
#OneToMany(mappedBy = "bot")
private List<Broadcast> broadcasts;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "admin_bot_features",
joinColumns = {#JoinColumn(name = "bot_id")},
inverseJoinColumns = {#JoinColumn(name = "feature_id")})
private Set<Feature> features;
public Bot() {
this.active = true;
}
public Bot(String name, DefaultWidgetProperties defaultWidgetProperties) {
this.publicIdentifier = UUID.randomUUID().toString();
this.chatbaseConfig = new ChatbaseConfig();
this.amioWhatsAppConfigs = new AmioWhatsAppConfigs();
this.timezone = 0;
this.name = name;
this.active = true;
this.messengerConfigs = new MessengerConfigs();
this.dialogflowConfigs = new DialogflowConfigs();
this.widget = new Widget(defaultWidgetProperties);
this.supportSettings = new SupportSettings(false);
}
}
#Getter
#Setter
#Entity
#Accessors(chain = true)
#ToString
#Table(name = "admin_amio_whatsapp_configs")
public class AmioWhatsAppConfigs implements AmioWhatsAppBot {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String channelId;
private String accessToken;
private String secret;
}
Please help
I am editing my answer please check example
#EntityScan(basePackages = {"com.botscrew",
"com.botscrew.demoadminpanel.entity.jpa","com.botscrew.admin.entity.services.configs.*"})

Could not write JSON: Infinite recursion

I am getting StackOverflow recursion error when I run query in Postman or Browser .
When i run says:
.w.s.m.s.DefaultHandlerExceptionResolver : Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)
Here is the model classes :
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private String title;
#NotNull
private String description;
#NotNull
private double price;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "category_id", nullable = false)
private Category category;
private boolean isSealed;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "currency_id", nullable = false)
private Currency currency;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
private User user;
#Nullable
#OneToMany(mappedBy = "product",
cascade = CascadeType.ALL, orphanRemoval = true)
private List<Images> images;
private Date createdDate = new Date();
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "product")
private View view;
#OneToOne(fetch = FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="type_id")
private Type type;
private Long viewCount; }
#Entity public class Images{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String imagePath;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
private Product product; }
#Entity public class User implements UserDetails, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty
private String fullName;
#NotEmpty
#Email
#Column(unique = true)
private String email;
#NotNull
#Column(unique = true)
private int phoneNumber;
#NotEmpty
#Size(min = 5)
private String password;
private Date createAt = new Date();
#Nullable
private String picPath;
#Nullable
private String token;
#ManyToMany
#JoinTable(name = "user_roles", joinColumns = {#JoinColumn(
name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")})
private List<Role> roles;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "user")
private Product product;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "user")
private View view; }
#Entity
public class Currency{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String currency;
private String region_country;
#OneToOne(mappedBy = "currency", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Product product; }
#Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String imagePath;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
mappedBy = "category")
private Product product;
#OneToMany(mappedBy = "category", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private Set<Brand> brands; }
#Entity public class Brand {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "category_id", nullable = false)
private Category category; }
#Entity public class View {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
private User user;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "product_id", nullable = false)
private Product product; }
#Entity public class Type {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private String name;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "type")
private Product product; }
#Id
private String role;
#ManyToMany(mappedBy = "roles")
private List<User> users;
}
More than one of your entities have each other in themselves.
For example, Product has an object of User, and User has an object of Product.
To solve this, you have to write
#JsonBackReference(value = "user-product")
private User user;
in the Product class,
and
#JsonManagedReference(value = "user-product")
private Product product;
In the user class.
Do it in every field and for every class that call each other.
Also, Check this out
JPA: Having lists on both ends without infinite loop
You have cycles in your data model. For example, Product holds Images and Images point back to Products.
This works in an object oriented world, because only pointer references are stored in those fields.
When serialized, however, the actual object is written out as json text. Your Product prints the Images object which in turn prints the Product object which again prints the Image object and so on.
You need to decide how you want to represent your json, map your database model into simple plain old java object and use this for serializations. These POJOs are often called View Model or Transport Objects.

How to map an entity as java.util.Map with spring Data JPA?

I have such entities:
Bonus_Request entity:
#Entity
#Table(name = "bonus_request")
public class BonusRequest {
//some code above...
#OneToMany(fetch = FetchType.EAGER, mappedBy = "bonusRequest")
#JsonManagedReference(value = "parameter-bonus_request")
private Set<BonusRequestParameter> parameters;
}
Bonus_Request_Parameter entity:
#Entity
#Table(name = "bonus_request_parameter")
public class BonusRequestParameter {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(max = 30)
#Column(name = "parameter", nullable = false)
private String parameter;
#Size(max = 50)
#Column(name = "value", nullable = false)
private String value;
#JoinColumn(name = "bonus_request_id", nullable = false)
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference(value = "parameter-bonus_request")
private BonusRequest bonusRequest;
}
I wonder if it is possible to map the BonusRequestParameter entity as a java.util.Map field in the BonusRequest entity.
For example:
#Entity
#Table(name = "bonus_request")
public class BonusRequest {
#OneToMany(fetch = FetchType.EAGER, mappedBy = "bonusRequest")
private Map<String, String> parameters; //String parameter, String value
}
I use:
Spring Data JPA - 2.1.7
PostgreSQL DB - 10.7
This will work. It loads the map eagerly by default.
#Entity
#Table(name = "bonus_request")
public class BonusRequest {
...
#ElementCollection
private Map<String, String> parameters; //String parameter, String value
}
Resolved with this:
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "bonus_request_parameter",
joinColumns = {#JoinColumn(name = "bonus_request_id", referencedColumnName = "id")})
#MapKeyColumn(name = "parameter")
#Column(name = "value")
private Map<String, String> parameters;
Thank you for help.

access many to many relation in spring

I have a class called Tag:
#Entity
#Table(name = "tags")
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
...
}
And a class called Post
#Entity
#Table(name = "posts")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "post_tags",
joinColumns = { #JoinColumn(name = "post_id") },
inverseJoinColumns = { #JoinColumn(name = "tag_id") })
private Set<Tag> tags = new HashSet<>();
...
}
It creates another table named post_tags.
How can I write a Controller to access that table as it is not similar a repository?
Is there more easy and convenient way to implement ManyToMany relationship ?
My pom.xml
You don't need to access that relation table manually. You can load load all Tag entities, and then load all the referenced Post entities.
The relation table is enterily managed by your ORM frameork.
But, if you still want to access the relation table, you can use native queries in your Spring Data JPA repository, e.g.
#Query(value="select post_id, tag_id from post_tags", nativeQuery=true)
List<PostTag> loadPostTags();
PostTag class is not a jpa-managed entity and must match the structue of the returned table:
public class PostTag {
private long postId;
private long tagId;
// getter, setter
}
Use this way
#Entity
#Table(name = "tags")
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "post_tags",
joinColumns = { #JoinColumn(name = "id") },
inverseJoinColumns = { #JoinColumn(name = "post_id") })
private Set<Post> posts = new HashSet<>();
...
}
#Entity
#Table(name = "posts")
public class Post {
#Id
#Column(name = "post_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long postId;
...
}

Get Jackson to ignore some fields at controller level in Spring Boot

I have the following class:
#Entity
#Table(name = "Positions")
#NamedQuery(name = "Position.findAll", query = "SELECT p FROM Position p")
public class Position implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
private Long id;
private String title;
private String description;
#ElementCollection
#CollectionTable(name = "qualifications", joinColumns = #JoinColumn(name = "position_id"))
#Column(name = "required_qualifications")
private List<String> requiredQualifications;
#ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinTable(name = "positions_competencies", joinColumns = #JoinColumn(name = "position_id"), inverseJoinColumns = #JoinColumn(name = "Competence_id"))
private List<Competence> competencies;
#ManyToOne
#JoinColumn(name = "department_id")
private Department department;
I'm using spring boot with spring data.
I want to ignore some of the fields in the getAllPositions method, but not in the getPositionById method, so #JsonIgnore won't work for me. What is the best way to do this in Spring Boot 2.0.2?
Here is the controller:
#Autowired
private PositionRepository positionRepository;
#GetMapping(path = "/positions")
public Iterable<Position> getAllPositions() {
return positionRepository.findAll();
}
#GetMapping(path = "/positions/{id}")
public Position getPositionById(#PathVariable Long id) {
return positionRepository.findById(id).get();
}

Resources