#ManyToOne Referencing a composite key in JPA - spring-boot

I have following entities
#Entity
#IdClass(SubjectId.class)
class Subject {
#Id
String name;
#Id
String volume;
........
}
class SubjectId {
String name;
String volume;
//constructor, getters and setters
..........
}
#Entity
class Student {
#Id
String studentId;
String subject;
String subjectVolume;
}
I want to map the fields subject and subjectVolume of class Student to composite primary key of class Subject as a #ManyToOne relationship. But I don't know what should I pass inside #ManyToOne(?).
Any help would be appreciated, thanks.
Edit:
I want to use the columns subjectName and subjectVolume as entity fields in class Student as well. I don't want to do student.getSubject().getSubjectName() instead I want student.getSubjectName().

You could just declare the relation this way (instead of declaring the fk fields):
class Student{
#Id
String studentId;
#ManyToOne // this is sufficient create foreign-key columns in the Student-table
Subject subject;
}
The generated columns of the Student table will have these names by default:
In case you need different column names you should look for the #JoinColumn annotation.
edit: to be able to directly call student.getSubjectName() you could still decide to include single parts of the composite foreign key additionally as entity properties, in this case you need to make sure to declare the second (duplicate) column mapping with insertable=false and updatable=false, since its value is already managed by the #ManyToOne fk:
#Entity
static class Student {
#Id
String studentId;
#ManyToOne
Subject subject;
#Column(name = "subject_name", insertable = false, updatable = false)
String subjectName;
}
However, I'd probably prefer simply declaring a custom getSubjectName() getter which just returns subject.getName().

Related

map a #OneToMany relationship using a string column as a key

Good Morning!
I would like to know if there is a way to map a #OneToMany relationship using a string column as a key. The database in SQL Server already has all the tables, but their relationship was not made by the id, but by values ​​of type string. Can anyone help me?
Example:
student table with the fields name, phone, emailthe key of this table is the column "name"
class tablewith the fields name, date, discipline the key of this table is the column "name" that refers to "student name".
as I tried to do:
#Entity
public class Student {
...
#OneToMany(mappedBy = "student")
private List<Class> class = new ArrayList<>();
}
#Entity
public class Class{
...
#ManyToOne
#JoinColumn(name = "student_id")
private Student student;
}
When I run it through H2, it creates a column with the students' id, but I would like it to map the data that is already in the database, because I can't change the data structure. Can someone help me?
String is a valid type for an identifier. If you don't want an additional id column, you can map name as an identifier:
#Entity
public class Student {
#Id
#Column(name = "`name`")
String name;
#OneToMany(mappedBy = "student")
private List<Class> class = new ArrayList<>();
...
}
For some databases name is a special keyword, if you use #Column(name = "`name`"), Hibernate will handle it correctly.

Two tables connected via Primary Key

I have read about the use of #MapsId and #PrimaryKeyJoinColumn annotations, which sounds like a great options. I have two tables (UserList and UserInformation) which have a child, parent relationship, respectively; both classes below are abbreviated to just include the relevant columns. UserInformation's primary key value is always null and does not take the value of its parent column.
User Class
#Entity
#Data
#Table(name = "user_list")
public class UserList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// List of foreign keys connecting different entities
#OneToOne(mappedBy = "user")
#MapsId("id")
private UserInformation userInfo;
}
UserInformation Class
#Entity
#Data
#Table(name = "user_information")
public class UserInformation implements Serializable {
#Id
private Integer userId;
#OneToOne
private UserList user;
}
I would prefer to not use an intermediary class if possible. I'm not tied to MapsId or even this implementation if there is a better solution.
Thanks!
The question is not very clear to me, but I think you could improve the following in the modeling of the entity:
The #column annotation can only be omitted when the class parameter is called exactly the same as the database column, taking into account the table name nomenclature, could it be that the column is user_id ?, if so the id parameter should be :
#Id
#column(name="USER_ID")
private Integer userId;
In the user entity being id, it will match the DB ID field so the #column annotation is not necessary

OneToMany relationship using non-primary composite key

I have a table structure like this
Good Assignments Entity
#Embeddable
public class GoodAssignmentId {
String clientId,
String assignmentNumber;
LocalDate effectiveDate;
// Getters and setters
}
#Entity
#IdClass(GoodAssignmentId.class)
class GoodAssignment {
#id
String clientId;
#Id
String assignmentNumber;
#Id
LocalDate effectiveDate;
#OneToMany(mappedBy = "parentKey")
Set<GoodTasks> children;
String description;
// getters and setters goes below
}
Bad Assignments Entity
#Entity
#IdClass(BadAssignmentId.class)
class BadAssignment {
#id
String clientId;
#Id
String assignementNumber;
#Id
LocalDate effectiveDate;
String description;
// Getters and setters goes below
}
Child entities
#Entity
#IdClass(ParentTasksId.class)
#DiscriminatorColumn(name = "fieldD", discriminatorType = DiscriminatorType.STRING)
class ParentTasks {
#Id
String clientId;
#Id
String assignmentNumber;
#Id
String taskNumber;
}
#Entity
#DiscriminatorValue("G")
class GoodTasks extends ParentTasks {
#ManyToOne
#JoinColumns({
#JoinColumn(name = "clientId", referencedColumName = "clientId"),
#JoinColumn(name = "assignmentNumber", referencedColumName = "assignmentNumber")
})
GoodAssignments parentKey;
other fields....
}
This shows the error referencedColumnNames(fieldA, fieldB) of .... not mapped to a single property.
Unfortunately I cannot change the table structure. TableA has 3 columns as primary key, but only two of them forms the primary key in table B along with another field (fieldD via #DiscriminatorValue used by multiple classes). How can I map this relationship to get list of TableB items in TableA?
Example Class Diagram:
Effective date in the assignments tables is not part of tasks. So this is not a perfect relationship in JPA terms. It's a legacy design which cannot be changed for some reasons.

Usual field as foreign key

I have two tables. I want to make between them relationship, but the thing is that the child table connects to an attribute in a parent node, which is not a PK. How can I assign a non-PK field as a FK for a table?
Here are the tables. User Information:
#Entity
#Table(name="user")
public class userinformation implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="USER_ID")
private int uID;
#Column(name="LIB_ID")
private String libID;
//Other attributes
}
Lib Information
#Entity
#Table(name="libinfo")
public class Auth {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="AUTH_ID")
private int authID;
#Column(name="LIB_ID")
private String lib_ID;
//Other attributes
}
They both should be linked through libID (surely unique). Any idea how to implement it correctly?
Given:
class User {
#Column(name = "lib_id")
private String libID;
}
you must map the Auth entity as:
class Auth {
#JoinColumn(name = "lib_id", referencedColumnName = "lib_id")
#ManyToOne
private User user;
}
Basically, referencedColumnName is used to inform the JPA provider that it should use a column other than the primary key column of the referenced entity (which is used by default with #ManyToOne mappings).

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