JPA Hibernate bidirectional relation ends in null pointer - mysql

I have 2 entities: Version and Change log.
A Version has N change Logs and a Change Log has one Version.
My Version entity looks like:
#Table(name="change_log_version")
#Entity
public class ChangeLogVersionEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="version")
private String version;
#Column(name="date")
private LocalDateTime date;
#OneToMany(mappedBy = "version", cascade=CascadeType.ALL, fetch =
FetchType.EAGER)
public List<ChangeLogEntity> changeLogEntities;
public void addChangeLog(ChangeLogEntity changeLogEntity) {
this.changeLogEntities.add(changeLogEntity);
changeLogEntity.setVersion(this);
}
...
}
My Change Log entity looks like:
#Table(name="change_log")
#Entity
public class ChangeLogEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="description")
private String description;
#Column(name="added")
private boolean isAdded;
#Column(name="modified")
private boolean isModified;
#Column(name="deleted")
private boolean isDeleted;
#Column(name="public")
private boolean isPublic;
#Column(name="date")
private LocalDateTime date;
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "version_id")
private ChangeLogVersionEntity version;
...
}
I am kinda new to hibernate and i am stuck in a null pointer by adding a change log to the version. What do i have to change to archive the relation?
Many thanks in advance so far :)

That's because the changeLogEntites List is not initialized.
You should initialize it in the declaration
#OneToMany(mappedBy = "version", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
public List<ChangeLogEntity> changeLogEntities = new ArrayList<>();

Related

Hibernate & MySQL: When a record is created, a second record is added and is completely null

I'm fairly new to spring and hibernate and I'm creating a simple application that has two entity classes that are linked by #OneToMany and #ManyToOne relationships.
#Entity
public class ActiveIngredient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#Column(name="active")
private String active;
#Column(name="potency")
private double potency;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name="manufacturer")
private Manufacturer manufacturer;
and
#Entity
public class Manufacturer {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int manId;
#Column(name="name")
private String manName;
#Column(name="country")
private String country;
#Column(name="city")
private String city;
#Column(name="email")
private String email;
#Column(name="phone")
private String phone;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH}, mappedBy = "manufacturer")
private List<ActiveIngredient> activeIngredients;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH}, mappedBy = "manufacturer")
private List<ExcipientIngredient>excipientIngredients;
On the front end, a thymeleaf template is used where users can submit the data to create an ingredient and manufacturer. Below is the controller:
#Controller
#RequestMapping("/excipients")
public class ExcipientIngredientController {
#Autowired
private ExcipientIngredientService excipientIngredientService;
#Autowired
private ManufacturerService manufacturerService;
#GetMapping("/listExc")
public String listExcipients(Model model) {
List<ExcipientIngredient> theExcipient = excipientIngredientService.findAll();
//add list to the model.
model.addAttribute("excipient", theExcipient);
//return the thymeleaf page with this path.
return "list-excipients";
}
#GetMapping("/addExcipientForm")
public String addForm(Model model){
//to add a new excipient ingredient, need to create a new object
ExcipientIngredient excipientIngredient = new ExcipientIngredient();
Manufacturer manufacturer = new Manufacturer();
//add to the model
model.addAttribute("excipientIngredient", excipientIngredient);
model.addAttribute("manufacturer", manufacturer);
return "add-excipient-form";
}
#PostMapping("/saveExc")
public String saveExcipients(#ModelAttribute("excipientIngredient") ExcipientIngredient excipientIngredient, #ModelAttribute("manufacturer") Manufacturer manufacturer) {
// save the Ingredient
excipientIngredientService.saveOrUpdate(excipientIngredient);
manufacturerService.saveOrUpdate(manufacturer);
// use a redirect to prevent duplicate submissions
return "redirect:/excipients/listExc";
}
}
and this is the implementation for the save/update method.
#Override
public void saveOrUpdate(ExcipientIngredient excipientIngredient) {
Session currentSession = entityManager.unwrap(Session.class);
currentSession.saveOrUpdate(excipientIngredient);
}
Everything works fine when creating an ingredient and being updated into MySQL database, however, when the manufacturer is added to the database, an extra record is created that is completely null:
MySQL entry
I've been trying for a few hours to resolve this issue but have had no luck. Any suggestions or pointing me in the right direction would be appreciated.
In the Manufacturer class, you can try removing the cascade parameter from the #OneToMany annotation.
Here's a good article that you might find useful: https://www.baeldung.com/hibernate-one-to-many

Many to many relation ship gives null

using spring data and mysql as persistence layer getting some issues in Many to many mappings
#Getter
#Setter
public class BusinessUnitEntitiy extends AbstractTenantEntity implements Auditable{
private static final long serialVersionUID = -1123383144979037984L;
#Column(name = "NAME")
String name;
#Column(name = "DESCRIPTION")
String description;
#ManyToMany(fetch = FetchType.LAZY,mappedBy = "businessUnits" )
private Set<User> businessUsers;
public Set<User> fetchBusinessUsers() {
return businessUsers;
}
#Column(name = "DISPLAY_SEQUENCE_NUM")
protected Long displaySequenceNum;
#Column(name = "UNIQUE_SEQUENCE_ID",unique = true)
protected String uniqueSequenceId;
}
#Getter
#Setter
public class User extends AbstractTenantEntity {
private static final long serialVersionUID = 65981149772133526L;
#Column(name = "PROVIDER_USER_ID")
private String providerUserId;
private String email;
#Column(name = "enabled", columnDefinition = "BIT", length = 1)
private boolean enabled;
#Column(name = "DISPLAY_NAME")
private String displayName;
private String password;
private String provider;
#Column(name = "DISPLAY_SEQUENCE_NUM")
protected Long displaySequenceNum;
#Column(name = "UNIQUE_SEQUENCE_ID",unique = true)
protected String uniqueSequenceId;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "business_unit_user",
joinColumns={#JoinColumn(name ="user_id")},
inverseJoinColumns = { #JoinColumn(name="business_unit_id") }
)
Set<BusinessUnitJpaEntitiy> businessUnits;
}
fetching the user from businessunit works perfectly
but fetching businessunits from users gives null set even updating the same user is persisiting only the newly linked businessunit older values vanishes
If you persisted the user within the transaction without initializing the businessUnits fields, that's what you get. Either you also initialize the set correctly before persisting, or you detach the user after persisting, so that the user is reloaded from the database and the set is properly initialized.
you can try this
#ManyToMany
#JoinTable(
name = "business_unit_user",
joinColumns={#JoinColumn(name ="user_id",referencedColumnName = "id")},
inverseJoinColumns = { #JoinColumn(name="business_unit_id",referencedColumnName = "id") }
)
Set<BusinessUnitJpaEntitiy> businessUnits;
and you must have setter and getter for each property

Advise a dummy on how to structure my query and set up my mapping

Pardon me if this looks too basic, I am just a beginner in springboot. I am trying to create a simple project. I have entity class "company.java" and another "jobs.java"
I have already mapped them such that it will be saving both "companyID,jobID"
now my question is here :
1) how can I ensure that the record is captured into that new table when i feed data from thymeleaf fields. I have tried a dummy entry and its saving jobs into the jobs table but its not capturing anything to insert into the joined table
2) How do I query such that it saves and also when I search a job by its ID it shows me the company.
#Entity
#Table(name = "company_account")
#EntityListeners(AuditingEntityListener.class)
#SecondaryTable(name = "company_profile")
public class CompanyAccount {
private int id;
private String CompanyName;
private String PhoneNumber;
private String Number_of_employees;
private String CompanyURL;
private String OrganisationType;
private String PrimaryIndustry;
private byte[] Logo;
private String Location;
private String DateEstablished;
private String Headquarters;
private String SocialMedia;
public CompanyAccount() {
}
///////////////////////here i tried the mapping
#OneToMany(fetch = FetchType.LAZY, mappedBy = "company_account")
private List<NewJobs> jobs = new ArrayList<NewJobs>();
#Id
// #org.hibernate.annotations.ColumnDefault("001")
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
//////////////////////////////////
deleted the getters and setters
///////////////////////////////////////////////////////////////
#Entity
#Table(name = "newjobs")
#EntityListeners(AuditingEntityListener.class)
public class NewJobs {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#org.hibernate.annotations.ColumnDefault("001")
private int newjobid;
public NewJobs() {
}
tried my mapping here
#ManyToOne(fetch = FetchType.LAZY)
// #JoinColumn(name = "company_account")
#JoinTable(name = "company_jobs", joinColumns = { #JoinColumn(name = "id") }, inverseJoinColumns = {
#JoinColumn(name = "newjobid") })
private CompanyAccount CompanyAccount;
}
How can I query my insert and also my search by id ?

Nested one to many relationship jpa spring boot

I have three classes - Document, Page, Sentence. A Document will have multiple Pages & each Page will have multiple Sentences. I'm trying to map One to Many relationship using Spring Data JPA annotation. But it only works when there are only one layer like - Document>Page. Doesn't work while it's Document>Page>Sentence.
Can anyone please give me a solution for how to do it for nested one to many relationship ?
My classes are given below.
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "FILEID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long idFile;
#Lob
#Column(name = "CONTENT")
private byte[] content;
#Column(name = "NAME")
private String name;
#Column(name = "ID_MAIL_USER")
private String idMailUser;
#Column(name = "NUM_PAGES")
private int numPages;
#Column(name = "TO_ANALIZE")
private boolean toAnalize;
#Column(name = "HASH")
private String hash;
#Column(name = "EXTENSION")
private String extension;
#Column(name = "SIZE")
private double size;
#Column(name = "LINK_TO_DRIVE_FILE")
private String linkToDriveFile;
#Column(name="PATH")
private String path;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#Column(name = "PAGES")
private List<Page> pages = new ArrayList<>();
// Setter Getters
}
.
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
#Id
#Column(name = "PAGE_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long idPage;
#Column(name = "PAGE_NUMBER")
private int pageNum;
#Lob
#Column(name = "CONTENT")
private String content;
#ManyToMany(cascade = CascadeType.ALL)
#Column(name = "SENTENCES")
private List<Sentence> sentences = new ArrayList<>();
// Setter Getters
}
.
#Entity
#Table(name = "SENTENCE")
public class Sentence implements Serializable {
//private long idFile;
//private long idPage;
#Id
#Column(name = "SENTENCE_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "PAGE_NUMBER")
private int pageNumber;
#Column(name = "ORDER")
private int ord;
#Column(name = "CONTENT")
private String content;
#Column(name = "HASH")
private String hash;
// Setter Getters
}
Your OneToMany mappings are incorrect. Correct it as follows
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
......
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "DOCUMENT_ID") //Name the foreign key column in PAGE table to DOCUMENT_ID
private List<Page> pages = new ArrayList<>();
}
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
....
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "PAGE_ID") //Name the forein key column in PAGE table to PAGE_ID
private List<Sentence> sentences = new ArrayList<>();
}
Use #JoinColumn annotation instead of #Column to give the name of the foreign key that do the physical mapping between tables in your database.
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="document")
#Column(name = "PAGES")
private List<Page> pages = new ArrayList<>();
}
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
#ManyToOne
#JoinColumn(name="DOCUMENT_ID")
private Document document;
#ManyToMany(cascade = CascadeType.ALL, mappedBy="pages")
#Column(name = "SENTENCES")
private List<Sentence> sentences = new ArrayList<>();
}
#Entity
#Table(name = "SENTENCE")
public class Sentence implements Serializable {
#ManyToMany(mappedBy="sentences")
private List<Page> pages;
}
Here a Document One to Many relationship with Pages.
So.. we need define mappedBy in the entity we want to map another entity.. so in this case
#OneToMany(mappedBy="document",cascade = CascadeType.ALL, orphanRemoval = true)
and in referenced entity i.e. Pages we want foreign key DOCUMENT_ID, so we have to define it using
#ManyToOne
#JoinColumn(name="DOCUMENT_ID")

Foreign Keys in Spring Boot (MySQL, Hibernate, JPA)

I am trying to write a RESTful API using Spring Boot and I am not able to figure out a way to map my relations in the database. I have a User and a Reports table. Each User can have multiple Reports, and a single report consists of "FROM USER" and "TO USER" columns to indicate who sent the report and to whom. My User ID is the primary key and for the Report table, I am generating REPORT ID as the primary key using AUTO INCREMENT. Here is my User model class -
#Entity
#Table (name = "user")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL)
private List<Report> reportReceivedList;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Report> reportSentList;
/* Getters and setters ..... */
}
Here is my Report Model class -
#Entity
#Table (name = "report")
#EntityListeners(AuditingEntityListener.class)
public class Report {
#Id
#Column (name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne()
#JoinColumn(name = "from_user_id")
private Long fromUserId; //THIS SHOULD BE FROM "USER" TABLE
#ManyToOne()
#JoinColumn(referencedColumnName = "to_user_id")
private Long toUserId; //THIS SHOULD BE FROM "USER" TABLE
#Temporal(TemporalType.DATE)
#CreatedDate
private Date createdAt;
private String observation;
private String context;
//Other variables and getters and setters .....
}
Can someone please show me a way to correctly define this relationship. My current model doesn't work. Also, I want rows from REPORT class to be deleted as soon as a user is deleted. Thanks!
I finally fixed it by changing my User class as follows -
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "to_user_id")
private List<Report> reportReceivedList;
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "from_user_id")
private List<Report> reportSentList;
And by changing my Report class as -
#Column(name = "from_user_id")
private Long fromUserId;
#Column(name = "to_user_id")
private Long toUserId;