How to create row with selected entity in Jpa, RestController Spring Boot - spring

I have 3 Entity A, B, C where C will created though JSON with #OneToOne relation Entity A and #ManyToOne Entity B,
How to send the data using JSON, do I only need to send the ID or complete data of Entity A,B. also I made the relation insertable and updateable falsed, because they dont need to update or create, only need to select among list of value.
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(...)
private Set<B> objB = new HashSet<B>();
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(...)
private A objA;
JSON I am posting,
{
"name": "Test Store",
"description": "Test Description",
"objB": [
{
"id": 19
}
],
"objA": {
"id": 1,
}
}
If I send like this, then Object is null for objB and JSON parse error for objA. How I can Handle this. what can be the best approche.

Did you try use like that instead?
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(...)
private List<B> objB;

Related

Spring JPA delete entity not working when iterating

I am trying to delete an entity using its remove method of its repository from another service class, but it is not getting deleted. Below code works when I hard code the Id:
long id = 1234;
Optional<Employee> employeeOptional = employeeRepository.findById(id);
Employee employee = employeeOptional.get();
employeeRepository.delete(employee);
Above code is working fine, but if I try with below code, deletion is not happening.
for (Employee employee : department.getEmployees()) {
if (employee.getRole().equals("Manager")) {
employeeRepository.delete(employee);
}
}
I am trying the code from DepartmentServiceImpl class, but above is not working, but same when id is hardcoded it works.
Inside Department I have relationship like below,
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "deal")
private Set<Employee> employees= new HashSet<>();
And inside Employee I have like below,
#ManyToOne
#JoinColumn(name = "department_id", referencedColumnName = "department_id")
private Department department;
How can I fix this issue?
You are attempting to delete Employees, but your entities still have references to each other.
A better way to delete an employee is to use orphan removal and remove the employee from the collection which will trigger a delete.
Also mappedBy = "deal" should be the name of the attribute on the owning side of the relationship so this should be mappedBy = "department"
#OneToMany(
cascade = CascadeType.ALL,
mappedBy = "department",
orphanRemoval = true
)
private Set<Employee> employees= new HashSet<>();
add a method to Department to remove the Employee and keep the bidirectional relationship in sync by also removing Department from Employee
public void removeEmployee(Employee employee) {
employees.removeEmployee(employee);
employee.setDepartment(null);
}
you can then remove the 'Managers' from your Employees collection which should trigger the delete statements
List<Employee> managers = department.getEmployees.stream()
.filter(e -> e.getRole().equals("Manager"))
.collect(Collectors.toList());
for (Employee manager : managers) {
department.removeEmployee(manager);
}
Not tested but should work fine:
Try tweaking your code a little like this:
Set<Employee>employees= new HashSet<>();
for (Employee employee : department.getEmployees()) {
if (employee.getRole().equals("Manager")) {
employees.add(employee);
}
}
department.setEmployees(employees);//I assume you have a setter
departmentRepository.save(department); //I hope you understand what departmentRepository means
Here you are reassigning the valid employees list.
You could follow another method, instead of deleting each entity separately, you could call a bulk-save using saveAll(...) method on the valid list.

Best design pattern for Spring Boot CRUD REST API with OneToMany relationships

I'm struggling to find what feels like a good design for a Spring Boot CRUD REST API app that involves several OneToMany relationships w/ join tables. For example, consider this DB structure in MySQL which allows one "Recipe" to be associated with several "Recipe Categories":
create table recipes
(
id int auto_increment primary key,
name varchar(255)
);
create table recipe_categories
(
id int auto_increment primary key,
name varchar(64) not null
);
create table recipe_category_associations
(
id int auto_increment primary key,
recipe_category_id int not null,
recipe_id int not null,
constraint recipe_category_associations_recipe_categories_id_fk
foreign key (recipe_category_id) references recipe_categories (id)
on update cascade on delete cascade,
constraint recipe_category_associations_recipes_id_fk
foreign key (recipe_id) references recipes (id)
on update cascade on delete cascade
);
On the Java side, I'm representing the structures as JPA entities:
#Entity
#Table(name = "recipes")
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
#JsonManagedReference
private Set<RecipeCategoryAssociation> recipeCategoryAssociations;
// ... setter/getters ...
}
#Entity
#Table(name = "recipe_categories")
public class RecipeCategory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "name", nullable = false)
private String name;
// ... setter/getters ...
}
#Entity
#Table(name = "recipe_category_associations")
public class RecipeCategoryAssociation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "recipe_category_id", nullable = false)
private RecipeCategory recipeCategory;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "recipe_id", nullable = false)
#JsonBackReference
private Recipe recipe;
// ... setter/getters ...
}
This works OK, but my hang-up is that to persist/save a new Recipe via REST JSON API, the caller needs to know about the join table recipe_category_associations. For example a PUT request w/ this payload could add a new Recipe to the DB associating it with the "category foo" recipe category:
{
"name": "Chicken soup",
"recipeCategoryAssociations": [{
"recipeCategory": {
"id": 123,
"name": "category foo"
}
}]
}
Using this in the controller:
#PutMapping(path = PATH, produces = "application/json")
#Transactional
public #ResponseBody Recipe addNewRecipe(#RequestBody Recipe recipe) {
return recipeRepository.save(recipe);
}
To me, the inclusion of "recipeCategoryAssocations" key in the JSON payload feels weird. From the client POV, it doesn't really need to know the mechanism creating this association is a join table. Really, it just wants to set a list of recipe category ids like:
{
"name": "Chicken soup",
"recipeCategories": [123, 456, ...]
}
Any tips how best to accomplish this in nice way? It'd be nice if I can keep the REST implementation super clean (e.g., like I have now with one recipeRepository.save(recipe); call). Thanks in advance.
When writing software we expect requirement to change. Therefore we want to make sure our code will be flexible and easy to evolve.
Coupling our server response with our DB structure makes our code very rigid. If a client needs a new field or if we want to arrange the DB schema differently everything will change.
There are several different approaches to designing your software correctly. A common approach is called "Clean Architecture" and is outlined in a book by this title by the great Uncle Bob. The Book itself outlines the approach in high level but there are many example projects online to see what it means in action.
For example this article by my favourite Java blog:
[baeldung.com/spring-boot-clean-architecture][1]
If you are looking for something simpler, you can follow the ["3-Tier Architecture"][2] (not really an architecture in my mind). Separate your code in to 3 layer:
Controller/Resource/Client
Service/BusinessLogic
Repository/DataAccess
Each layer will use a different data object. the business logic layer will have the object in it's purest form without constraints regarding who will want to read it and where it is stored and will be mapped/converted to the objects in the other layers as needed.
So in your case you might have 3 (or more) different objects:
RecipeDTO
Recipe
model.Recipe (and model.RecipeCategoryAssociation etc.)
Make sure that the Business level object only have fields that makes sense from a business logic. The code in each layer will use the objects that are relevant to that layer. When a rest controller class for example calls the business logic server it will need to convert the DTO object to the Business level object for example. Very important to maintain this separation between layers

JPA Hibernate - how to (REST api) POST object with foreign key as ID?

I have two entities, with one-to-many mapping. Now, I would like to add new object to database (new employee with department foreign key).
What's the easiest way to do that?
That's how entities looks like:
#Entity
public class Employee {
...
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "department_id", nullable = true)
private Department department;
}
#Entity
public class Department {
...
#OneToMany(mappedBy = "department", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonBackReference
private Set<Employee> employees;
}
The goal is to pass this kind of json:
{
"surname": "smith",
"department": 1
}
Fail safe strategy
Search employee by surname: smith. If does not exist, return a json with this message: "smith user does not exist" and http status: 400 (bad request)
Search department by id: 1. If does not exist, return a json with this message: "department does not exist" and http status: 400 (bad request)
If the previous sentences were fine, you will have an instance of Employee and Department, ready to use
Employee emp = employeeRepository.findBySurname("smith");
//if emp does not exist, instance a new one
Department dep = employeeRepository.findBySurname("smith");
emp.setDepartment(dep);
employeeRepository.save(emp);
Hibernate and jpa must persist your simple entity
Direct strategy
Instance your employee with smith surname
Instance your Department with id: 1
Set your department instance in employee instance
Execute employeeRepository.save(emp);
Use a try catch to return a json message in case of error: "Internal error when employee was being persisted..."

Spring REST api - foreign key ID instead of entire object

I have a problem with Json returned by my REST api GET method.
That's how my entities looks like:
#Entity
public class Employee {
...
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "department_id", nullable = true)
private Department department;
}
#Entity
public class Department {
...
#OneToMany(mappedBy = "department", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonBackReference
private Set<Employee> employees;
}
And this is the answer I get, while I'm trying to GET Employee by its id:
{
"id": 1,
"surname": "smith",
"department": {
"id": 1,
"name": "HR",
"room": "13"
}
}
Now, Instead entire Department object, I would like to get just simple id: "department_id": 1, and I don't know how to do that.
Second question: what's the good practise in this situation in REST api? Should I leave it like it is; expose only id (what I'm asking you how to do); or use DTO and not showing it at all?
Moreover, anyway I'm going to add _links to this user's department, and in this case i thought that leaving only id should be ok (tell me if I'm wrong).
Looking forward for your answers!
A good practice is to define a DTO that represents the data that is exposed by your API.
This should be decoupled from your domain (Employee) as it will offer you more flexibility, just like what you want to achieve.
class EmployeeDTO extends RepresentationModel {
private long id;
private String surname;
private long departmentId;
// getters and setters
}
This should work. Of course you need to map your Employee entity to the EmployeeDTO. RepresentationModel contains the _links property that you want for the HATEOAS (for example, have a look at
https://www.baeldung.com/spring-hateoas-tutorial )
About exposing the id from your database, I think that a good reason for not doing it is that you are giving information about your database size for free and it's something that you might not want to. More information could even be derived from that.
Here you can find a good discussion on the topic:
Exposing database IDs - security risk?
I would suggest to have a look at UUID which is a universally unique alphanumeric identifier that doesn't expose this information about your data.
More about UUID: https://www.baeldung.com/java-uuid
#JsonIgnoreProperties
To just get department id without changing any implementation you may use #JsonIgnoreProperties({"name", "room"}) as following
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "department_id", nullable = true)
#JsonIgnoreProperties({"name", "room"})
private Department department;
which will respond with the following
[
{
"id": 1,
"surname": "smith",
"department": {
"id": 1
}
}
]
You may also like to explore other ways to achieve the same here
Best Practices
We should never expose and return our modal and entities as a response to APIs. We may create the DTOs/DAOs to receive and transfer the objects and data. You may also convert the entity to DTO and DTO to entity using mappers.
In the case of DTO, you may just include the department id and may fetch the object if required using the repository.

Unable to parse Json to Object : JPA ManytoOne unidirectional

I am using spring data rest with repositories, db is mysql.
I have objects Parent and child. Relationship is Many to one .
child to Parent relationship is unidirectional. I do not have List of child obj inside Parent.
Parent{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id = null;
#NotNull(message = "name of user should not be null")
#Size(min=2, max=30)
private String name;
}
Child{
private String name;
#ManyToOne
private Parent parent;
}
I have an parent "1" and i want to refer it to newly creating child. The json input for new child "POST" req is
{
"name":"child name",
"parent":{
"id":1
}
}
Need help in associating parent "1" in newly creating child.
Is there any changes required in json format? I tried with "parent_id" as well but still having error.
Since spring-data-rest uses HATEOAS. The following format worked.
{
"name":"testname1",
"parent": "http://localhost:8080/parent/2"
}
Thanks to all who tried to answer.

Resources