How to fix my bidirectional one to many relationship in Spring Boot - java

I am trying to make an one to many relationship between two tables, one of the two is the Aggregate root table (Vessel). But when I am creating a new entity the many to one table doesnt map its foreign key to the one to many table's primary key. What I am doing wrong?
Vessel Table (One to many)
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "vessel_info")
#SequenceGenerator(name = "vessel_id_seq",sequenceName = "vessel_id_seq", initialValue = 1,allocationSize = 1)
public class Vessel {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator ="vessel_id_seq" )
#Column(name = "vessel_code")
private Long vesselCode;
private String name;
private Long companyId;
private Long imo;
private String type;
#Column(name = "fleet_id")
private Long fleetId;
private String yard;
private Integer hn;
private Date delivered;
private Double age;
#OneToMany(mappedBy = "vessel",cascade = CascadeType.ALL,
targetEntity = BoilerInfo.class, orphanRemoval = true,fetch = FetchType.EAGER)
private List<BoilerInfo> boilerInfo ;
BoilerInfo Table (Many to one)
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "boiler_info")
#SequenceGenerator(name = "boiler_id_seq",sequenceName = "boiler_id_seq", initialValue = 1,allocationSize = 1)
public class BoilerInfo {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator ="boiler_id_seq" )
private Long id;
private String maker;
private String type;
private String tubeType;
private String exhGasByPass;
#ManyToOne(fetch = FetchType.EAGER, optional = false,targetEntity = Vessel.class)
#JoinColumn(name="vessel_code",nullable = false)
private Vessel vessel;
}
CREATE VESSEL
private final VesselRepository vesselRepository ;
#Override
public Vessel create(Vessel entity) {
log.info("Creating {}.", entity);
Vessel vessel = vesselRepository.save(entity);
return vessel;
}
JSON POST
{
"fleetId":"1",
"name":"BOILERTEST",
"type":"temp",
"companyId":"1",
"boilerInfo":[{
"maker": "temp",
"type":"temp"
}]
}
The logs from the SQL query

Set both side of relation:
vesel.getBoilerinfos().add(boilerInfo);
boilerInfo.setVesel(vesel);

Related

Springboot JPA query builder Cannot join to attribute of basic type

I am trying to get the latest Flight on a specific airport of an Aircraft with Spring JPA query builder. The Aircraft data is managed in a different application.
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(schema = "schema1")
public class Flight implements Serializable {
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "flight_sequence"
)
#SequenceGenerator(
name = "flight_sequence",
allocationSize = 1
)
#Column(nullable = false, updatable = false)
private Long id;
private Aircraft aircraft;
private Date date;
private String origin;
private String destination;
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(schema = "schema2")
public class Aircraft implements Serializable {
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "aircraft_sequence"
)
#SequenceGenerator(
name = "aircraft_sequence",
allocationSize = 1
)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="operator_id", nullable=false)
private Operator operator;
private String registration;
private String acType;
private Date createdAt;
private Date updatedAt;
}
public interface FlightRepository extends JpaRepository<Flight, Long> {
Flight findFirstByDestinationAndAircraftRegistrationOrderByDateDesc(String destination, String registration);
}
But it throws this exception:
org.hibernate.query.criteria.internal.BasicPathUsageException: Cannot join to attribute of basic type
You need to define association mapping for the Aircraft attribute of the Flight entity. Probably ManyToOne as you did for the Operator attribute of the Aircraft entity.

How should I map my one table to another using JPARepositoy?

I have a table schema like this. Notification can be of 2 types : twitter or fb
For each notification, there will be 1 row in notification table and 1 in either twitter_post/ fb_post.
Each table have auto sequence id generator.
FBPost and TwitterPost is mapped via notification_id (PK)
I have created entity class like this:
#Entity
#Table(name = "notification")
public class Notification {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "not_id_sequence")
private String id;
#Column(name = "description")
private String description;
#OneToOne(mappedBy = "notification")
private FBPost fbPost;
#OneToOne(mappedBy = "notification")
private TwitterPost twitterPost;
}
#Entity
#Table(name = "fb_post")
public class FBPost {
#Id
#Column(name = "post_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_fid_sequence")
private Long postId;
#Column(name = "post_content")
private String postContent;
#OneToOne
private Notification notification;
}
#Entity
#Table(name = "twitter_post")
public class TwitterPost {
#Id
#Column(name = "post_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_tid_sequence")
private Long postId;
#Column(name = "post_content")
private String postContent;
#OneToOne
private Notification notification;
}
I am using JpaRepository to save into the database. But not getting any row into fb_post or twitter_post table.
Am i doing it wrong in entity class.
Try adding #JoinColumn(name = "notification_id", referencedColumnName = "notification_id") below #oneToOne in TwitterPost and FBPost class for the notification variable
Looks like you are missing cascade. Try to add cascade = CascadeType.ALL into #OneToOne of the parent entity - Notification:
#Entity
#Table(name = "notification")
public class Notification {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "not_id_sequence")
private String id;
#Column(name = "description")
private String description;
#OneToOne(mappedBy = "notification", cascade = CascadeType.ALL)
private FBPost fbPost;
#OneToOne(mappedBy = "notification", cascade = CascadeType.ALL)
private TwitterPost twitterPost;
}
For more information I would recommend to read hot to map one-to-one relationship.

Hibernate joining tables with multiple primary keys

I have figured out how to join 2 tables with single primary key. But now I need to join 4 tables and some of these table with composite primary keys.
Here is my table picture
And I want to join them, so I generate classes for them:
// Record
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "record")
public class Record implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "student_id")
private Integer studentId;
#Id
#Column(name = "exam_id")
private Integer examId;
#Column(name = "total_score")
private Integer totalScore;
#Column(name = "student_score")
private Integer studentScore;
#Column(name = "submission_id")
private Integer submissionId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "student_id")
private Student student;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "submission_id")
private Submission submission;
}
// Submission
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "submission")
public class Submission implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "submission_id")
private Integer submissionId;
#Id
#Column(name = "question_id")
private Integer questionId;
#Column(name = "stu_answer")
private String stuAnswer;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "submission")
private Record record;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "submission")
private Set<Question> question;
}
// Question
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "question")
public class Question implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "question_id")
private Integer questionId;
#Column(name = "content")
private String content;
#Column(name = "score")
private Integer score;
#Column(name = "is_delete")
private Integer isDelete;
#Column(name = "option_id")
private Integer optionId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "submission_id")
private Submission submission;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "optional_id")
private Optional optional;
}
// Optional
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "optional")
public class Optional implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "option_id")
private Integer optionId;
#Column(name = "content")
private String content;
#Column(name = "is_delete")
private Integer isDelete;
#Column(name = "answer")
private String answer;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "optional")
private Question question;
}
// Final class to store information
public class RcdSubQuesOpt {
private Integer studentId;
private Integer examId;
private Integer questionId;
private String stuAnswer;
private String qContent;
private String oContent;
private String answer;
}
And this is code for JPA
#Override
public List<RcdSubQuesOpt> getRcdSubQuesOpt(int studentID, int examId) {
Session session = this.getSession();
List<RcdSubQuesOpt> results;
Transaction transaction = null;
try {
transaction = session.beginTransaction();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<RcdSubQuesOpt> criteriaQuery = criteriaBuilder.createQuery(RcdSubQuesOpt.class);
// Try to join tables
Root<Record> pRoot = criteriaQuery.from(Record.class);
pRoot.join("submission", JoinType.INNER);
pRoot.join("question", JoinType.INNER);
pRoot.join("optional", JoinType.INNER);
criteriaQuery.multiselect(
pRoot.get(columns in RcdSubQuesOpt Class......));
// Try to add constraints
Predicate predicate = pRoot.get("examId").in(Arrays.asList(1));
criteriaQuery.where(predicate);
// try to do queries
results = session.createQuery(criteriaQuery).getResultList();
transaction.commit();
} catch (Exception e) {
results = null;
if (transaction != null) {
transaction.rollback();
}
} finally {
session.close();
}
return results;
}
But hibernate throw error as following:
Enitial SessionFactory creation failedA Foreign key refering com.domain.Submission from com.domain.Record has the wrong number of column. should be 2
Exception in thread "main" java.lang.ExceptionInInitializerError
I think it's the composite primary keys problem. But solution I searched is not suitable to solve it. Anyone give me some advice? Thanks!
To reference a composite primary key, you need to use #JoinColumns (plural).
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "sub_submission_id", referencedColumnName = "submission_id"),
#JoinColumn(name = "sub_question_id", referencedColumnName = "question_id")
})
private Submission submission;
However, I must admit, I don't understand your model - especially why Submission has a composite PK with question_id. It looks that one Submission has many Questions, why to include question_id as part of Submission PK?
Perhaps, I'm missing something, because the diagram is not fully visible.

Mapping a database view entity with a simple entity and pass to DTO using Spring Data

I'm just learning Spring Data. I want to map a database view Entity with a simple Entity and pass to DTO which will contain columns both entities. I understand that I can use a special database view but I need to map precisely entities of Spring Data.
I have a database view Entity "MentorStudents":
#Entity
#Table(name = "mentor_students")
#Immutable
public class MentorStudents implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "mentor_id", updatable = false, nullable = false)
private Long mentorId;
//This entity I need to map
private Mentor mentor;
#Column(name = "active_students")
private Integer activeStudents;
public MentorStudents() {
}
//getters, setters, equals, hashCode
}
A database view sql of an above entity is:
SELECT id AS mentor_id, active_students
FROM mentor
LEFT JOIN ( SELECT mentor_id, count(mentor_id) AS active_students
FROM contract
WHERE close_type IS NULL
GROUP BY mentor_id) active ON mentor.id = active.mentor_id
ORDER BY mentor.id;
And I have a simple Entity "Mentor":
#Entity
#Table(name = "mentor")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Mentor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#NotNull
#Column(name = "last_name", nullable = false)
private String lastName;
#Column(name = "patronymic")
private String patronymic;
#Column(name = "phone")
private String phone;
#NotNull
#Column(name = "email", nullable = false)
private String email;
#Column(name = "skype")
private String skype;
#Column(name = "country")
private String country;
#Column(name = "city")
private String city;
#Column(name = "max_students")
private Long maxStudents;
//getters, setters, equals, hashCode
I have to get a DTO which contains all Mentor fields and an "activeStudents" MentorStudents field without a "mentorId" field. How do it?
Use spring data projection:
public interface YourDto {
// all Mentor get fields
String getFirstName();
...
// activeStudents get field
Integer getActiveStudents();
}
public interface YourRepository extends JpaRepository<YourEntity, Integer> {
#Query(value = "select ...(all fields match YourDto) from Mentor m, MentorStudents s where m.id = s.mentorId and m.id = ?1")
Optional<YourDto> findMyDto(Integer mentorId);
}

JPA hibernate one to many relationship creates extra column name

I have two entities. I want to cascade the insertion of the child entity when the owner entity is persisted and set the SSO_ID of the child entity to the one that was generated for the owner by the generator.
#Entity(name = "USERS")
#Table(name = "USERS")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID_GENERATOR")
#SequenceGenerator(name = "ID_GENERATOR", sequenceName = "ID_SEQUENCE")
#Column(name = "SSO_ID")
private Long ssoId;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserEmail> userEmails = new ArrayList<>();
// getters, setters etc.
}
#Entity(name = "USER_EMAILS")
#Table(name = "USER_EMAILS")
#IdClass(UserEmailId.class)
public class UserEmail {
#Id
#Column(name = "SSO_ID")
private Long ssoId;
#Id
#Column(name = "USER_MAIL")
private String userMail;
#Id
#Column(name = "START_DATE")
private Date startDate;
#Id
#Column(name = "EMAIL_TYPE")
private String emailType;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
// getters, setters etc.
}
The UserEmail ID class is:
public class UserEmailId implements Serializable {
private Long ssoId;
private String userMail;
private Date startDate;
private String emailType;
// getters, setters etc.
}
Instead, I get an error:
insert into hub_users_emails (user_sso_id, email_type, sso_id, start_date, user_mail) values (?, ?, ?, ?, ?)
(etc.)
binding parameter [1] as [BIGINT] - [1234837655] => this is user_sso_id
(etc.)
binding parameter [3] as [BIGINT] - [null] => this is the original sso_id
SQL Error: 904, SQLState: 42000
o.h.engine.jdbc.spi.SqlExceptionHelper : ORA-00904: "USER_SSO_ID": invalid identifier
I've tried some other setups of one to many (bidirectional, unidirectional, etc.) but It seems that this problem persists between all implementations.
Help is appreciated.
As you use #ManyToOne and #OneToMany, hibernate will create user_sso_id on your USER_EMAILS table. I am not sure why do you want another ssoId on USER_EMAILS.
I have removed sso_id from USER_EMAILS and now it's working fine. I know this is not the exact answer of your question. Following code may help you.
#Entity(name = "USERS")
#Table(name = "USERS")
#Setter
#Getter
public class User {
#Id
#SequenceGenerator(name = "ID_GENERATOR", sequenceName = "ID_SEQUENCE")
#GeneratedValue(generator = "ID_GENERATOR" )
#Column(name = "SSO_ID")
private Long ssoId;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "user")
private List<UserEmail> userEmails = new ArrayList<>();
}
#Setter
#Getter
#Entity(name = "USER_EMAILS")
#Table(name = "USER_EMAILS")
#IdClass(UserEmailId.class)
public class UserEmail {
#Id
#Column(name = "USER_MAIL")
private String userMail;
#Id
#Column(name = "START_DATE")
private Date startDate;
#Id
#Column(name = "EMAIL_TYPE")
private String emailType;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
}
#Setter
#Getter
public class UserEmailId implements Serializable {
private String userMail;
private Date startDate;
private String emailType;
}
public class SomeClass{
public User saveUser(){
User user = new User();
UserEmail userEmail = new UserEmail();
userEmail.setUser(user);
userEmail.setEmailType("type");
userEmail.setStartDate(new Date());
userEmail.setUserMail("someEmail#gmail.com");
user.setUserEmails(Arrays.asList(userEmail));
userRepo.save(user);
}
}

Categories

Resources