Hibernate makes insert twice and results in unique key constraint violation - java

hi I'm getting this "javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: ERROR: duplicate key value violates unique constraint"
I have two tables A and B,
A has id, b_id, B has id,A_id's.
A - oneToMany , B - manyToOne relationship.
on A,
#OneToOne(cascade = { CascadeType.ALL })
#JoinColumn(name = "LATEST_VERSION_ID") #Valid
#EntityProperty(type = "GuidKey", relation = "B.id")
on B,
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "A_ID") #Valid
#EntityProperty(type = "Key", relation = "A.id")
when I create Page A I was able to Do so, But when I try to update I get unique constraint violation on table 'B'.
It says the record already exist.

You have to make a bi-directional relation using the mappedBy property.
See:
one to one bidirectional hibernate mapping
http://www.mkyong.com/hibernate/hibernate-one-to-one-relationship-example-annotation
http://www.codereye.com/2009/04/hibernate-bi-directional-one-to-one.html
Also, #EntityProperty isn't required for this. The foreign key should be in one table in one-to-one.

Related

SpringBoot - Entity propagation for relationships

I have two entities: WorkoutTemplate and ConcreteExercise.
In WorkoutTemplate I have this relationship with ConcreteExercises
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
And in ConcreteExercise I have this relationship with WorkoutTemplate
#ManyToOne
private WorkoutTemplate belongingWorkout;
I would like to insert a WorkoutTemplate into my database...
I make the request to the controller sending a Json like this:
{ "workoutName" : "My Workout",
concreteExercises: [
{
"name" : "Squat"
}
]
}
The DAO insert into my DB the WorkoutTemplate
And insert also in the table of the ConcreteExercise the name.
But not the reference to the WorkoutTemplate...
Practically, the table ConcreteExercise is made of:
id, name, belongin_workout_id
With the request above, we populate the id (auto-increment) and the name, but not the foreign key to the WorkoutTemplate.
How can I solve this ?
I would like to automatically insert the foreign key without sending it in the request or doing it manually into the service
Hi there it's because you are not using #JoinColumn which marks a column for as a join column for an entity association or an element collection.
On your WorkoutTemplate entity - you can retain this:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
But on your ConcreteExercise entity - you need to have this change:
#ManyToOne
#JoinColumn(name = "workout_template_id", nullable = false)
private WorkoutTemplate belongingWorkout;
The above code will create a foreign key linking the ConcreteExercise entity with the primary key from the WorkoutTemplate entity. The name of the foreign key column in the ConcreteExercise entity is specified by name property which for this case is workout_template_id - feel free to change this.
If you have set this up but still not working, it will also be helpful to share a code snippet on how are you saving these.

Spring JPA OneToOne FK as PK Cascade.Remove

I've got two tables, b and a:
they have a one-to-one bidirectional relationship
a has a foreign key to b that defines this relationship
this foreign key is also considered as a primary key for a, and a JPA #ID
I want a cascade removal that deletes the related b when a is deleted
in MySQL, a's b_id is NOT NULL
The problem is that when I delete my A object with JPA repository, I get a ConstraintViolationException on its foreign key.
I would expect that both a and b rows are deleted (cleverly starting with a's one).
How could I work around this knowing that I want to keep:
my DB schema the same
the cascade removal from a to b
the b id being the JPA #Id for a
CREATE TABLE `b` (
`dbid` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`dbid`),
);
CREATE TABLE `a` (
`b_id` int(11) NOT NULL,
KEY `b_fk` (`b_id`),
CONSTRAINT `b_fk` FOREIGN KEY (`b_id`) REFERENCES `b` (`dbid`),
);
#Entity
#Table(name = "a")
public class A {
#Id
#Column(name = "b_id")
#GeneratedValue(generator = "gen")
#GenericGenerator(name = "gen", strategy = "foreign", parameters = #Parameter(name="property", value="b"))
private Integer bId;
#OneToOne(cascade = CascadeType.REMOVE)
#PrimaryKeyJoinColumn
private B b;
}
#Entity
#Table(name = "b")
public class B {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "dbid")
private Integer id;
#OneToOne(mappedBy = "b")
private A a;
}
[EDIT] After all discussions in answer comments and re-reading my question, the proposals with orphanRemoval indeed are in scope and work.
If you want to delete object of B, whenever the associated A is deleted (it's the fourt point of your wishlist:
I want a cascade removal that deletes the related b when a is deleted
then you need to change your mapping in A to:
#OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
#PrimaryKeyJoinColumn
private B b;
In terms of just the MySQL side of your implementation, the records in table B have no 'knowledge' of any record in table A. In the database the relationship is unidirectional
The native cascade functionality exists to prevent foreign key errors, by telling the DB what to do when deleting a record would leave a foreign key pointing nowhere. Deleting a table A record would not cause a foreign key error in any table B records, so any native cascade functionality would not be triggered
To reiterate; You cannot keep the schema the same, and the cascade removal from a to b, because you don't actually have the cascade removal from a to b
You also mentioned in the comments that some table B records can exist without a table A records which isn't in the original question
To obtain the automatic deletion of table B records you describe, you have a few options with regards to the DB:
Swap the relation over - Remove the current foreign key and add a nullable foreign key column in table B that references the primary key of table A. You can then put a cascade delete on this foreign key. Keep the new column null for the table B records that do not 'belong' to a table A record. You could also add a unique index to this column to secure a one to one relationship
Add a DB trigger - On deletion of a table A record, add a DB trigger that removes the referenced table B record
Add a DB procedure - Add a procedure that deletes a table A record and then the referenced table B record in turn, probably within a transaction. Going forwards, only delete table A records using the procedure
Don't solve the problem at the DB level - Basically the same as option 3, but move the procedure logic out of the DB layer into the application logic
There may be something in JPA that solves your dilemma out of the box, but under the hood it will be doing one of the above (not option 1 and probably option 4)
In order to achieve what you have asked, I have tweaked your tables as follows:
CREATE TABLE b (
dbid INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE a (
b_id int(11) NOT NULL PRIMARY KEY REFERENCES b(dbid) ON DELETE CASCADE
);
CASCADE DELETE wasn't added in your DDL.
This will enable cascade delete. To delete the b record on deletion of a I made following changes in class A:
#Entity
#Table(name = "a")
public class A {
#Id
#Column(name = "b_id")
#GeneratedValue(generator = "gen")
#GenericGenerator(name = "gen", strategy = "foreign", parameters = #Parameter(name="property", value="b"))
private Integer bId;
#OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
#PrimaryKeyJoinColumn
private B b;
}
Find link here to the working solution.
Can you try in class B to add the following
#OneToOne(mappedBy = "b", cascade = CascadeType.REMOVE)
private A a;
In addition, if in the database you have only a foreign key "a has a foreign key to b" can you also make a foreign key from b to a as well.
#OneToOne(mappedBy = "b",cascade = CascadeType.ALL,fetch = FetchType.LAZY,orphanRemoval=true )
private A a;

How to control hibernate reverse engineering #JoinTable and it's elements?

I am using hibernate Reverse Engineering tool to generate pojo's from my database. Say I have two table's A and B in my database and another table ABMap which has two columns A_id and B_id that are foreign keys to table A and B respectively, and the primary key of ABMap is the composite key of A_id and B_id.
Now, when I build my project and generate the pojos, instead of ABMap being generated as a separate entity by hibernate, it is added into the entity A as a Set. Below is the snippet of code generated in entity A,
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = “ABMap”, schema= “myDB”, joinColumns = {
#JoinColumn(name = “A_id”, nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = “B_id”, nullable = false, updatable = false) })
public Set getBs() {
return this.bs;
}
public void setBs(Set bs) {
this.bs = bs;
}
Now the issue here is, using hibernate or Jpa I can do a insert into the ABMap table without actually having an entity of ABMap but I cannot update the same record since the updatable element in #JoinColumn is set to false by hibernate reverse engineering tool. Below is the sql error that occurs when an attempt is made to update the value of B_id.
2014-12-17 13:26:50,639 ERROR (qtp850520326-20) [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] - The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_A_B". The conflict occurred in database "myDB", table "B", column 'B_id'.
How can I set the updatable element in #JoinColumn to true?

Hibernate multiple ManyToMany association leads to wrong number of columns error

I have a bit of a complex database structure which I want to explain at first. We have the following tables:
Mandant (Id, Name)
Leistungsbereich (Id, Label)
Benutzer (Id, LoginName)
Here we have two association tables:
Mandant_Leistungsbereich (Id, MandantId, LeistungsbereichId)
Benutzer_Mandant_Leistungsbereich (Id, BenutzerId, MandantLeistungsbereichId)
The primary keys are always the 'Id' columns and the foreign keys are obvious I think.
For the first three tables I have the corresponding Domain Java classes that are working with Hibernate.
To get all Leistungsbereich items for the Mandant I have the following code in my Mandant domain class:
#JoinTable(
name = "Mandant_Leistungsbereich",
joinColumns = {
#JoinColumn(
name = "MandantId"
)
},
inverseJoinColumns = {
#JoinColumn(
name = "LeistungsbereichId"
)
}
)
#ManyToMany( fetch = FetchType.LAZY )
private Set<Leistungsbereich> leistungsbereiche;
When I load a Mandant automatically all Leitungsbereich items are loaded in the Java Set. Hibernate knows that it has to search in the Mandant_Leistungsbereich association table, so this works fine so far.
Now comes the strange part. I also want to achieve the same thing for the Benutzer because the Benutzer should have a list (or better to say a set) of Mandant_Leistungsbereich items.
So I do the same in the Benutzer class:
#JoinTable(
name = "Benutzer_Mandant_Leistungsbereich",
joinColumns = {#JoinColumn( name = "BenutzerId" ) },
inverseJoinColumns = {#JoinColumn( name = "MandantLeistungsbereichId" ) }
)
#ManyToMany( fetch = FetchType.LAZY )
private Set<MandantLeistungsbereich> mandantLeistungsbereiche;
But then loading a user the following error gets printed out:
org.hibernate.MappingException: Foreign key (FK_71hckf2bhqrp78gom71l6eq38:Benutzer_Mandant_Leistungsbereich [MandantLeistungsbereichId])) must have same number of columns as the referenced primary key (Mandant_Leistungsbereich [MandantId,LeistungsbereichId])
I really dont know why hibernate thinks that the Benutzer_Mandant_Leistungsbereich table needs two foreign keys for the Mandant_Leistungsbereich table. Both in the database and in the hibernate mapping is ALWAYS ONLY an Id column as primary key so there is no composite key. Why does hibernate tells me that I need two columns for the foreign key?

Hibernate Exception: Missing Column (column exists)

Okay, so within the database we have a table called distributionCompanies, created like so:
CREATE TABLE `distributionCompanies` (
`distributionCompanyID` INT(11) NOT NULL,
`distributionCompanyName` VARCHAR(255) NOT NULL,
PRIMARY KEY (distributionCompanyID)
);
I'm trying to map this table to a class using Hibernate:
#Entity
#Table(name = "distributionCompanies")
public class DistributionCompany implements DatabaseObject {
#Id
#GeneratedValue
#Column(name = "distributionCompanyID", length = 11, unique = true, nullable = false)
private int distributionCompanyID;
....
However, when running, I hit this issue:
Initial SessionFactory creation failedorg.hibernate.HibernateException: Missing column: distributionCompanyID_distributionCompanyID in database2.distributionCompanies
This isn't the only table in the database, and I've managed to map other classes successfully using the same method, so I'm a little stumped as to why this is causing an issue.
Thank you for your time,
Samuel Smith
EDIT: In response to Xavi's comment, I temporarily removed another mapping for the column, and the error went away, so the bad-egg probably lays in the following code:
#ManyToOne(targetEntity = DistributionCompany.class)
#JoinTable(name = "distributionCompanies", joinColumns = { #JoinColumn(name = "distributionCompanyID", nullable = false) })
private int distributionCompanyID;
Hibernate is looking for a column named distributionCompanyID_distributionCompanyID in your distributionCompanies table.
This is probably due to a ToOne association mapping towards this table without #JoinColum.
From Hibernate Documentation:
The #JoinColumn attribute is optional, the default value(s) is like in one to one, the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side. In this example company_id because the property name is company and the column id of Company is id.
If you've got a #ManyToOne or #OneToOne association mapping in another entity, this would explain why Hibernate is looking for such a column.
EDIT Seeing the association mapping you posted, it looks like it should be:
#ManyToOne(targetEntity = DistributionCompany.class)
#JoinColumn(name = "distributionCompanyID")
private DistributionCompany distributionCompany;
The #JoinTable annotation is used to specify a join table (that means an intermediate table used to model many-to-many associations). And the point of mapping an association would be to dispose of the mapped object instance (in this case a DistributionCompany, not just a distributionCompanyId).

Categories

Resources