How to create table with unidirectional relation in Hibernate? - java

i have the empty database in mysql, and two java entites. One of those have unidirectional relation. When hibernate tryes to create tables, i got the error:
Error executing DDL "alter table entry add constraint FK6ov2k83sx3crs9v3q8nvjuf1j foreign key (category_name) references category (name)" via JDBC Statement
There are my entites:
#Entity
public class Entry {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
private int id;
#Column
private String myfio;
private String descr;
#OneToOne(cascade = CascadeType.ALL)
private Category category;
}
And the second:
#Entity
#Table(name="category")
public class Category {
#Id
#Column
private String name;
}
How to create tables without errors?

OneToOne relationship shares the same id. So it should be the same type, but the first one is int (actually it should be Integer to allow null value for the transient (not stored) entities) and the second one is String. It seems you simply missed a line. Also, it worths to mention Vlad Mihalchea’s article https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/

Related

Correct way to check for foreign key constraint violation in Hibernate?

I have two tables that already exist inside postgres, lets call the Table A and Table B. One column of Table B has a foreign key constraint in that it has to be the primary key of Table A. Thus there is a many-to-one relationship between B and A, where multiple records in Table B correspond to one record of Table A.
The Entity for both these tables are defined as follows.
public class TableA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
#Column(name = "phone_number")
private String phoneNumber;
}
TableB's entity is defined as follows:
public class Shots implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private Long itemId;
#Column(name = "user_id")
private Long userId;
}
Where userId is the foreign key mapping to the primary key user_id in Table A.
These constraints have already been defined in the underlying postgres database, so i didn't consider using the #ManyToOne annotation relationship (still trying to wrap my head around it).
The way i currently handle the case when a foreign key constraint violation occurs is by doing the following:
try {
tableBrepository.save(newShot);
} catch (ConstraintViolationException ex) {
logger.error("Violating foreign key constraint" + ex.getMessage());
}
My question is, is there a better way to check for this violation? Is there anything i can do to generally better structure the foreign key constraint in Spring Data JPA?
Thus there is a many-to-one relationship between B and A, where multiple records in Table B correspond to one record of Table A.
This kind of stuff in JPA entities is handled with #ManyToOne annotation. You usually do not refer to any id field directly but tell JPA what there should be. So in your class TableB (or should I call it... Shots?) should be something like:
#ManyToOne
private TableA tableA;
// and get rid of this
// #Column(name = "user_id")
// private Long userId;
And optionally - so not necessarily - you could have, in your TableA:
#OneToMany
private List<TableB> tableBsOrShouldICallYouShots;
I am not sure what is your actual problem but when setting and referring to id fields directly might cause your difficulties.
Now if you -for example- use repository to find some TableB you can then after that just do
tableB.getTableA()
And when saving you would before that do:
tableB.setTableA(somSortOftableA);
// so not tableB.setUserId(someLongIdFOrtableA);
Now the point is that there is no problem with referential integrity because you do not need to know any IDs and you cannot set any wrong ID. Unless you first need to fetch TableA by id before setting it to TableB but in that case you would still not set any IDs.

Hibernate-OneToMany mapping for existing DB Tables

I am trying to join to Hibernate Entities in a OneToOne Mapping. I am able to fetch the data for a given primary key from the Main Entity, the joining entity, however, returns null. I am new to hibernate and any help will be appreciated.
I have two Tables,
PT_CORE
Primary Key: ptId - Integer;
Foreign Key: stId(ST_AUX) - Integer;
Columns: ptId, ptName
ST_AUX
Primary Key: stId;
Columns: stId, stName
The two tables get populated by other applications and mine is a read-only operation.
Below is my first Entity class(PtCore.java)
#Entity
#Table(name="PT_CORE")
public class PtCore implements Serializable{
#Id
#Column(name="ptId", nullable = false)
private int id;
#Column(nullable=false)
private int stId; //The Foreign key column
#OneToOne
#JoinTable( name = "core_aux", joinColumns = {#JoinColumn(Name="ptId")},
inverseJoinColumns = {#JoinColumn(Name="stId")}
)
private StAux staux;
//Getters, setters and toString() for above
}
StAux is another Entity, defined as below,
#Entity
#Table(name="ST_AUX")
public class StAux implements Serializable {
#Id
#Column(nullable=false)
private Integer stId;
#OneToOne
private PtCore ptcore;
#Column
private String stName;
//Getters, Setters and toString follow.
}
I do below in the Service method:
PtCore obj = (PtCore) session.get(PtCore.class,1);
System.out.println(obj);
In the Results, I get the value of ptName, but the stAux class variables are null, Indicating that the join does not work as expected.
First of all you have the mapping information existing in your PT_CORE. And I assume it is something like FOREIGN KEY (stid) REFERENCES (stid). If you want to use existing schema and existing data I guess there is no mapping table core_aux really existing. At least you did not mention it. However it is visible as #JoinTable annotation but still there is this above mentioned foreign key which seems to be the real mapping (so again not the join table).
I suggest the following
remove this
#Column(nullable=false)
private int stId; //The Foreign key column
from your PtCore. I think it is not needed. Also in PtCore, remove the #JoinTable (because what I told above) and add mapping informaiion to #OneToOne annotation, like:
#OneToOne
#JoinColumn(name = "stid")
private StAux staux;
from your PT_CORE.
Then in StAux alter also a bit:
#Id
#Column(name = "stid") // this might not be needed but if there is like "st_id"...
private Integer stId; // so just for sure
#OneToOne(mappedBy = "staux")
private PtCore ptcore;
Because you have existing tables and constraints there might raise errors if hibernate tries to auto-generate those again by JPA instructions.
Check this for example for more information.
UPDATE: just realized also that in your title is #OneToMany but in your code is #OneToOne.
So you might want to elaborate your question and/or title a bit.
In your relation, the owning side is PtCore, the inverse side is StAux.
In bidirectional OneToOne relations, the inverse side has to have the mappedBy attribute. Actually, the mappedBy attribute contains the name of the association-field on the owning side.
So, you must change your inverse side code (StAux Entity). You have to add mappedBy attribute to #OneToOne in StAux class:
#OneToOne(mappedBy="staux")
private PtCore ptcore;

Switching foreign key in hibernate is not working

TLDR;
I'm using spring boot and jpa.
I want to switch the foreign key of an object, in this case just switching the category of a vehicle.
But when i try to do that hibernate interprets it as if i'm trying to change the primary key of the category object instead of just switching the foreign key and I get this error
org.hibernate.HibernateException:identifier of an instance of abc.package.mode.Category was altered from 1 to 2
I have an entity Category which i'm using only for categorizing vehicle entity object.
#Entity
public class Category {
#Id
private Long id;
private String name;
}
Here is the Vehicle class which needs to be categorized.
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator="dish_seq")
private Long id;
private String name;
private Integer price;
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.DETACH)
private Category category;
}
Lets say there's 3 categories,
'Sedan'
'Convertible'
'Hatchback'
If i have a car object,
Nissan-PT76, $30000, category: [id:1, name:Sedan]
When i try to change category manually to [id:2, name:Convertible] and persist it, i get
org.hibernate.HibernateException:identifier of an instance of abc.package.mode.Category was altered from 1 to 2
I cannot switch from one existing object to another. I have tried to look this up in the internet but i couldn't find the right keywords to search for this kind of relationship in hibernate, or does it not allow this kind of relationship at all?
Add column reference to your Category field in the Vehicle class
#JoinColumn(name = "category_id", nullable = false)

Hibernate one-to-one loads eagerly always

I have an Employee and Address with one-to-one bi-directional mapping:
#Entity
public class Employee {
#Id
#Column(name = "EMP_ID")
private long id;
private String firstName;
private String lastName;
private double salary;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ADDRESS_ID")
private Address address;
}
Below is my address entity:
#Entity
public class Address {
#Id
#Column(name = "ADDRESS_ID")
private long id;
private String street;
private String city;
private String province;
private String country;
private String pinCode;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "address")
private Employee owner;
}
In Address I have set Fetch type as Lazy. So if I get an address then I am expecting hibernate to run select query on address only, but I see in logs that it is trying to get Employee also.
Below is my HQL query:
List<Address> emps = session.createQuery("from Address where id=20").list();
These are the queries run by Hibernate:
Hibernate:
/*
from
Address
where
id=20 */ select
address0_.ADDRESS_ID as ADDRESS_1_0_,
address0_.city as city2_0_,
address0_.country as country3_0_
from
Address address0_
where
address0_.ADDRESS_ID=20
Hibernate:
/* load Employee */ select
employee0_.EMP_ID as EMP_ID1_1_0_,
employee0_.ADDRESS_ID as ADDRESS_5_1_0_,
employee0_.firstName as firstNam2_1_0_,
employee0_.lastName as lastName3_1_0_
from
Employee employee0_
where
employee0_.ADDRESS_ID=?
Why hibernate loads Employee eagerly even when I set its fetching strategy as LAZY.
This great article describes the problem and a possible solution:
https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
Possible solution: It must be a one-directional relationship from child to parent. The parent cannot have a #OneToOne field to access the child because:
"For every managed entity, the Persistence Context requires both the entity type and the identifier, so the child identifier must be known when loading the parent entity, and the only way to find the associated {child} primary key is to execute a secondary query."
Second solution: Use #OneToMany instead. Don't use #OneToOne because it has this complicated, subtle, quirky problem. You can alter the code to only allow one-to-one access and optionally add a unique key to enforce 1-1.
Lazy loading on one-to-one mapping is possible either by
Setting optional=false (If its not nullable) or
JoinColumn (not on PK and might require schema change)
You can refer to this link for moreinfo.
Explanation : You can refer to explanation link for detailed explanation about this.
Newer version of hibernate can't use trick such as optional=false.
Checkout the updated solution best way to map a onetoone relationship

ManyToMany reflexive relationship with an extra coloumn in JPA

First of all, sorry for my english. This is my first post.
Im developing an app for a friend that uses JPA (EclipseLink) and i can´t figure out how to make a ManyToMany reflexive relationship with an extra column describing the relationship.
I tried the solution of this post (without using a bidirectional relation):
#Entity
#Table(name="relationships")
public class Relationship implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne()
#JoinColumn(name = "associated_id_one")
private Person associatedPersonOne;
#ManyToOne()
#JoinColumn(name = "associated_id_two")
private Person associatedPersonTwo;
#Column(name="description")
private String description;
//Getters, Setters and constructor
...
}
But JPA gives me this error: The entity has no primary key attribute defined.
I know i have to specify a primary key but i don´t now how to make a composite primary key with those two foreign key.

Categories