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

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.

Related

#ManyToOne Referencing a composite key in JPA

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

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

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

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

Can Spring Data JPA Enforce ManyToOne Relationship via #JoinColumn

I have a Spring Boot application using Spring Data REST and Spring Data JPA. I have two domain entities: Student and Classroom, where many students can belong to the same classroom.
Student:
#Data
#Entity
#Table(name = "STUDENT")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "STUDENT_ID")
private Integer studentId; // This Id has been setup as auto generated in DB
#Column(name = "ROOM_ID")
private Integer roomId;
#ManyToOne
#JoinColumn(name = "ROOM_ID", nullable = false, updatable = false, insertable = false)
private Classroom classroom;
}
Classroom:
#Data
#Entity
#Table(name = "CLASSROOM")
public class Classroom {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ROOM_ID")
private Integer roomId; // This Id has been setup as auto generated in DB
#OneToMany(mappedBy = "classroom")
private List<Student> studentList;
.....// other fields related to a classroom
}
And the Student repository:
public interface StudentRepository extends CrudRepository<Student , Integer>{
List<Student> findByClassroom(#Param("room") Classroom room);
}
And the Classroom repository:
public interface ClassroomRepository extends CrudRepository<Classroom , Integer>{
}
And I have a SpringApplication main file, but no controller.
There is already one classroom with room id=1 in the CLASSROOM table. When I gave the following request to POST to http://localhost:8080/students, a new student record was created in the Student table, which I expected it to fail because there isn't a classroom with id=100 exists in the CLASSROOM.
So my question is that: can Spring Data JPA enforce a manyToOne relationship or this foreign key enforcement has to be done on the database side (the not-null ROOM_ID column in the Student table is NOT defined as foreign key by our DBA for a legitimate consideration). If it has to be done on the database side, what is the point to define the manyToOne relationship in entity files?
Also, I know that I have redundant classroom fields in the Student entity, I just don't know which one to keep in the Student entity (the roomId or the "classroom" field), because when I create a student, I want to give only the roomId of a classroom in the request. Thanks!
{
"roomId": 100 // I expect this request to fail because no roomId=100 in the CLASSROOM table.
}
what is the point to define the manyToOne relationship in entity files
Because is an Object Relational Mapping tool that allows you define entity graphs.
You are currently passing roomId which in your Entity is just another field so you needs to remove that.
#Entity
#Table(name = "STUDENT")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "STUDENT_ID")
private Integer studentId; // This Id has been setup as auto generated in DB
#ManyToOne
#JoinColumn(name = "ROOM_ID", nullable = false)
private Classroom classroom;
}
In Spring Data Rest you then defined an association by passing the self link of the referenced entity.
Your request then needs to look like the below:
{
"classroom" : "http://localhost:8080/classrooms/1"
}
Also removing the ID as you are POSTing a new record and, as you note, the ID is auto-generated in the database.
See also:
https://www.baeldung.com/spring-data-rest-relationships

Zero to One (Optional One to One) Entity Relationship Issue (attempted to assign null one-to-one property)

Unable to update entity for optional one to one relationship using spring data jpa(2.1.2.RELEASE) and spring boot(2.1.2.RELEASE)
Getting the error attempted to assign null one-to-one property
#Entity
#Table(name = "table_a")
public class EntityA {
#Id
String id;
String aa;
int bbb;
#Nullable
#OneToOne(mappedBy = "inv", optional = true,cascade = CascadeType.ALL)
EntityB bEntity;
}
#Entity
public class EntityB{
#Id
String id;
String aaa;
String nnnn;
#OneToOne
#MapsId
#JoinColumn(name = "id")
EntityA aEntity;
}
DAO Code as below
Optional eA = entARepo.findById("1234");
EntityA entA= null;
if (eA.isPresent()) {
entA= eA.get();
}
EntityB eB = entA.getBEntity();
if (Objects.isNull(eB)) {
eB= new EntityB();
eB.setAAA("12121");
eB.setAEntity(entA);
entA.setBEntity(entB);
}
repository.save(entA);
}``
I resolved this by using a join table instead of a shared primary key approach. would still to know how to make the shared primary key approach work for optional one to one relationship

Resources