How to use Spring Boot CRUD API to insert data in multiple tables using one POST endpoint - spring-boot

How can a data be inserted using single POST endpoint in multiple tables. For example there are two tables
1. Employee
2. Department
These two tables have a primary key and foreign key relationship.
How to achieve data insertion in two tables using a single POST endpoint ?

Ok I see what you want.... your entities have to look like this...
You have to create a one to one relationship something like this:
Department entity:
#Entity
#Table
#Data
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
}
Employee entity:
#Entity
#Table
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String email;
private String address;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "department_id", referencedColumnName = "id")
private Department department;
}
And than you can add Data on Startup like this:
#Component
public class DBSeeder implements CommandLineRunner {
#Autowired
private EmployeeRepository repository;
#Override
public void run(String... args) throws Exception {
Department dep1 = new Department();
dep1.setName("Demolition");
dep1.setDescription("Do demo");
Employee emp1 = new Employee();
emp1.setName("John Rambo");
emp1.setEmail("john.rambo#demolition.com");
emp1.setAddress("Demolition Av. 5");
emp1.setDepartment(dep1);
this.repository.save(emp1);
}
}

#Repository
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
Employee save(Employee employee);
}
Do you also ask how the entity objects have to look like?

Related

How implement ManyToOne delete on cascade?

This is my code
#Entity
public class School{
#Id
#GeneratedValue
private long id;
}
#Entity
public class Student{
#Id
#GeneratedValue
private long id;
#ManyToOne(cascade=CascadeType.REMOVE)
#JoinColumn(name="STUDENT_ID")
private School school;
}
Im using JPA delete (Impl code)
#Autowired
SchoolRepository sr;
#Override
void delete(Integer id) throws Exception {
sr.deleteById(id);}
In another answer it says to set all his child to null , how i make this? Is there any way to make it easier? Thx
You must add one List of Student on Entity School annotated by #OneToMany with cascade = CascadeType.REMOVE, to remove all students of school.
#Entity
public class School{
#Id
#GeneratedValue
private long id;
#OneToMany(cascade = CascadeType.REMOVE, mappedBy = "school")
private List<Student> students;
}
#Entity
public class Student{
#Id
#GeneratedValue
private long id;
#ManyToOne
#JoinColumn(name="STUDENT_ID")
private School school;
}

JPA issue (lazy loading? eager ?)

Iam building a simple Spring Boot app, with 2 entities:
- Student model
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Student {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String password;
private boolean active;
private Date dob;
private String roles;
#ManyToOne
private Training training;
}
- Training model
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Training {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int duration;
#OneToMany(mappedBy = "training")
#JsonIgnore
private Collection<Student> students;
}
EDIT
I run the app by adding 2 resources in the db:
public static void main(String[] args) {
SpringApplication.run(MsSchoolingSbApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Training t1=trainingRepo.save(new Training(null,"php", 20, null));
Training t2=trainingRepo.save(new Training(null,"java", 20, null));
Student st=new Student(null, "XXXX", "ZZZZ", true,new Date(),"ADMIN",t1);
Student st2=new Student(null, "XXXXX2", "ZZZZZ2", true,new Date(),"USER",t2);
studentRepo.save(st);
studentRepo.save(st2);
}
END EDIT
EDIT 2
- StudentRepo
#RepositoryRestController
public interface StudentRepo extends JpaRepository<Student, Long>{
public List<Student> findByNameStartsWith(String name);
Optional<Student> findByName(String name);
}
- TrainingRepo
#RepositoryRestController
public interface TrainingRepo extends JpaRepository<Training, Long> {
}
END EDIT 2
i've tried to put fetch = FetchType.EAGER or LAZY, i've also added #JsonIgnore but as soon as i fill the db with new data (trainings and students) and run the app, i get this message:
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.schooling.models.Training.students, could not initialize proxy - no Session
What am i doing wrong ?
The problem you got must have related to how you use those 2 entities so you need to provide more information about how you use it.
You might want to look out for your problem in this tutorial: https://www.baeldung.com/hibernate-initialize-proxy-exception
Do not use Lombok's #Data annotation on #Entity classes.
Reason: #Data generates hashcode(), equals() and toString() methods that use the generated getters. Using the getter means of course fetching new data even if the property was marked with FetchType=LAZY.
Somewhere along the way hibernate tries to log the data with toString() and it crashes
EDIT
you can exclude the relation from the toString method by adding, for example in my case:
#ToString(exclude = {"students"})

How to Inner Join using Spring Boot JPA / Hibernate in Repository?

I am currently learning spring boot , hibernate and Spring Boot JPA
I developing a Classroom App for coaching centers and institutes .
In it, students enrolled to multiple courses in a single institute
The Student Entity class :
#Entity
#Table(name = "student")
public class Student {
private String name;
private String dob;
private String gender;
private String address;
private String email;
private Integer mobile;
private String joined;
private Integer instID;
#Id
private String studentid;
getters and setters()....
}
Course Table Entity class
#Entity
#Table(name = "courses")
public class Course {
private String name;
private String description;
private String logo;
private String start;
private String end;
private Integer fee;
#Id
private String courseid;
private Integer instID;
getters and setters();
}
Enrolled Classes Table's Entity class
public class EnrolledCourses {
#Id
String enrollID;
String courseid;
String studentid;
Date joined;
getters and setters()...
}
JPA Repository
#Repository
public interface StudentRepository extends CrudRepository<Student, String> {
Student findTopByInstIDOrderByStudentidDesc(int instID);
}
#Repository
public interface CourseRepository extends CrudRepository<Course,String> {
}
#Repository
public interface EnrolledRepository extends CrudRepository<Course,String> {
}
My Need
Now I am retrieving enrolled students for a given course in a given institute... by using this MySQL query
SELECT
`enrolled_courses.enrollID`,
`student.name`, `student.studentid`
FROM `enrolled_courses`
INNER JOIN `student`
WHERE
`enrolled_courses.studentid` = `student.studentid`
AND
`student.instID` = 13
AND
`enrolled_courses.courseid` = '13I01C' ;
Now I need to implement this Inner join query in CourseRepository (or enrolledstudent repo)
How to achieve this ?
Please guide me
If we use hibernate mapping in EnrolledCourses entity like
#Entity
public class EnrolledCourses {
#Id
String enrolledId;
#ManyToOne
Student student;
#ManyToOne
Course course;
Date joined;
getters and setters()...
}
from the above mappings without using any SQL queries, you can retrieve all student who comes under a particular course
By using the method in the interface
#Repository
public interface EnrolledRepository extends CrudRepository<EnrolledCourses,String> {
List<EnrolledCourses> findByCourse_CourseId(String courseId);
}
if we have some relations between the entities we can easily retrieve all the fields using Jpa.

How can I add a tenant condition to Spring Data JPA Default and Dervied Queries

I have a Springboot Application with Repositories having Spring Data JPA Queries like findOne, findAll and also derived ones like findByID or findByName etc.
What I want to achieve is multitenancy. All entities have an "account_id" column which holds the tenant.
How do I add a filter like "account_id" to all the queries metioned above without using derived queries that contains those name slike findIdAndAccountid (which would be findone)
#Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {
Category findByName(String name);
}
Here's the corresponding entity
#Entity
#Table(name = "unit")
#Data
public class Unit {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#Column(name = "account_id")
private Long account_id;
}
I know most people use schemas as tenant separation but that's impossible for me. Is there a way (I didn't find one) to add such a tenant filter condition on those queries without writing NamedQueries or using DerivedQueries. An elegeant solution like annotate the repository or entity or maybe the queries that all queries should add the additional filter "account_id"?
You can add Where clause on your Entity classes (Didnt had time to test )
#Entity
#Table(name = "unit")
#Data
#Where(clause = "account_id= :account_id")
public class Unit {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#Column(name = "account_id")
private Long account_id;
}
Update and Solution
1. Create a Filter & FilterDef on the entity like so
#FilterDef(name="accountFilter", parameters=#ParamDef( name="accountId", type="long" ) )
#Filters( {
#Filter(name="accountFilter", condition=":accountId = account_id")
} )
public class Category {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#Column(name = "account_id")
private Long account_id;
}
enable filtering in the controller by autowiring entitymanager, writing a method to enable the filter and activate the filter in #ModelAttribute for each request
#RestController
#RequestMapping(path = "/categories",produces = MediaType.APPLICATION_JSON_VALUE )
public class CategoryController {
private final CategoryRepository repository;
#Autowired
private EntityManager entityManager;
CategoryController(CategoryRepository repository) {
this.repository = repository;
}
private void activateFilter() {
Session session = entityManager.unwrap(Session.class);
Filter filter = session.enableFilter("accountFilter");
filter.setParameter("accountId", Long.valueOf(TenantContext.getCurrentTenant()));
}
#ModelAttribute
public void initFilter() {
activateFilter();
}
... your rest methods here
}

i'm getting null value in a child table as a foreign key of parent table using spring data rest or spring data jpa accosiation

enter image description here In this image first address for empId 1 and last two records are empid 2 (empid 2 haveing to address)
file:///home/user/Pictures/fk.png
#Entity
#Table(name = "Employee")
public class Employee {
#Id
#GeneratedValue
private Integer id;
private String name;
private Integer sal;
#OneToMany(cascade = CascadeType.ALL,mappedBy="employee")
private List<Address> addresses;
//getter setter
Child entity
#Entity(name="Address")
public class Address {
#Id
#GeneratedValue
private Integer aid;
private String city;
private String state;
#ManyToOne
#JoinColumn(name="id")
private Employee employee;
//getter setter
Repository
#Repository
#RepositoryRestResource(path="employee")
public interface EmployeeRepo extends JpaRepository<Employee,Integer> {
}
Input from RestClient
{
"name":"rdhe",
"sal":"20000",
"addresses":[{
"city":"hyd",
"state":"ts"
}]
}
if i use spring data jpa then code will be
// jpa Repository
public interface EmployeeRepo extends JpaRepository<Employee,Integer> {
}
// EmployeeServer class
#Service
public class EmployeeService {
#Autowired
EmployeeRepo employeeRepo;
public void saveEmployee(Employee employee){
employeeRepo.save(employee);
}
}
// controller
#RestController
public class EmployeeController {
#Autowired
EmployeeService employeeService;
#PostMapping(path="/save")
public void saveEmp(#RequestBody Employee employee){
employeeService.saveEmployee(employee);
}
}
if i'll use spring-data-rest at that time no need to create employeeService and controller class
I was getting the same problem until JsonManagedReference came to my rescue.
Try changing your entities to include them like this:
In the Employee Entity:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy ="employee")
#JsonManagedReference
private List<Address> addresses;
In the Address Entity:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id", nullable = false, updatable = false, insertable =true)
#JsonBackReference
private Employee employee;
I was not able to find why it works this way, so please let me know if you come to know :)
It is probably due to the fact that your mentioning #JoinColumn(name="id"). The name attribute in #JoinColumn defines the name of the foreign key field in the child table. Since you are specifying foreign key column as id on hibernate, it could be the issue. Please update it to the same name(ie fk_empid) as specified in database, it should work...

Resources