Problem summary
I am getting unrelated data from the Many-to-Many With a New Entity concept for the first two cases.
Fetching a particular company and associated users of the company.
Fetching particular user and associated company.
Not able to feth company and user whose company status is not equal to 3 (CompanyStatus=1 "Pending" ,CompanyStatus=2 "Other",CompanyStatus=1 "Active")
To work on this Many-to-Many With a New Entity concept I have used Company, User, and CompanyUserMapping as an entity.
The blog that I have referred https://www.baeldung.com/jpa-many-to-many section 4.2
As mentioned above I have created Company, User, and CompanyUserMapping as below
Company
#Getter
#Setter
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long companyId;
private String companyName;
private String companyDescription;
private String companyWebsite;
private String companyEmailDomain;
private Integer companyTypeID;
private Integer numberOfEmployeesID;
private String companyLogo;
private Integer companyStatus;
private Boolean active;
#OneToMany(mappedBy = "company",fetch = FetchType.EAGER)
Set<CompanyUserMapping> companyUserMapping;
}
User
#Getter
#Setter
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String firstName;
private String lastName;
private String email;
private String encryptedEmail;
private String username;
private String password;
private String userStatus;
private String guid;
private Boolean isNotlocked;
private Date lastLogin;
private String profilePic;
#OneToMany(mappedBy = "user",fetch = FetchType.EAGER)
Set<CompanyUserMapping> companyUserMapping;
}
CompanyUserMapping
#Getter
#Setter
#Entity
public class CompanyUserMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long companyUserMappingId;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "company_id")
private Company company;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
private String role;
private String [] authorities;
private boolean isExternal;
private boolean isActive;
private Long customerId;
}
Then I inserted two companies and a user in each of them. Then I have also inserted a common user for both of the companies.
{
"companyId": 1,
"companyName": "ABC company",
"companyUsers": [
{
"companyUserMappingId": 1,
"company": 1,
"user": {
"userId": 1,
"email": "sachintendulkar#gmail.com",
"companyUserMapping": [
1
]
},
"active": true,
"external": false
},
{
"companyUserMappingId": 3,
"company": 1,
"user": {
"userId": 3,
"email": "shanewarne#gmail.com",
"companyUserMapping": [
3,
{
"companyUserMappingId": 4,
"company": {
"companyId": 2,
"companyName": "XYZ company",
"companyUsers": [
{
"companyUserMappingId": 2,
"company": 2,
"user": {
"userId": 2,
"email": "sehwag#gmail.com",
"companyUserMapping": [
2
]
},
"active": true,
"external": false
},
4
]
},
"user": 3,
"active": true,
"external": false
}
]
},
"active": true,
"external": false
}
]
}
In the above JSON, ABC and XYZ are two companies and a common user for both of the companies is shanewarne#gmail.com
As mentioned in the summary If I try to pull a particular company(ABC company) and associated users, it also brings another company and users which is unrelated.
To resolve this problem I have Implemented like below
public CompanyDto getConsolidatedCompanyData(Long companyId) {
Company existingCompany = companyRepository.findById(companyId).orElseThrow(() -> new ResourceNotFoundException("Company not found for the companyId :: "+companyId));
CompanyDto companyDto=new CompanyDto();
companyDto.setCompanyId(existingCompany.getCompanyId());
companyDto.setCompanyName(existingCompany.getCompanyName());
Set<CompanyUserMappingDto> companyUserMappingDtoList=new LinkedHashSet<>();
Set<CompanyUserMapping> companyUserMappingList=existingCompany.getCompanyUserMapping();
for (CompanyUserMapping cum : companyUserMappingList) {
CompanyUserMappingDto companyUserMappingDto=new CompanyUserMappingDto();
companyUserMappingDto.setCompanyUserMappingId(cum.getCompanyUserMappingId());
CompanyUserDto cdto=new CompanyUserDto();
cdto.setUserId(cum.getUser().getUserId());
cdto.setEmail(cum.getUser().getEmail());
companyUserMappingDto.setUser(cdto);
companyUserMappingDto.setActive(cum.isActive());
companyUserMappingDto.setExternal(cum.isExternal());
companyUserMappingDtoList.add(companyUserMappingDto);
}
companyDto.setAssociatedUsers(companyUserMappingDtoList);
return companyDto;
}
In the same way, if I try to fetch a particular user and associated company I am getting related data so I have implemented code
private UserDto getConslidatedUserData(Long userId) {
User existingUser = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("User not found for the userId :: "+userId));
Set<UserCompanyMappingDto> associatedCompanies=new HashSet<>();
UserDto userDto=new UserDto();
userDto.setUserId(existingUser.getUserId());
userDto.setEmail(existingUser.getEmail());
Set<CompanyUserMapping> companyUserMapping=existingUser.getCompanyUserMapping();
for (CompanyUserMapping companyUserMapping2 : companyUserMapping) {
UserCompanyMappingDto userCompanyMappingDto=new UserCompanyMappingDto();
userCompanyMappingDto.setCompanyUserMappingId(companyUserMapping2.getCompanyUserMappingId());
UserCompanyDto userCompanyDto=new UserCompanyDto();
userCompanyDto.setCompanyId(companyUserMapping2.getCompany().getCompanyId());
userCompanyDto.setCompanyName(companyUserMapping2.getCompany().getCompanyName());
userCompanyMappingDto.setCompany(userCompanyDto);
associatedCompanies.add(userCompanyMappingDto);
}
userDto.setAssociatedCompanies(associatedCompanies);
return userDto;
}
Do I need to improvise Implementation for the first two cases?
I'm using Jackson to serialise My Rest Api
POJO :
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME)
public class Project implements Serializable {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String projectName;
private String resource;
#ManyToMany
private List<Collaborator> collaborators;
#JsonIgnore
#OneToMany(mappedBy = "project",cascade = CascadeType.ALL)
private List<Task> tasks;
public Project(String projectName, String resource) {
this.projectName = projectName;
this.resource = resource;
}
}
output:
{
"id": 1,
"dateDebut": "2022-05-31T13:14:39.091+00:00",
"dateFin": "2022-05-31T13:14:39.091+00:00",
"project": {
"Project": {
"id": 2,
"projectName": "project Suivi Activite 2",
"resource": "resource 2",
"collaborators": []
}
},
"collaborator": null,
"days": []
}
how can i avoid field "Project" inside "project" ?
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME) ?
I have one stackOverflowerror, between two entities, one entities is client, and other's is annonce, annonce, having one author client, with relation entities list column join...
If i asking for list of client, the stack over coming with listing from annonce and relist altime in annonce with author, and that's is circulary...How to avoid that's and keep one listing form annonce by each client ?...
Client:
#Entity
#Table(name = "client")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Client implements UserDetails {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Column(length = 16, name = "client_id", nullable = false, updatable = false)
private UUID client_id;
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String firstname;
#Column(nullable = false)
private String lastname;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String nationalite;
#Column(nullable = false)
private String rue;
#Column(nullable = false)
private String numero;
#Column(nullable = false)
private String cp;
#Column(nullable = false)
private String ville;
#Column(nullable = false)
private String pays;
#Column(nullable = true)
private String tel;
#Column(nullable = true)
private String imgType;
#Column(columnDefinition = "BLOB")
private byte[] profilePicture;
#Column(nullable = false)
private Boolean accountNonExpired;
#Column(nullable = false)
private Boolean accountNonLocked;
#Column(nullable = false)
private Boolean credentialsNonExpired;
#Column(nullable = false)
private Boolean enabled;
#ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
#CollectionTable(name = "roles", joinColumns = #JoinColumn(name = "client_id"))
#Enumerated(EnumType.STRING)
private List<Role> roles;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "createdAt", insertable = false, columnDefinition = "DATETIME DEFAULT NOW()")
private Date createdAt;
#Column(name = "updateAt", nullable = true, columnDefinition = "DATETIME")
private Date updateAt;
#OneToMany(mappedBy = "cartFrom")
private Collection<Cart> cartList = new ArrayList<>();
Here-->
#OneToMany(mappedBy = "author", targetEntity = Annonce.class, orphanRemoval = true, cascade = CascadeType.ALL)
private Collection<Annonce> annonceList = new ArrayList<>();
#OneToMany(mappedBy = "conversationOf")
private Collection<Conversation> conversationList = new ArrayList<>();
#OneToMany(mappedBy = "orderedFrom")
private Collection<Ordered> orderedList = new ArrayList<>();
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Annonce :
#Entity
#Table(name = "annonce")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Annonce {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Column(length = 16, name = "annonce_id", nullable = false, updatable = false)
private UUID annonce_id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String title;
#Column(nullable = false)
private String description;
#Column(nullable = false)
private double price;
#Column(nullable = false)
private int quantity;
#Column(nullable = false)
private String salesTerms;
And here -->
#ManyToOne
#JoinColumn(name = "author_client_id")
private Client author;
#OneToOne(mappedBy = "annonce", fetch = FetchType.LAZY)
#JoinColumn(name = "annoncepicture_id", nullable = false)
private AnnoncePicture annoncepicture;
#ManyToMany
#JoinTable(name = "annonce_carts",
joinColumns = #JoinColumn(name = "annonce_id"),
inverseJoinColumns = #JoinColumn(name = "cart_id"))
private List<Cart> cartList = new ArrayList<>();
#OneToMany(mappedBy = "conversationOnAnnonce")
private Collection<Conversation> conversationList = new ArrayList<>();
#ManyToMany(mappedBy = "annonceList")
private List<Categorie> categorieList = new ArrayList<>();
#Enumerated
#ElementCollection(fetch = FetchType.LAZY)
private List<State> states;
#OneToMany(mappedBy = "noteFor")
private Collection<Note> noteList;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "createdAt", insertable = false, columnDefinition = "DATETIME DEFAULT NOW()")
private Date createdAt;
#Column(name = "updateAt", nullable = true, columnDefinition = "DATETIME")
private Date updateAt;
#Column(nullable = false)
private String categorie;
}
and than here is explain of problem...
"client_id": "58b80786-29d3-4ba9-b657-659cc32890a4",
"username": "shimshaker#gmail.com",
"firstname": "Christophe",
"lastname": "Cuypers",
"email": "shimshaker#gmail.com",
"password": "$2a$10$chNqhZ73r.kfEU/ZJ7M7XeU3Dck.q9XMRXUURc0ZnWMggJFRZat/m",
"nationalite": "Français",
"rue": "Rue de Beauraing",
"numero": "11B6",
"cp": "6920",
"ville": "Wellin",
"pays": "France",
"tel": "474/474.206",
"imgType": null,
"profilePicture": null,
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true,
"roles": [
"ADMIN",
"CLIENT",
"BUYER",
"MODERATOR"
],
"createdAt": "2022-04-11T12:01:59.000+00:00",
"updateAt": null,
"cartList": [],
"annonceList": [
{
"annonce_id": "a41a271c-f9bb-4644-894d-fc5245c2b054",
"name": "Ordinateur",
"title": "Vielle ordinateur",
"description": "Vielle ordinateur avec écran",
"price": 250.0,
"quantity": 1,
"salesTerms": "A prendre sur place",
"author": {
"client_id": "58b80786-29d3-4ba9-b657-659cc32890a4",
"username": "shimshaker#gmail.com",
"firstname": "Christophe",
"lastname": "Cuypers",
"email": "shimshaker#gmail.com",
"password": "$2a$10$chNqhZ73r.kfEU/ZJ7M7XeU3Dck.q9XMRXUURc0ZnWMggJFRZat/m",
"nationalite": "Français",
"rue": "Rue de Beauraing",
"numero": "11B6",
"cp": "6920",
"ville": "Wellin",
"pays": "France",
"tel": "474/474.206",
"imgType": null,
"profilePicture": null,
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true,
"roles": [
"ADMIN",
"CLIENT",
"BUYER",
"MODERATOR"
],
"createdAt": "2022-04-11T12:01:59.000+00:00",
"updateAt": null,
"cartList": [],
"annonceList": [
{
"annonce_id": "a41a271c-f9bb-4644-894d-fc5245c2b054",
"name": "Ordinateur",
"title": "Vielle ordinateur",
"description": "Vielle ordinateur avec écran",
"price": 250.0,
"quantity": 1,
"salesTerms": "A prendre sur place",
"author": {
"client_id": "58b80786-29d3-4ba9-b657-659cc32890a4",
"username": "shimshaker#gmail.com",
"firstname": "Christophe",
"lastname": "Cuypers",
"email": "shimshaker#gmail.com",
"password": "$2a$10$chNqhZ73r.kfEU/ZJ7M7XeU3Dck.q9XMRXUURc0ZnWMggJFRZat/m",
"nationalite": "Français",
"rue": "Rue de Beauraing",
"numero": "11B6",
"cp": "6920",
"ville": "Wellin",
"pays": "France",
"tel": "474/474.206",
"imgType": null,
"profilePicture": null,
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true,
"roles": [
"ADMIN",
"CLIENT",
"BUYER",
"MODERATOR"
],
"createdAt": "2022-04-11T12:01:59.000+00:00",
"updateAt": null,
"cartList": [],
"annonceList": [
{
"annonce_id": "a41a271c-f9bb-4644-894d-fc5245c2b054",
"name": "Ordinateur",
"title": "Vielle ordinateur",
"description": "Vielle ordinateur avec écran",
"price": 250.0,
"quantity": 1,
"salesTerms": "A prendre sur place",
"author": {
"client_id": "58b80786-29d3-4ba9-b657-659cc32890a4",
"username": "shimshaker#gmail.com",
"firstname": "Christophe",
"lastname": "Cuypers",
"email": "shimshaker#gmail.com",
"password": "$2a$10$chNqhZ73r.kfEU/ZJ7M7XeU3Dck.q9XMRXUURc0ZnWMggJFRZat/m",
"nationalite": "Français",
"rue": "Rue de Beauraing",
"numero": "11B6",
"cp": "6920",
"ville": "Wellin",
"pays": "France",
"tel": "474/474.206",
"imgType": null,
"profilePicture": null,
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true,
"roles": [
"ADMIN",
"CLIENT",
"BUYER",
"MODERATOR"
],
Thanks for your helps...Have great day...
Use FetchType.LAZY on the members which cause the circularity. Also, if you log the entity at any time or have any identity checks, also use #ToString.Exclude and #EqualsAndHashCode.Exclude (or, better, annotate the classes with #EqualsAndHashCode(onlyExplicitlyIncluded = true) and the id members with #EqualsAndHashCode.Include()).
I have problems with Many to One relationship because I don't show correctly the entity.
Could anyone helps to me ?
I attached my code.
Invoice
#Entity
#Table(name = "invoices")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class Invoice {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String clave;
#OneToMany(mappedBy = "invoice", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, orphanRemoval = true)
private List<InvoiceLine> lines;
InvoiceLines
#Entity
#Table(name = "invoice_lines")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class InvoiceLine {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "product", nullable = false)
private String product;
#ManyToOne
#JoinColumn(name = "invoice_id", referencedColumnName = "id", nullable = false)
private Invoice invoice;
Controller
#RestController
public class InvoiceController{
#Autowired
private InvoiceRepository invoiceRepository;
#Autowired
private InvoiceLineRepository invoiceLineRepository;
#GetMapping("/")
public Iterable<Invoice> findAllnvoices(){
return invoiceRepository.findAll();
}
#GetMapping("/invoiceLine")
public Iterable<InvoiceLine> findAllInvoiceLine(){
return invoiceLineRepository.findAll();
}
#GetMapping("/{id}")
public Optional<Invoice> findTagByInvoice(#PathVariable("id") Long id){
return invoiceRepository.findById(id);
}
}
The response when I call a endpoint invoiceLine :
[
{
"id": 1,
"product": "Tag1-ES",
"invoice": {
"id": 1,
"clave": "Tag1",
"lines": [
1,
{
"id": 2,
"product": "Tag1-FR",
"invoice": 1
},
{
"id": 3,
"product": "Tag1-IT",
"invoice": 1
}
]
}
},
2,
3
]
My question :Why is not showing correctly the response the ManyToOne entity if I have all correct ?
If I understood your problem correctly after the comments, you are bothered by the "numbers" that are displayed. Those numbers are used to avoid infinite recursion, and they refer to entities that were already displayed.
So the number "2" would be this actually:
{
"id": 2,
"product": "Tag1-FR",
"invoice": 1
}
If a representation like that is not used, then the whole invoice and it's items would be repeated infinitely.
There are several different ways to avoid this behavior, such as using #JsonIgnore or #JsonBackReference and #JsonManagedReference. Take a look at this explanation about their differences
Difference between #JsonIgnore and #JsonBackReference, #JsonManagedReference
I want to populate the repository with user roles and two initial users related to those roles.
This is the JSON I want to upload:
[
{
"_class": "models.Role",
"#id": 1,
"name": "ROLE_BLOG_ADMIN",
"description": "Rol de los administradores del blog"
},
{
"_class": "models.Role",
"#id": 2,
"name": "ROLE_BLOG_CONTRIBUTOR",
"description": "Rol de los editores de artículos"
},
{
"_class": "models.User",
"username": "sergio11",
"password": "$2a$10$0eCQpFRdw8i6jJzjj/IuNuKpJYnLaO5Yp9xSJ3itcfPmQNXVhmNyu",
"email": "gfhdsgfjhdsgfjhdsgf#gmail.com",
"fullName": "Sergio Sánchez Sánchez",
"roles": [1, 2]
},
{
"_class": "models.User",
"username": "dani33",
"password": "$2a$10$0eCQpFRdw8i6jJzjj/IuNuKpJYnLaO5Yp9xSJ3itcfPmQNXVhmNyu",
"email": "danihiglesias#usal.es",
"fullName": "Daniel de la Iglesia",
"roles": [2]
}
]
I am using JsonIdentityInfo in the Roles entity:
#Entity
#Table(name = "roles")
#JsonIdentityInfo(generator=IntSequenceGenerator.class, property="#id")
public class Role implements Serializable
I have included Jackson2RepositoryPopulatorFactoryBean in the context:
#Bean(name="repositoryPopulator")
public Jackson2RepositoryPopulatorFactoryBean provideJackson2RepositoryPopulatorFactoryBean(){
Resource sourceData = new ClassPathResource("data.json");
Jackson2RepositoryPopulatorFactoryBean factory = new Jackson2RepositoryPopulatorFactoryBean();
factory.setResources(new Resource[] { sourceData });
return factory;
}
But, no role is associated with any user.
This is the association:
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name="USER_ROLES",
joinColumns=#JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="ROLE_ID", referencedColumnName="ID"))
private Set<Role> roles;
Does anyone know how to fix them?
It should only complain about a missing constructor for the Role entity, for me everything went smoothly with the following :
#Entity
#Table(name = "roles")
public class Role implements Serializable {
public Role() {
}
public Role(Integer id) {
this.id = id;
}
#Id
#JsonProperty("#id")
private Integer id;
//additional properties, getters & setters
...
}
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name="USER_ROLES",
joinColumns=#JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="ROLE_ID", referencedColumnName="ID"))
private Set<Role> roles;
//additional properties, getters & setters
...
}
And I properly get :
[
{
"id":1,
"roles":[
{
"name":"ROLE_BLOG_ADMIN",
"#id":1
},
{
"name":"ROLE_BLOG_CONTRIBUTOR",
"#id":2
}
],
"email":"sss4esob#gmail.com"
},
{
"id":2,
"roles":[
{
"name":"ROLE_BLOG_CONTRIBUTOR",
"#id":2
}
],
"email":"danihiglesias#usal.es"
}
]
Can you provide more of your entities code if this isn't working for you ? Do you encounter any exception ?