How do I link two entities within a #RestController? - spring-boot

I have the following endpoint:
#PostMapping
Employee createEmployee(#Valid #RequestBody Employee newEmployee, BindingResult bindingResult) {
return employeeRepository.save(newEmployee);
}
I can send POST requests as JSON and have an employee created, but how do I link associated entities, such as a Department entity?
When I send a post request, it looks like this (I know it's not valid JSON, it's just browser console output):
departmentId: 1
emailAddress: "dillon#james.com"
firstName: "Dillon"
lastName: "James"
phoneNumber: undefined
The department exists in my database, and has an ID of 1. I thought that Spring would automatically set the departmentId somehow on my Employee model, but it doesn't seem to do that, or I must be missing something.
I'm basically trying to set the department of the newly created employee, but how I have it currently doesn't seem to do so.
For reference, this is how my Employee entity is defined:
#Entity
#Getter
#Setter
public class Employee {
#Id
#GeneratedValue
private Long id;
#NotNull
private String firstName;
#NotNull
private String lastName;
#NotNull
#Column(unique = true)
private String emailAddress;
#ManyToOne
private Department department;
private String phoneNumber;
}

Related

Confused why getting a User from Repository fixed "failed to lazily initialize a collection of role" compared to using SecurityContextHolder

My goal was to pass a List of Businesses to the model from the controller to display it in a view and I have succeeded, but have a bit of confusion.
When I initially tried using:
public User getCurrentAuthenticatedUser() {
UserDetailsImpl user = (UserDetailsImpl) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return user.getUser();
}
#GetMapping("")
public String list(Model model) {
model.addAttribute("businesses", userService.getCurrentAuthenticatedUser().getBusinesses());
return "business/list";
}
I got this error: "failed to lazily initialize a collection of role: com.xyz.User.businesses could not initialize proxy - no Session"
Then I tried:
#GetMapping("")
public String list(Model model) {
int userId = userService.getCurrentAuthenticatedUser().getId();
User user = userService.getById(userId); // gets User using Spring Data JPA UserRepository
List<Business> businesses = user.getBusinesses();
model.addAttribute("businesses", businesses);
return "business/list";
}
And this worked perfectly fine.
What was the issue using the first method. It seemed more simple rather than calling a User from the UserRepository. I've seen some posts that say you should use EAGER fetching, but that's just seems like a bandaid solution.
From the beginner's understanding: Since fetch type is LAZY the businesses don't exist yet in the User but are fetched on demand later on so there shouldn't be an issue.
Edit: After more thought I remembered that with basic Hibernate you would have to create Transactions and commit transactions. I'm assuming that User is not within a Transaction that's why I can't get businesses using the 1st method.
What would be a better solution to fetch the current Authenticated user? And that user's attributes such as a list of businesses.
Model Classes:
Business:
#Entity
#Table(name = "businesses")
public class Business {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private LocalDate date;
#ManyToOne(cascade={CascadeType.MERGE})
#JoinColumn(name="user_id")
private User user;
public Business() {
}
public Business(String name, String description, LocalDate date, User user) {
...
}
public Business(Long id, String name, String description, LocalDate date, User user) {
...
}
... getters/setters
}
USER:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private boolean enabled;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable( name = "users_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
#OneToMany(fetch = FetchType.LAZY, mappedBy="user", cascade={CascadeType.MERGE})
private List<Business> businesses;
... getters/setters
}

Spring JPA save() inserting instead of updating

I have a table in below format
Customer:
ID PK
value1
value2
address
I have unique constraint for value1 and value2.
While saving/updating I will not be having value for ID
So here is my service class code to update customer:
#Transactional
public void saveCustomer(String value1,String value2, String address) {
Customer customer = customerRepository.findByValue1AndValue2(value1,value2).orElse(new Customer());
customer.setValue1(value1);
customer.setValue2(value2);
customer.setAddress(address);
customerRepository.save(customer);
}
I am getting "Cannot insert duplicate key row in object error."
When I get the customer object using findByValue1AndValue2, it will have id orrect? so it should do update not insert
Adding my customer class:
#Entity
#Table(name = "customer")
#Data
#NoArgsConstructor
#AllArgsConstructor
#EntityListeners(AuditingEntityListener.class)
public class Customer{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name="value1")
private String value1;
#Column(name="value2")
private String value2;
#Column(name="address")
private String address;
#CreatedDate
#Column(name="created_date", nullable = false, updatable = false)
private Date created_date;
#LastModifiedDate
#Column(name="updated_date")
private Date updated_date;
}
I have added annotation #EnableTransactionManagement
Is there any thing specific need to be done
Please help me with this issue.
Thanks in advance

Spring + hibernate one to one mapping

I am developing web application using spring and hibernate. I am using one to one mapping between two tables employee and PersonelDetails.
below are my bean classes
=======================Employee=====================================
#Entity
#Table(name="employee")
public class Employee {
#Id
#Column
#GeneratedValue
private int empid;
#Column
private String firstName;
#Column
private String lastName;
#Column
private String email;
#Column
private String password;
#Column
private boolean isAdmin;
#Column
private boolean isActive;
#Column
private boolean isLocked;
//getter setters
====================PersonalDetails class====================
#Entity
#Table(name="PersonalDetails")
public class PersonalDetails {
#Column
#Id
private int empid;
#Column
private String personalEmail;
#Column
private String mob;
#Column
private String permenantAdress;
#Column
private String currentAddress;
#Column
private String gender;
#Column
private String maritialStatus;
#MapsId
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "empid", referencedColumnName = "empid")
#ForeignKey(name="empid")
private Employee employee;
//getter setters
In my application table employee is filled by Admin user while creating new employee after that employyee himself fill personalDetails table by login to his accountCreated by Admin)
Now when I try to send personal details bean to hibernate layer first I have to get the employee bean from employee table then call setEmployee method over personalDetails class and save employee bean in personalDetails and send to hibernate layer for saving in database.
So while getting employee bean from database and again send back through personalDetails bean leads to a performance issue.
Can anyone help here to clarify while saving data in child table(PersonalDetails) is it really mandatory to pass parent object(Employee) ?
=======================code to store personalDetails===============
#RequestMapping(value="addpersonal")
public ModelAndView addPersonalDetails(#ModelAttribute("personalDetails") PersonalDetails personalDetails) {
//personalDetails.setEmpid(1);
personalDetails.setCurrentAddress("niljyoti");
personalDetails.setMob("9405715872");
personalDetails.setPermenantAdress("address");
Employee e = empService.getEmployeebyUserName(uname);
personalDetails.setEmployee(e);
personalDetailsService.addPersonalDetails(personalDetails);
return new ModelAndView("home");
}
On read:
You can change fetch strategy if you are worried.
Based od JPA spec, default fetch type for #OneToOne is EAGER.
By setting fetch = FetchType.LAZY, instead of real PersonalDetails object, an object of a subclass behaving as a proxy is returned. Hence, selecting from employee table starts only after getEmployee is called.
On write:
You need to specify connection between entities, in your model, the only way is the employee field. However, you can specify mappedBy, see answer to this question:
Java: Hibernate #OneToOne mapping

Spring 3 mvc #Valid annotation doesn't work with List<Entity> property

I want to update an entity, which has a one-to-many List collection of other entity. When the handler method gets called, the validation doesn't seem to run on the collection. I've read the documentation, and searched stackoverflow, but did not find anything useful.
Model:
#Entity
public class Employee {
#Id
#GeneratedValue
private int employeeId;
#NotEmpty
private String name;
#Min(value=18)
private int age;
#OneToMany(mappedBy="parent",cascade=CascadeType.ALL)
private List<Child> children;
//getters,setters
}
#Entity
public class Child {
#Id
#GeneratedValue
private int childId;
#Column(nullable=false)
#NotNull
#Size(min=1,message="Child's name must not be empty")
private String childName;
#Max(value=18)
private Integer age;
#ManyToOne
#JoinColumn(name="employeeId")
private Employee parent;
//getters,setters
}
In the controller:
#RequestMapping(value = { "/edit/{id}" }, method = RequestMethod.POST)
private String update(#PathVariable int id, ModelMap model, #Valid Employee employee, BindingResult result) {
if (result.hasErrors()) {
return "employee/edit";
}
employeeDao.merge(employee);
return "redirect:../list";
}
The validation works for the simple properties of the Employee bean, but not for the elements in the children list.
How can this be fixed?
Seems like you should decorate your children list with #Valid annotation, as described here.
It should look something like this:
#OneToMany(mappedBy="parent",cascade=CascadeType.ALL)
#Valid
private List<Child> children;

JPQL Special Query

I have two entity bean :
#Entity
public class User {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<Comment>();
//SOME OTHER CLASS VARIABLES
//GETTERS AND SETTERS
}
and my Comment class is like this :
#Entity
public class Comment {
#Id
#GeneratedValue
private Long id;
private String title;
private String content;
#ManyToOne
private User user
//SOME OTHER CLASS VARIABLES
//GETTERS AND SETTERS
}
now I know that I can get the User Object from session and set the user for my comment like this in order to be able to use the join feature in JPA:
commentObject.setUser(TheSessionGrabedUserObject/UserObjectWhichHasFetchedFromDbUsingUserId);
but as long as I have the userId for my user Object I do not need to do this.
I'm looking for a way to insert this foreignKey into my comment table without getting the User Object from session or maybe query to database to fetch it first !
how I'm gonna do it using JPQL ?
You can use the entityManager.getReference() method. In your case:
entityManager.getReference(User.class, userId);
This will not perform any DB query, but will give you a User instance with only the ID populated, and you can pass that to commentObject.setUser().

Resources