I have the following code, where I need to populate a set with multiple objects that are instances of the same class (User). The problem is, I only get the first object when I log.
user = User.builder()
.id(1L)
.username("2397047")
.nickname("test1")
.build();
anotherUser = User.builder()
.id(2L)
.username("23971948")
.nickname("test2")
.build();
Set<User> userSet = new HashSet<>();
userSet.add(user);
userSet.add(anotherUser);
System.out.println("User set from test " + userSet);
This code produces the following output
User set from test [User(id=1, nickname=test1, username= 2397047, password=null, roles=null, groups=null)]
Why am I unable to get the entire collection?
This is my User class
package com.chama.chamaservice.user;
import com.chama.chamaservice.BaseEntity;
import com.chama.chamaservice.Views;
import com.chama.chamaservice.config.ApplicationUserRole;
import com.chama.chamaservice.group.Group;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.*;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
public class User extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(Views.Public.class)
private Long id;
#JsonView(Views.Public.class)
private String nickname;
#JsonView(Views.Public.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
private Set<ApplicationUserRole> roles;
#JsonIgnore
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
private Set<Group> groups = new HashSet<>();
}
In the User class, #Data annotation which will implement #Getter, #Setter, #ToString method.
It will print all values in the Set.
Found an answer, although it may not be the optimal solution. I annotated the User class with #EqualsAndHashCode
Related
I've created 2 entities in Spring with JPA annotations:
Project:
package com.example.technologyradar.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name="native", strategy = "native")
private Long id;
private String name;
#ManyToMany(mappedBy = "projects")
private Set<Technology> assignedTechnologies = new HashSet<Technology>();
}
Technology:
package com.example.technologyradar.model;
import com.example.technologyradar.dto.constant.TechnologyStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Technology {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name="native", strategy = "native")
private Long id;
private String name;
#Enumerated(EnumType.STRING)
private TechnologyStatus technologyStatus;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, targetEntity = Category.class)
#JoinColumn(name="category_id", referencedColumnName = "id", nullable = false)
private Category category;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, targetEntity = Coordinate.class)
#JoinColumn(name="coordinate_id", referencedColumnName = "id", nullable = false)
private Coordinate coordinate;
#ManyToMany
#JoinTable(
name = "projects_technologies",
joinColumns = #JoinColumn(name="technology_id"),
inverseJoinColumns = #JoinColumn(name="project_id")
)
private Set<Project> projects = new HashSet<Project>();
}
My goal is to get List of projects with technologies usage list with ignoring Coordinate and Category from Technology Entity. When I perform simply findAll():
public List<Project> getProjectsWithTechnologyUsage() {
return (List<Project>) projectRepository.findAll();
}
then I'm obtaining famous Infinite Recursion error:
Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.example.technologyradar.model.Project["assignedTechnologies"])]
I know that one of the solutions is to add #JsonManagedReference and #JsonBackRerefence annotations but I don't know how to do it correctly for my particular case.
I would be grateful for any suggestions.
Thanks!
If you use json serialization you can
https://github.com/mikebski/jackson-circular-reference
but anyway add to Project entity
#EqualsAndHashCode(of = "id")
#ToString(of = "id")
Actually, for your scenario, you probably just want #JsonIgnore on top of Technology.projects.
Alternatively, if you sometimes want to print Technology.projects, you can use #JsonView on top of Project.technologies instead, to modify the behavior just for this one scenario where Project is the top level object to serialize.
I have problem with adding users role during registration request
This is my DB diagram:
And I have the following classes: first is entity role:
package application.model;
import lombok.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.persistence.*;
import java.util.List;
#NamedQueries({
#NamedQuery(name = User.GET_USERS, query = User.QUERY_GET_USERS),
})
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "users")
public class User {
public static final String GET_USERS = "User.get_users";
public static final String QUERY_GET_USERS = "select u from User u";
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
public int id;
#NotNull
#NotEmpty
#Column(name="firstname")
private String firstname;
#NotNull
#Column(name="lastname")
private String lastname;
#NotNull
#Column(name="email")
private String email;
#NotNull
#Column(name="password")
private String password;
#JoinTable
#OneToMany
private List<Role> roles;
}
second entity is Role:
package application.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "role")
public class Role {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public int id;
#NotNull
#Column(name="name")
private String name;
}
So, i have 3 roles in my application, when I register new user with post request:
{
"firstname": "imasdie5",
"lastname": "nazasdwisko5",
"email": "masdil",
"password": "pass",
"roles": [
{ "id":2 }
]
}
First user is registered correctly, but when i send second request with same roles.id=2, i have:
ERROR: duplicate key value violates unique constraint "uk_d9najy24fium4vkivgwjuf0hw"
Detail: Key (roles_id)=(2) already exists.
In dbeaver table users_role have constraint uk_d9najy24fium4vkivgwjuf0hw with type UNIQUE_KEY, so that's the problem, but how to change type to non-unique? Many users may have same role, so it is necessary for me
All tables are generated with hibernate.
Table users_role is not entity in my application, maybe it should be entity?
Do You have any advice what I should change to add one role for many users?
Try to use #ManyToMany annotation
#JoinTable
#ManyToMany
private List<Role> roles;
I've got a spring mvc application in which users book meeting rooms. When I try to delete a booking by using deleteById(), the user that belongs to the booking is also deleted. How do I prevent this?
Booking object:
package spe_booker.models;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.Date;
#Entity
public class Booking {
#Id
#GeneratedValue
private Long id;
#NotNull
private Date startDateTime;
#NotNull
private Date endDateTime;
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private Room room;
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private User user;
private Date creationDate;
public Booking() { }
...getters and setters...
User object:
package spe_booker.models;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue
private Long id;
private String username;
String name;
private String password;
private String faculty;
private String role;
private Boolean blacklisted;
#Column(name = "enabled")
public int enabled;
#OneToMany(mappedBy = "user")
private List<Booking> bookings = new ArrayList<>();
...getters and setters...
Booking repository:
package spe_booker.Repositorys;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import spe_booker.models.Booking;
import spe_booker.models.User;
import java.util.List;
#Repository
public interface BookingRepository extends CrudRepository<Booking, Long> {
List<Booking> findAll();
List<Booking> findBookingsByUser(User user);
}
The delete call:
bookingRepository.deleteById(id);
Change the cascade of
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
private User user;
to the appropriate values. Do you change a user and then save a booking and a user at the same time? Do you create bookings with users at the same time? If you can answer these questions with "no", you might just remove (cascade = CascadeType.ALL) at all. And you should ask yourself why the cascades are present in the first place!
CascadeType.ALL forces JPA to cascade all operations (persisting, removing, updating....). That means when you delete/update a booking, it willl delete/update the Room and User objects too
The problem is that when i run the application hibernate creates a column user_id in the tahograph_cards table which i don't want and it doesn't create a column tahograph_card_id in the users table which I want to.
It's a OneToMany relationship.
Here are my files:
application.properties:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/getmydrivercard
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto = update
User:
package com.getmydrivercard.getmydrivercard.models;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
#Entity
#Table(name = "users")
public class User {
private static final String EMPTY_STRING = "";
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "BINARY(16)")
private UUID userId;
#NotEmpty
private String username;
#NotEmpty
private String password;
#NotEmpty
private String email;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Set<TahographCard> tahographCards;
private String comments;
protected User(){}
public User(String username, String password, String email){
setUsername(username);
setPassword(password);
setEmail(email);
setComments(EMPTY_STRING);
setTahographCards();
}
//getters and setters
}
TahographCard:
package com.getmydrivercard.getmydrivercard.models;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import java.util.UUID;
#Entity
#Table(name = "tahograph_cards")
public class TahographCard {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "BINARY(16)")
private UUID tahographCardId;
#NotEmpty
private Boolean isActive;
#NotEmpty
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userId")
private User user;
protected TahographCard(){}
public TahographCard(User user){
setUser(user);
setActive(true);
}
//getters and setters
}
I tried with mappedBy and some other stuff I found on the Internet but still doesn't work. Thank you in advance.
The problem here is not the entities configuration but the database design.
You can't map a oneToMany relationship with a FK column on the one side.
For example if you have 2 Users and 4 TahographCard (2 for each user) normally it would look like this:
User
id name
1 Foo
2 Bar
Table
id user_id (fk)
1 1
2 1
2 2
2 2
The opposite with the TahographCardId on the User table is impossible
I am creating a project using spring MVC.
I am validating fields using jsr303.
It is working fine It showing errors of fields in console but not in jsp page
Can any one let me know what wrong I am doing here ?
My controller method is
#RequestMapping(value="/addCampaign", method = RequestMethod.POST)
public String processForm(#ModelAttribute(value="Campaign") #Valid CampaignEntity campaignObj,BindingResult result, ModelMap model, Principal principal) {
validator.validate(campaignObj, result);
if(result.hasErrors()){
System.out.println(result.getErrorCount());
System.out.println(result.toString());
CampaignEntity campaignBean = new CampaignEntity();
Map<String,String> agencies = new LinkedHashMap<String,String>();
agencies.put("1", "United Stated");
agencies.put("2", "China");
agencies.put("3", "Singapore");
agencies.put("4", "Malaysia");
model.addAttribute("agencies", agencies);
model.addAttribute("publishers", agencies);
model.addAttribute("Campaign", campaignBean);
return "addCampaign";
}else{
return campaign(model, principal);
}
}
In jsp I am showing error using tag
<form:errors path="*" cssClass="error" />
my entity class is
package com.nativeunlock.dto;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotEmpty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
#SuppressWarnings("serial")
#NamedQueries({
#NamedQuery(
name = CampaignEntity.GET_CAMPAIGNS_QUERY,
query = "from CampaignEntity campaign"
),
#NamedQuery(
name = CampaignEntity.DELETE_CAMPAIGNS_QUERY,
query = "DELETE FROM CampaignEntity campaign WHERE campaign.campaign_id = :campaign_id"
)
})
#Entity
#Table(name = "campaign")
public class CampaignEntity implements Serializable {
public static final String GET_CAMPAIGNS_QUERY ="getCampaignList";
public static final String DELETE_CAMPAIGNS_QUERY ="deleteCampaignList";
#Id
#Getter
#Setter
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "campaign_id", unique = true, nullable = false)
private int campaign_id;
#Getter
#Setter
#Column(name = "name", unique = true)
#NotEmpty(message="Name field is mandatory.")
private String name;
#Getter
#Setter
#Column(name = "no_of_views")
#NotNull(message="No. of Views field is mandatory.")
private int no_of_views;
#Getter
#Setter
#Column(name = "video_url")
#NotEmpty(message="Video URL field is mandatory.")
private String video_url;
#Getter
#Setter
#Column(name = "start_date")
#NotEmpty(message="Start Date field is mandatory.")
private String start_date;
#Getter
#Setter
#Column(name = "end_date")
#NotEmpty(message="End Date field is mandatory.")
private String end_date;
#Getter
#Setter
#Column(name = "click_to_play")
#NotNull(message="Click to play field is mandatory.")
private int click_to_play;
#Getter
#Setter
#Column(name = "frequency")
#NotNull(message="Frequency field is mandatory.")
private int frequency;
#Getter
#Setter
#Column(name = "priority")
#Digits(fraction = 0, integer = 100)
#NotNull(message="Priority field is mandatory.")
private int priority;
#Getter
#Setter
#Column(name = "divice")
#NotNull(message="Divice field is mandatory.")
private int divice;
#Getter
#Setter
#Column(name = "operating_system")
#NotNull(message="Operating system field is mandatory.")
private int operating_system;
#Getter
#Setter
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "publisher_id")
#NotEmpty(message="Publisher field is mandatory.")
private PublisherEntity publishers;
#Getter
#Setter
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "agency_id")
#NotEmpty(message="Agency field is mandatory.")
private AgencyEntity agencies;
}
my validator class is
package com.nativeunlock.Validator;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import com.nativeunlock.dto.CampaignEntity;
#Component
public class CampaignValidator implements Validator{
#Override
public boolean supports(Class c) {
return CampaignEntity.class.isAssignableFrom(c);
}
#Override
public void validate(Object command, Errors errors) {
CampaignEntity campaignBean = (CampaignEntity)command;
/*if(!regBean.getPassword().equals(regBean.getRePassword()))
errors.rejectValue("rePassword","password.notmatch");*/
}
}
The problem is that by adding a model attribute model.addAttribute("Campaign", campaignBean); you're removing your binding errors from the model.
Binding errors are bound to the validated instance. If you set a breakpoint to a first line inside your method and inspect a model, you will see a validated object with the key Campaign, but also a binding error object with the key org.springframework.validation.BindingResult.Campaign
When you move further, and reach a point where you're adding a new instance to the model under a 'Campaign' key, notice that the binding errors will get removed from the model, since the instance they're bound is no longer existing, and that is why you don't see them in your JSP.
Remove the model.addAttribute("Campaign", campaignBean); or change the key, and you should see your binding erros.
Also not that you don't need the manual invocation validator.validate(campaignObj, result); the #Valid will call the validation for you