Why is Hibernate not filling this foreign key column - java

This problem seems occur often, but I couldn't find a working solution for my case. Problem is that Hibernate leaves the foreign key of one foreign key column empty (null). Strange thing is that I use the same setup of these two affected tables in another program and it works fine. Only difference is that I now added 2 other tables.
I checked the MySQL output (via show_sql option) and Hibernate inserts the parent table before the child. So the key should be there.
Parent:
public class Page {
#Id
#GeneratedValue
#Column(name="page_id")
private Integer id;
//****** THIS IS THE CHILD
#OneToMany(orphanRemoval=true, mappedBy="pageId", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Tag> tag = new HashSet<Tag>();
//****** THESE ARE THE NEW TABLES
#OneToOne(cascade=CascadeType.ALL)
private Video video;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name="creator_id")
private Creator creator;
Child:
public class Tag {
#Id
#GeneratedValue
private Integer id;
#Index(name = "tag")
#Column
private String tag;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name="page_id")
private Page pageId;
How it is saved (saveOrUpdate is necessary):
public static void save(SessionFactory sessionFactory, Page page) {
Session session = sessionFactory.openSession();
session.beginTransaction();
session.saveOrUpdate(page);
session.getTransaction().commit();
session.close();
}
Every suggestion are highly appreciated!

Are you managing both sides of the association? Meaning that you add the Tag to the Set<Tag> in Page and set the Page field on the Tag?
Before passing Page to the save method you should be managing the relationship as follows:
Page page = new Page();
Tag tag = new Tag();
//managing the both sides of the relationship
tag.setPage(page); //Setting Tags page field
page.getTags().add(tag); //Adding tag to the `Set<Tag>`

Related

JPA Spring saving entity to a different name in MariaDB

The first code block is the entity and the second code saves the tag to the database.
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String tagName;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "tags")
private Set<Board> boards = new HashSet<>();
}
public void getTagList(String tagName) {
Tag tag = new Tag();
tag.setTagName(tagName);
tagRepository.save(tag);
}
Whenever I send over the tagName, I get an error saying
*java.sql.SQLSyntaxErrorException: (conn=564) Unknown column 'tag_name' in 'field list'
I don't have 'tag_name' anywhere in my code and I'm not sure why JPA is trying to save it with a slightly different name.
I added a column called tag_name in the database and it seems to work and save the data into tag_name.
You can match the DB column name with your field like this.
#Column(name="tag_name")
private String tagName;

Hibernate duplicates record on INSERT with children

The database table structure is the following:
id INT
extId VARCHAR
name VARCHAR
parent INT (references self.id)
Here is the entity
#Entity
#Table(name = "categories")
public class Category
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne
#JoinColumn(name = "parent", referencedColumnName = "id")
private Category parent;
#org.hibernate.annotations.OrderBy(clause = "name ASC")
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", cascade = CascadeType.ALL)
private Set<Category> children = new HashSet<>();
#Column(name = "extId")
private String extId;
#Column(name = "name")
private String name;
public void addChild(Category child)
{
child.setParent(this);
this.children.add(child);
}
//getters and setters ...
}
In the very beginning there is only one entity:
{
id: 0
extId: ''
name: 'Category'
parent: null
children: Set<Category>{/*empty*/}
}
This entity is fetched in the beginning of program and assigned to another class
Later this class performs addition of new Category as a child to existing root (the one that was fetched in the beginning) property
Addition of child is done like this:
Session session = HibernateSessionFactory.getInstance().getFactory().openSession();
//gather data
Category child = new Category();
//set name
//set extId
this.rootCategory.addChild(child);
Transaction tx = session.beginTransaction();
session.save(this.rootCategory);
tx.commit();
session.close();
After this instead of expected result in database:
Root(id(0), extId(''), parent(null), name('root'))
\-— Child(id(10), extId('art-0'), parent(0), name('child'))
I get the following result
Root(id(0), extId(''), parent(null), name('root'));
Root(id(10), extId(''), parent(null), name('root'))
\-— Child(id(11), extId('art-0'), parent(10), name('child'))
Notes:
New Session is created for each action and this session is acquired via Singleton SessionFactory
If I refresh() root entity before adding a child -- everything is OK, no duplicates
If I perform child addition immediately after fetching root entity -- no duplicates
What could be the reason of this behavior (assuming two different sessions)? And how can it be fixed?
After fetching from db at the beginning of your program, rootCategory becomes a detached object.
Later when you want to use it in another session, you need to reattach it to this session. Instead of session.save(), you can use session.update()
this.rootCategory.addChild(child);
Transaction tx = session.beginTransaction();
session.update(this.rootCategory);
tx.commit();
session.close();
If you are closing the session after fetching rootCategory then the rootCategory object becomes a detached object according to the hibernate lifecycle and using Session.save() on it will create a new row for it. Therefore you either need to fetch, add the child and save the rootCategory object in the same session or use refresh to tell hibernate that it is not a new object but one that is already saved.

How to correctly add records to jpa onetomany join table

I'm developing a filing system where I have 3 tables. PROJECTS table consists of projectid, project name and other details (see below). This is an existing class and populated schema and I do not want to modify this part of the application if possible.
Folders table (Called ProjectClassification) consists of folderid and foldername and is the owning side of a unidirectional onetomany relationship.
Project_Folders is a join table. I'm using JPA 2.0 (EclipseLink) and JSF 2.0 as my web framework.
My basic problem is I can't add duplicate records to the join table using a merge operation. MERGE is good for adding records until the owning key already exists, after which point it will only update the join table. I know this is the way it's supposed to work but I need to add new records even if there's a duplicate of the owning key. This will allow me to store different projects in the same folder.
I've looked through some other questions here such as:
onetomany unidirectional with jointable setup using jpa
This says what is needed to add one entity to the other in a join table but i need to know more about how to correctly persist or merge the added entity to the database.
The folder entity class:
#Entity
#Table(name = "PROJECTCLASSIFICATIONS")
public class ProjectClassifications implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int proclassid;
private int projectid;
private String classification;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "PROJECT_CLASSF_JOIN",
joinColumns = #JoinColumn(name = "proclassid", referencedColumnName = "proclassid"),
inverseJoinColumns = #JoinColumn(name = "projectid", referencedColumnName = "projectid", unique = true))
private Collection<Projects> projects;
public ProjectClassifications() {
}
public ProjectClassifications(String classification) {
this.classification = classification;
}
public ProjectClassifications(int proclassid, int projectid) {
this.proclassid = proclassid;
projects = new ArrayList<Projects>();
}
public ProjectClassifications(Projects newProject) {
projects = new ArrayList<Projects>();
}
public void addProject(Projects newProject) {
if(!getProjects().contains(newProject))
getProjects().add(newProject);
}
....
....
The Project entity class is a pre existing code and I do not want to modify at all if possible:
#Entity
#Table(name = "PROJECTS")
public class Projects {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int projectid;
private String projectName;
private String projectDescription;
#Temporal(javax.persistence.TemporalType.DATE)
private Date startDate;
#Temporal(javax.persistence.TemporalType.DATE)
private Date endDate;
private String commnts;
// foreign keys to parent tables
private int fk_countryid;
private int fk_companyid;
private int fk_employeeid;
#ManyToOne(optional = true)
#JoinColumn(name = "countryid")
private Country country;
....
....
I then use two html select lists to select values for projectid and proclassid which call the following methoid using a JSF managed bean:
public String makeProClassRecord() {
newProClass = new ProjectClassifications(proclassid, projectid);
newProject = proServ.findByProjectId(projectid);
newProClass.addProject(newProject);
facade.update(newProClass);
//facade.save(newProClass);
return showProclass();
}
My questions are:
1) Is MERGE the correct operation used to add records into a join table?
2) Is there a way to add records that contain duplicate keys (foreign keys represented as new records in the join table) using MERGE?
3) Should PERSIST be used to achieve question 2?
4) Would it be better to create an entity for the join table itself and simply use a PERSIST method to insert the records?
Many thanks
So I solved this myself a couple of weeks ago and thought of sharing the answer. Instead of doing merge or persist operations on any of the target entities, I created a Join table and unidirectional OneToMany relationship from the Project entity to the below ProjectFileSystem join table entity and simply did the persist operation using that entity. I need to add duplicate folders for different projects (or store many projects under a single folder item) so it seems more efficient to do the CRUD operations in the actual join table entity rather than from the target entity. Hope this helps:
#Entity
#Table(name = "PROFOLDERSYS_JOIN")
public class ProjectFileSystem implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int foldersysid;
private int proclassid;
private int projectid;
private String projectName;
private String folderName;
public ProjectFileSystem() {
}
public ProjectFileSystem(int proclassid, int projectid,
String projectName, String folderName) {
this.proclassid = proclassid;
this.projectid = projectid;
this.projectName = projectName;
this.folderName = folderName;
}
// getters and setters
}
The method in the bean would be:
public String makeProSys() {
newProSys = new ProjectFileSystem(proclassid, projectid, classification, projectName);
newProject = proServ.findByProjectId(projectid);
projectName = newProject.getProjectName();
newProSys.setProjectName(projectName);
newProClass = facade.findByContactId(proclassid);
classification = newProClass.getClassification();
newProSys.setFolderName(classification);
profilFacade.save(newProSys);
return showProSys();
}

org.hibernate.TransientObjectException if any many-to-one fields are null (or not selected in form)

I am using jpa with play framework.I have an entity JobseekerDetails
#Entity
public class JobseekerDetails {
#Id
#Column(name = "jobseekerDetails_id", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long jobseekerDetails_id;
#ManyToOne
#JoinColumn(name = "basicEducation_id")
private JobseekerFormBasicEducation basicEducation;
#ManyToOne
#JoinColumn(name = "masterEducation_id")
private JobseekerFormMasterEducation masterEducation;
#ManyToOne
#JoinColumn(name = "doctrateEducation_id")
private JobseekerFormDoctrateEducation doctrateEducation;
#ElementCollection
private List<String> certificateName =new ArrayList<String>();
#Column(length = 3000)
private String resume;
private Long experience;
private String skills;
private String resumePath;
#ManyToOne
#JoinColumn(name = "industry_id")
private JobseekerFormIndustry industry;
#ManyToOne
#JoinColumn(name = "functionalArea_id")
private JobseekerFormFunctionalArea functionalArea;
}
which have a many-to-one relation with other entities like JobseekerFormFunctionalArea , JobseekerFormIndustry etc.These entities have fixed value which is already saved in the database.
When JobseekerDetails is saved all its details should be saved with corresponding manytoone relation ids but donot save to Entity JobseekerFormFunctionalArea and JobseekerFormIndustry as they are predefined
My problem is that when i save(through my form) all the manytoone relation fields ids in JobseekerDetails it is saved properly but when submit my form without selecting any value in any manytoone relation fields For ex if i dont select anything in my fuctionalArea_id field it gives the below exception
org.hibernate.TransientObjectException:object references an unsaved transient instance - save the transient instance before flushing
If I select all the fields the details are saved but if I dint select anyone field which is mapped manytoone in my model then it gives above exception
But jpa automatically set nullable=true then why this happend
I have searched a bit and found that this problem can be solved by adding cascade .I added cacade type Merge but getting the above same exception.
I also tryed setting nullable=true but getting same error
On setting cascade = CascadeType.ALL and cascade = CascadeType.PERSIST I am getting below exception
PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist:
Any help would be appreciated
When you don't select any 'basicEducation' in the form and you are saving form then there is new object there with empty id. So you have:
jobseekerDetails.basicEducation = new JobseekerFormBasicEducation ()
This causes TransientObjectException
To avoid it you have to set
jobseekerDetails.basicEducation=null
before saving.

JPA fetching too many entity class objects even when it is not rqquired

I have been working on a Enquiry ( ticket raising ) application.
But the application performance is low.
Here i had two requirements (overview page,detail page) in my application.
Whenever we open the application it has to show open tickets(enquireis) currently assigned to that user in a overview page.
The overview page has pagination for 10 records.
Here each record has a link to look into its details ( detail page).
We are using JPA and wicket technologies.
Here whenever we open the application,
the overview page has to show few fields data that have been mapped to few fields in five entity classes.
Here the main entity class is Enquiry which has other entities mapped to it like User,
Company,
Dealer,
Issue,
EnquiryText.
So when i open my application it needs few fields data in all these entities.
But JPA is loading all fields in all entities that are mapped to Enquiry class.
The worst part is other entities in Enquiry entity also mapped to some other entities.
Select enquiry.,enquiry.user.*,enquiry.company.*,
enquiry.issue.*,enquiry.dealer.*,enquiry.enquiryText.*
from
Enquiry enquiry
where ( enquiry.* = input value, enquiry.issue.* = input value )
I also had other requirement that when i click on each record in overview page it should fetch all the details of that record.
So it has to fetch all the entities in this case.
Select enquiry
from Enquiry enquiry
where ( enquiry.* = input value, enquiry.issue.* = input value )
Is it possible for us to make our application to stop fetching all entities
but fetch few entity fields in one requirement(while showing overview page)
and to make it fetch all entity fields in other requirement(when we click on each record i.e ticket) ?
#Entity
#Table(name = "ENQRY")
public class Enquiry extends AbstractEntity {
#Id
#Column(name = "CONT")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "EMAILADRADD")
private String emailAddresses = "";
#Column(name = "CONFIHME")
private String Comments = "";
#Column(name = "STCONT")
private DateTime lastStatusChangeOn = new DateTime();
#Column(name = "CLANGOPTR")
private String operatorLanguage;
#Column(name = "CLANGISS")
private String issuerLanguage;
#ManyToOne
#JoinColumn(name = "USERLUPD")
private User lastUpdateByUser;
#ManyToOne
#JoinColumn(name = "USERCREAT")
private User createdByUser;
#ManyToOne
#JoinColumn(name = "USERISS")
private User issuer;
#ManyToOne
#JoinColumn(name = "CMPY")
private Company company;
#OneToMany(cascade = { CascadeType.ALL }, mappedBy = "enquiry", fetch = FetchType.EAGER)
#Where(clause = "CTEXT = 'DIAGN'")
private List<EnquiryText> diagnosis;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "enquiry")
private List<EnquiryText> texts;
There is not a single solution to this problem.. two that I have in mind are:
Create a db view and then map a custom entity to this view (remember that views are readonly)
Create a DTO class and then make a select new dtopackege.myDto(e.field, e.field1, e.field2, e.field3.subfield) from MyEntity e
Your DTO must have a constructor capable of initialize all parameters passed by the query.
Then based on your requirement you can chose to invoke your custom methods or simply the hibernate plain ones that will fetch all properties.
Hope this helps!

Categories