How should i define which Hibernate mapping to use, and when/where to use it? - spring-boot

i and having a use case where there are 2 kinds of users namely the "clients" and "professionals". These 2 entities have a parent entity called the "users" where each "user" has one entry in either "client/professional" depending on their role.
Let's consider a "clients".
A "user" has a one-to-one mapping with a "client"
A client might have several "companies" under him, i.e "client" has one-to-many relationship with "companies".
I am creating a REST API for this use case using spring boot. I still dont have any idea about why i should be using mapping in Hibernate. So far the only advantage i see is that, the CASCADING property of it. If a "user" gets removed, all the tables having the "user-id" will also be flushed. But consider a scenario where i need to add "companies" for a "client". I am confused to whether i should persist "companies" via "clients" entity or should i directly persist to "client" entity. I dont see any major advantage here because in both cases we are checking whether a "client" exists with the given ID before persisting in the "clients" table.
User Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long UID;
private Integer userRoleId;
private String username;
private String email;
private String phoneNumber;
private String firstName;
private String lastName;
private Long dateOfJoin;
private Boolean activeStatus;
private Long createdAt;
private Long updatedAt;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
private Client client;
}
Client Entity
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long CID;
#Column(unique = true)
private Long userId;
private Long createdAt;
private Long updatedAt;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "client")
private ClientCompany clientCompany;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "userId" ,referencedColumnName = "UID", insertable = false, updatable = false)
private User user;
}
Client Company Entity
public class ClientCompany {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long CCID;
private Long clientId;
private String email;
private String phoneNumber;
public String streetAddress1;
public String streetAddress2;
public String zipCode;
public String city;
public String state;
public String country;
private Long createdAt;
private Long updatedAt;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "clientId", referencedColumnName = "CID", insertable = false, updatable = false)
private Client client;
}

The advantage of using Hibernate/JPA is that you do not need to code JDBC calls.
You just use objects.
In your scenario,
load a Client instance from the database;
create a ClientCompany object;
assign the Client instance to it (no need to check the client existence since you loaded it from the database);
save to database.
Hibernate will take care of everything without you writing any SQL statements.
Step 1) can also be replaced with creating a new Client that will be saved to the database, but again Hibernate will handle saving correctly (if you configured it correctly).

Related

How can I retrieve all the children of a record in this Hibernate #ManyToOne relation?

I am working on a Spring Boot project using Spring Data JPA and Hibernate mapping. I have the following doubt about how can I implement the following query.
I have an User entity class like this:
#Entity
#Table(name = "portal_user")
#Getter
#Setter
public class User implements Serializable {
private static final long serialVersionUID = 5062673109048808267L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "first_name")
#NotNull(message = "{NotNull.User.firstName.Validation}")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "surname")
#NotNull(message = "{NotNull.User.surname.Validation}")
private String surname;
#Column(name = "sex")
#NotNull(message = "{NotNull.User.sex.Validation}")
private char sex;
#Column(name = "birthdate")
#NotNull(message = "{NotNull.User.birthdate.Validation}")
private Date birthdate;
#Column(name = "tax_code")
#NotNull(message = "{NotNull.User.taxCode.Validation}")
private String taxCode;
#Column(name = "e_mail")
#NotNull(message = "{NotNull.User.email.Validation}")
private String email;
#Column(name = "pswd")
#NotNull(message = "{NotNull.User.pswd.Validation}")
private String pswd;
#Column(name = "contact_number")
#NotNull(message = "{NotNull.User.contactNumber.Validation}")
private String contactNumber;
#Temporal(TemporalType.DATE)
#Column(name = "created_at")
private Date createdAt;
#Column(name = "is_active")
private boolean is_active;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "portal_user_user_type",
joinColumns = { #JoinColumn(name = "portal_user_id_fk") },
inverseJoinColumns = { #JoinColumn(name = "user_type_id_fk") }
)
Set<UserType> userTypes;
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
public User() {
super();
}
public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
String email, String pswd, String contactNumber, Date createdAt, boolean is_active) {
super();
this.firstName = firstName;
this.middleName = middleName;
this.surname = surname;
this.sex = sex;
this.birthdate = birthdate;
this.taxCode = taxCode;
this.email = email;
this.pswd = pswd;
this.contactNumber = contactNumber;
this.createdAt = createdAt;
this.is_active = is_active;
}
}
The instances of this class represents users of my system. An user can have a single specific parent (the concept is similar to that of a referral: an user can bring another user in the system). This is handled by this ManyToOne recursive relationship:
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
Basically an user contains is parent (who bring him\her into the platform). It works fine. So retrieving an user I can easily retrieve the information of who is its parent (it is contained into the retrieved User object).
Now I need to implement the inverse behavior: I have to define a "query" that starting from a parent retrieve all its children.
The previous User entity class maps the following DB table:
The highlighter parent_id contains the FK that define this recursive relationship. So it contains the PK of another user that is the parent.
I have this UserRepository repository interface (it extents the JpaRepository interface)
public interface UsersRepository extends JpaRepository<User, Integer> {
User findByemail(String email);
List<User> findByUserTypes_TypeName(String typeName);
}
As you can see I am using a "query by method" style. Is it possiblem implement a behavior like this using "query by method" style? (in case also JPQL could be fine)
You can do this
List<User> findByParent_Id(Integer id);
Or you can do this
#Query("SELECT u FROM User u WHERE u.id = ?1")
List<User> getReferredUsers(Integer id);
The relationship between the user and the parent is unidirectional in the given code. By making it bidirectional, it is easy to query the data in either ways.
Refer to below code to make it bidirectional. Also ensure the relevant FetchType to avoid the performance risk. Here FetchType.LAZY is used for one to many association so it queries the data using the proxy reference when needed.
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
#JsonBackReference
private User parent;
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
private Set<User> userSet = new HashSet<>();
Child entities are fetched only when parent.getUserSet is used because of the FetchType.Lazy
public Set<User> getUsers(int id) {
User parent = userRepository.getById(id);
return parent.getUserSet();
}

How to define a field for loading multiple images in spring boot model

I am developing an application for vehicle stock tracking system using spring boot, angular and mysql. Multiple images of the vehicle will be loaded from the interface. Normally, when there is only one image, I define a field of type byte [] with #lob annotation. But how can I keep it in the database when more than one image comes in. I think a relational structure is required but I couldn't.
public class User extends BaseEntity{
#Column(name = "TC_NUM", unique = true)
#NotNull
private String tcNum;
#Column(name = "EMAIL", unique = true)
#NotNull
private String email;
#Column(name = "USERNAME", unique = true)
#NotNull
private String username;
#Column(name = "PASSWORD")
#NotNull
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_ROLES",
joinColumns = #JoinColumn(name = "USER_ID"), inverseJoinColumns = #JoinColumn(name = "ROLE_ID"))
#NotNull
private Set<Role> roles;
}
Yes, you need to have One to Many relation.
Add another db table and entity for vehicle images. Let's say we called it VehicleImage:
#Entity
#Table(name="vehicle_image")
public class VehicleImage{
#Id
private Long id;
#Lob
#Column(name = "image", columnDefinition="BLOB")
private byte[] image;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
public VehicleImage() {}
// getters and setters
}
And add mapping to your User class like that:
public class User extends BaseEntity{
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<VehicleImage> vehicleImages;
public void addVehicleImage(VehicleImage vehicleImage) {
vehicleImages.add(vehicleImage);
vehicleImage.setUser(this);
}
public void removeVehicleImage(VehicleImage vehicleImage) {
vehicleImages.remove(vehicleImage);
vehicleImage.setUser(null);
}
//rest of your class
}
As you can see I've also added two utility methods to User class. For details, see this great post by Vlad Mihalcea -> https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
Now you can persist your images like that:
User user = new User();
user.addVehicleImage(
new VehicleImage (imageBytesArray)
);
entityManager.persist(user);

Map primary key to composite key in JPA

I have 2 tables namely user & user_session.
User table has user_id as a primary key which is referrers to user_session table.
Plus user_session has composite key including session_intime and user_id.
I have designed my entity in JPA. Now I want to map these two entities. I have tried to map these two tables. But my application build failed. Can you please help me out?
#Entity
#Table(name="user")
public class User {
#Id
#Email
#Column(name = "user_id")
private String userId;
#Column(name = "password")
private String password;
#Column(name = "fname")
private String fname;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "userId", referencedColumnName = "user_id")
private UserSession userSession;
}
#Entity
#Table(name="user_session")
public class UserSession{
#EmbeddedId
private UserSessionPK userSessionPK;
#Column(name = "remote_ip")
private String remoteIp;
}
#Embeddable
public class UserSessionPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "user_id")
private String userId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_in")
private Date timeIn;
}
I want to map user_id of User table to user_id of UserSessionPK. I am new to JPA, so I don't know how to map with embeddable class.
Remove the mappedBy attribute. This attribute is used when you have bidirectional relationship to indicate which side of the relationship is the owner.
But you will need to set the Foreign Key aka JoinColumn
#JoinColumn("user_id")
#OneToMany(fetch = FetchType.LAZY)
private UserSession userSession;

Can't delete child entity without deleting parent entity, regardless of CascadeTypes?

I'm trying to connect an entity (User) to entities they create which will be Surveys.
I have two repositories, one UserRepository and one SurveyRepository. I can load Surveys according to which User has them and currently they are all mapped by the User_ID, which is a field on the Survey entity.
However, when I try to remove a Survey, this removes my User whenever I define CascadeType.ALL.
But when I don't use that, I get another error "Caused by: java.sql.SQLIntegrityConstraintViolationException:"
I'm gussing this is all related to the password encryption I'm using, but I am not even trying to delete the User entity, I'm just deleting the Survey, which holds a reference, or an ID to the Survey..
I've tried CascadeType.All on both sides, and I've tried not having any CascadeType at all as well.. If I have it on both sides, this deletes the user whenever I tell my surveyRepository.delete(currentSurvey);
And whenever I don't have it on both sides, I get the exception above..
User Entity:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long id;
#NotEmpty
#Email
#Column(unique = true)
private String email;
private String password;
#NotBlank
private String username;
#NotBlank
private String firstName;
#NotBlank
private String lastName;
#NotBlank private String role;
#OneToMany(fetch = FetchType.EAGER)
private Set<Survey> surveys = new HashSet<>();
Survey Entity:
#Entity
#Table(name = "survey")
public class Survey {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "survey_id")
private Long id;
private String title, creator, description;
private LocalDate date = LocalDate.now();
#OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "survey_id")
#OrderBy("position ASC")
private Set<Question> questions = new HashSet<>();
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
I'm just not sure how I can tell JPA/Hibernate not to touch the User whenever we delete the Survey.
It doesn't matter if I save the User with Survvey or not does it?
Basically I've tried a lot of options and I figure I'm not quite grasping the issue, and I suspect it's about the annotations on the User side, but I still feel as if I should be able to delete the child entity with no problem at all since I am not touching the parent entity?
This is because of EAGER fetch type in User class for surveys.
You delete survey but because it is existed on surveys set in user yet, it wouldn't be deleted actually.
You need to do like this:
// User class
#OneToMany(cascade=CascadeType.ALL, orphanRemoval=true, mappedBy="user")
private Set<Survey> surveys = new HashSet<>();
//Survey class
#ManyToOne
#JoinColumn(name = "user_id")
private User user;

Spring POST request with relationship

I have two entity types in Spring with a relationship:
#Entity
public class Domain {
public Domain() {}
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
private String name;
private String description;
private String image;
#OneToMany(cascade=CascadeType.ALL, targetEntity=Subdomain.class,fetch = FetchType.LAZY)
#JoinColumn(name="domain_id")
private Set<Subdomain> subdomain = new HashSet<>();
//Default getters and setters
}
And the type subdomain:
#Entity
public class Subdomain {
public Subdomain() {}
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
private String name;
#JsonIgnore
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "domain_id", nullable = false)
public Domain domain;
//Default getters and setters
}
This works perfect with a get request, the relation is fetched. But how does it works with post request? I would create a new subdomain with the relationship to an existing domain:
"domain_id": "2"
And this:
"domain_id": "http://localhost/subdomain/2"
But this doesn't work. What is the best way to solve this?
Could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement

Resources