How to make sure country is not added twice in mysql database in hibernate using spring mvc one to many relationship? - spring

I have two entity first one is Student and the other one is an address. It is one too many relations ie one address can have many students. Now I have is a registration page. When I first register a student say with country name united states, it is saved in database giving primary id as 1 to united states and correspondingly gives correct id in student's database. But when I again try to register the next student with different information but the same country in my case united states it gives me a new primary key for the same country. But as this one is, one to many relationships I am thinking if there is anything in hibernate that maps to the same id in address database, therefore, I will only have one value of the united states in the address database. I need to have only a single entry of the united states a database. What is the appropraite way of doing need? Thank you
This one is Address table
#Entity
#Table(name = "tbl_address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "address_id")
private int addressId;
private String country;
#OneToMany(targetEntity = Student.class, mappedBy = "address")
private List<Student> student;
This one is Student table
#Entity
#Table(name = "tbl_student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "student_id")
private int studentId;
#Column(name = "first_Name")
private String firstName;
#Column(name = "second_Name")
private String secondName;
private String email;
#Column(name = "mobile_no")
private float mobileNo;
#DateTimeFormat(pattern = "yyyy-MM-dd")
#Temporal(TemporalType.DATE)
private Date dob;
private String gender;
#ManyToOne(cascade = {CascadeType.MERGE , CascadeType.ALL} )
#JoinColumn(name = "address_id")
private Address address;
}
This one is just the implementation in StudentRepositoryImpl class
#Override
public void saveUserInfo(Student user) {
Session session = HibernateUtil.getSession(sessionFactory);
session.save(user);
}
#Override
public void saveAddressInfo(Address address) {
// TODO Auto-generated method stub
Session session = HibernateUtil.getSession(sessionFactory);
session.save(address);
}

First option. On UI you should have a list of available countries (so I expect you already have populated country table in database). So UI will display to users available country names, but on backend side you will operate with countryId.
Second option. In case on UI you want users insert any string as country name, and if you want to register country on any new provided country name. For that you need to have unique index in country table on country name (it's up to you to decide whether it will be case insensitive or not).
Before saving student entity, you should fetch available country by it's name. if such one exist - use countryId with saving studend. if not exist - create a new country entity, and use generated countryId in studend entity.
Second option is risky in that way users could provide any values for country names, and probably with typo, so the first option is preferable.

Have a separate table for Country (That would be called master data). Then the Address entity would be something like this:
#Entity
#Table(name = "tbl_address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "address_id")
private int addressId;
#ManyToOne
private Country country;
#OneToMany(targetEntity = Student.class, mappedBy = "address")
private List<Student> student;
You can get the list of students doing a join query with Country and Address tables.
Hope this is helpful

Related

Hibernate JPA scenario to solve?

There is a class Consultant. A consultant can have many kind of experience like salaried, self-employed, freelancer. For each type of experience there are different data to save in database.
Salaried:
Total Experience
Company Name
Experience Time in years
Offer/Reveling letter Link
Self Employed:
Company Name
Total Experience
CIN_Number
GST_Number
CompanyCertificateLinkUrl
FreeLancer:
Total Experience
A user can have experience in more than one occupation type like a consultant is both salaried and freelancer, or self employed plus salaried and freelancer. So i am confused how to make the #Entity class for this type of use case.
My Solution
#Entity
class Consultant{
#Id
int id;
#OneToOne
Salaried semp;
#OneToOne
SelfEmployed selfemp;
#OneToOne
Freelancer femp;
}
But i think this is not good practice as it will lead to many null field in the database.
ANY BETTER SOLUTION
I think your approach is fine. #OneToOne fields are optional by default, so can be null. That means there wouldn't be a row in the corresponding tables, so you'd only have up to two null values per row in the Consultant table.
If you're really concerned about nulls in the database, then you can map the relationships the other way, so:
#Entity
class Consultant{
#Id
int id;
#OneToOne(mappedBy = "consultant")
Salaried semp;
#OneToOne(mappedBy = "consultant")
SelfEmployed selfemp;
#OneToOne(mappedBy = "consultant")
Freelancer femp;
}
This way, if there's no row in the Salaried table that's related to the Consultant, the semp field will be null in the Consultant object.
you can do with two classes consultant and profession(id,name) and the relation OneToMany,ManyToOne
Consultant's entity
#Entity
class Consultant{
#Id
private int id;
#OneToMany(mappedBy = "consultant",cascade = CascadeType.All)
List<ConsultantProfession> cp;
}
Profession's entity
#Entity
class Profession{
#Id
private int id;
private String name;
#OneToMany(mappedBy = "profession", cascade = CascadeType.All)
private List<ConsultantProfession> cp;
}
ConsultantProfession's entity
#Entity
#Table(name="consultant_profession")
public class ConsultantProfession{
#Id
private int id;
// this is the link to the consultant class
#ManyToOne
#JoinColumn(name="consultant_id")
private Consultant consultant; // this name is specified in the class seeing patients as value of parameter `mappedBy`
// this is the link to the profession class
#ManyToOne
#JoinColumn(name="profession_id")
private Profession profession;
}

Delete object in OneToOne relationship ConstraintViolationException

I'm trying to delete entity which is the owner of the relationship, but I am getting an exception as follows:
org.h2.jdbc.JdbcSQLException: Referential integrity constraint
violation: "FKS59BBPCYQ1GUKBWWA61TYF8YF: PUBLIC.RESERVATIONS FOREIGN
KEY(CAR_LICENSE_ID) REFERENCES PUBLIC.CARS(LICENSE_ID) ('EPA13S')";
SQL statement:
I know that is because of trying to delete an object to which another one has a reference with fk_key. Here is my model:
public class Reservation
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Enumerated(EnumType.STRING)
private DriverType driverType;
private LocalDateTime startTime;
private LocalDateTime stopTime;
#OneToOne(cascade = CascadeType.PERSIST)
private Car car;
private BigDecimal cost;
}
public class Car
{
#Id
#NonNull
#Size(min = 6, max = 6)
String licenseId;
#OneToOne(mappedBy = "car", cascade = CascadeType.REMOVE)
Reservation reservation;
}
How could I possibly deal with such scenario? I would like to delete car from parking when the reservation ends as I don't need it and having license id as pk_key make it vulnerable for trying to insert new car with upcoming reservation even though the previous one has ended.
Deleting car:
carRepository.deleteByLicenseId(reservation.getCarLicenseId());
#Query("DELETE FROM Car c where c.licenseId = :licenseId")
void deleteByLicenseId(#Param("licenseId") String licenseId);
I assume you are extending Spring CrudRepository<Reservation, Long>
The joinColumn is on the Reservation side and from what I can see what you want to do is to delete the Reservation as well. So why not delete it from the owning side, which looks like the reservation.
Change Reservation to.
#OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
Rather use the following for delete.
reservationRepository.delete(reservation);
This is a bidirectional relationship. Add this to Car class:
public void removeReservation() {
if (this.reservation != null) {
this.reservation.setCar(null);
this.reservation = null;
}
}
Call car.removeReservation().
Then delete car.

Like Button Implementation in Spring Boot

In my application users can post articles. And other users can like these articles.
Article class :
#Entity
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "article_id")
private int id;
#Column(name = "article_title")
private String articleTitle;
#OneToMany(mappedBy = "event")
private List<PeopleWhoLiked> peopleWhoLiked;
}
#Entity
public class PeopleWhoLiked {
#EmbeddedId
private PeopleWhoLiked id;
#ManyToOne #MapsId("articleId")
private Article article;
#ManyToOne #MapsId("userId")
private User user;
}
And there is category entity.Every article has one category.
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "category_id")
private int id;
#NotEmpty
#Column(name = "categoryName")
private String categoryName;
#OneToMany(mappedBy = "article")
private List<Article> articleList;
}
My Like Table
Article_id User_id
x x
These are both foreign keys to related tables.
With
category.getArticleList(); function i can show articles to users.They can like articles.But the thing is the system doesn't know that if the article was liked by user already. So always like button is active.
Querying (select statement for every article on Like table) is looks like has huge time complexity and overload to the system.) Even if i do how can i post this into thymeleaf th:each statement with only Article object.
I think querying 10 article's like per time with one select statement sounds good .But again how can i pass this to thymeleaf with Article object.
Your problem with performance is caused by additional request for every row.
For 1 select returning 100 rows you make additionals 100 select to database.
If you need display complicated result build view and than map result of view to your #Entity class, which will used only for presentation purpose.

#OneToMany relationship issue while saving multiple values in Hibernate and Spring

I have two entities with #OneToMany bidirectional relationship as below:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer companyId;
private String name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="company")
Set<Employee> employees = new LinkedHashSet<>();
Employee class
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer empId;
private String name;
private String address;
private String email;
#ManyToOne
#JoinColumn(name="companyId")
Company company;
Intially i saved 2 employees as below:
Employee emp= new Employee();
emp.setName("John");
emp.setEmail("John#gmail.com");
employeeRepository.save(emp);
Employee emp2= new Employee();
emp2.setName("Smith");
emp2.setEmail("smith#gmail.com");
employeeRepository.save(emp2);
Now I want to save one employee working for 2 different companies like below:
Company company =new Company();
company.setName("Google");
Employee emp = employeeRepository.findOne(1);
company.getEmployees().add(emp);
emp.setCompany(company);
companyRepository.save(company);
Company company2 =new Company();
company2.setName("Microsoft");
company2.getEmployees().add(emp);
emp.setCompany(company2);
companyRepository.save(company2);
It is updating only second company id into employee table. I want both the companies to be assigned to that employee. How can I do that?
The issue here is that you have a one-to-many when obviously it should be a many-to-many, viz. an employee can work for more than one company and a company has many employees.
You can either change the relationship to a #ManyToMany (and use #JoinTable if required) or you can create another entity, say, CompanyEmployee to which both Employee and Company have a one-to-many-relationship. The latter approach is probably preferable as you can then record additional information about the association e.g. start_date, end-date etc.
#Entity
#Table(name = "company_employees")
public class CompanyEmployee {
#ManyToOne
private Employee employee;
#ManyToOne
private Company company;
private Date startDate;
private Date endDate;
}

How to save an object in two different tables using Spring MVC and Hibernate

Let's consider the following:
#Entity
#Table(name="person")
public class Person implements Serializable {
#Id
#GeneratedValue
#Column(name="id")
private int id;
#Column(name="firstName")
private String firstName;
#Column(name="lastName")
private String lastName;
...getters/setters...
}
and
#Entity
#Table(name="car")
public class Car implements Serializable {
#Id
#GeneratedValue
#Column(name="id")
private int id;
#Column(name="brand")
private String brand;
#Column(name="model")
private String model;
#Column(name="personId")
private String personId;
...getters/setters...
}
Let's imagine that a user is going to subscribe and enter his personal info, like first name, last name, the brand of his car, as well as the model of the car.
I do not want the personal info of the person to be stored in the same table than the car info.
I also would like to be able to retrieve the car information with the personId, this is why I have personId in the Car class.
Which annotations should I use to be able to accomplish this? Obviously I will need a constraint on the Car table and make personId a foreign key, right? What is the best way?
I have seen different things, what is the best?
In Car class, replace
#Column(name="personId")
private String personId;
with
#ManytoOne(fetch = FetchType.LAZY)
#CJoinColumn(name="person")
private Person person;
In Person class, add
#OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
private List<Car> cars;
You are now forming bi-directional one-to-many which means you can retrieve cars of person and person (ownder) of the car.
The cascade allows saving or updating of cars when person is saved. All cars are also deleted when person is removed.
It depends on your requirements.
If you want to use the same vehicle for multiple users, then you shall make it an entity, and use a many-to-many relationship.
If you don't want to change your entity structure at all, but just the database mapping then look at #SecondaryTable and #SecondaryTables annotations, they define more tables for an entity, and then you shall specify which table to use for each column (otherwise they are assigned to main table).

Resources