Map primary key to composite key in JPA - spring-boot

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;

Related

Spring data jpa hibernate one to may duplicate issue

I have one to many relation ship between a User and UserRole.
public class User {
#Id
#GeneratedValue
private long id;
#Column(unique = true)
private String username;
private String password;
#OneToMany(fetch=FetchType.EAGER)
#JoinTable(
name = "user_roles",
joinColumns = {#JoinColumn(name="userId")},
inverseJoinColumns = {#JoinColumn(name="roleId")}
)
private Collection<UserRole> roles;
}
and
public class UserRole {
#Id
#GeneratedValue
private long id;
#Column(unique = true)
private String roleName;
}
I am able to save a user at first. But when I try to save another user it rejects with an exception of duplicated entry on the junction table
One thing I noticed is when we have One-to-Many association the many side foreign key will be a primary key on the junction table. So, I need to make the relation Many-to-Many. So that the combination of both foreign keys will serve as a composite key.

OneToOne JPA issue

I have 2 class
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String age;
#OneToOne
#JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
}
and
public class Address {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String building;
private String country;
#OneToOne(mappedBy = "address")
private User user;
}
in my table address, I have a few rows.
When I insert table user with data
{
"id":null,
"name":"Foo",
"age":"18",
"address":{
"id":1,
"building":"Too",
"country":"ABS"
}
}
Table user have 1 row with address_id =1.
I insert same data as above
Table user have 2 row with address_id =1.
My answer is: why 2 table connected by one to one can happen the above case?
You can find your answer here
Why #OneToOne is allowing duplicate associations?
Basically, #JoinColumn(name = "address_id", referencedColumnName = "id") alone doesn't serve the semantics of one-to-one in the database, you need to add unique=true into the #JoinColumn, which makes it #JoinColumn(name = "address_id", referencedColumnName = "id", unique = true).
Side-note: I suggest you drop your tables and then re-creating them before trying this out. If you are using Hibernate, you can set hibernate.hbm2ddl.auto to create-drop

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

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).

Insert in child and parent JSON Spring Boot

I have 3 entities in my spring boot App data rest, Appusers, Teacher, and Student
Appusers
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name= "appuserId", updatable=false)
private Long appuserId;
#Column(name = "username")
private String username;
#Column(name = "fullname")
private String fullName;
#Column(name = "email")
private String email;
#Column(name = "password")
private String password;
#OneToOne(mappedBy = "appuser")
private Teacher teacher;
#OneToOne(mappedBy = "appuser")
private Student student;
Teacher
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "teacherId" , updatable = false)
private Long teacherId;
#Column(name= "firstname")
private String firstName;
#Column(name = "lastname")
private String lastName;
#Column(name="designation")
private String designation;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "appuserId", nullable = true)
private Appuser appuser;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval=true ,mappedBy="teacher")
#JsonIgnore
private List<Course> courses;
Student
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name= "studentId", updatable=false)
private Long studentId;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#Column(name = "enrolledSince")
private String enrolledSince;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "appuserId", nullable = false)
private Appuser appuser;
#OneToMany(cascade = CascadeType.ALL,orphanRemoval=true, mappedBy="student")
#JsonIgnore
private List<CourseStudent> courseStudents;
i can insert in appusers table using json format in postman and it goes well. but when i try to insert in teacher or student table the result in appusers is null. it shouldnt be null because teacher and student foreign key to appusers.
This should not happen. When you save a teacher or a student you should specify appuser which is already in the database. And use appuserId instead Appuser, which is quite enough to identify to which appuser it belongs.
You can get your appuser after you save a teacher or a student and do request with join to the database.
when you try to insert in teacher or student table, please make sure that you are setting the value to appuser while persisting.
Student std =new Student();
// create an object of appuser,set its vaue and assign it to student object
Appuser ap = new Appuser();
// assigning values to the appuser object as ap.setfullname="...";... so on
std.setAppuser=ap;
now persist this student object the entries will be reflected in the mapped table
or you can set the id of appuser in std object that is already persisted .

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;

Resources