JPA - One to many - duplicate key value error - java

I need implement join table for users which will have 2 columns USER_CONTACTS(user_id, contact_id)
I need store for contacts lists for users. I need to make possible that user with id 1 can have in contacts user with id 5, but user 5 can also have in contacts user with id 1 (but he musn't).
I need to be possible do following inserts:
INSERT INTO USERS_CONTACTS(user_id, contact_id) VALUES(1, 5);
INSERT INTO USERS_CONTACTS(user_id, contact_id) VALUES(5, 1);
Now I got this error:
Unsuccessful: INSERT INTO USERS_CONTACTS(user_id, contact_id)
VALUES(2, 3) 2017-12-05 15:00:15.146 ERROR 14268 --- [ main]
org.hibernate.tool.hbm2ddl.SchemaExport : ERROR: duplicate key value
violates unique constraint "uk_j9ggomsdbjte1eqfo5e61vh8a"
Here is my relationship implementation of User in JPA:
#Entity
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", columnDefinition = "serial")
private long id;
#OneToMany
#JoinTable(name = "USERS_CONTACTS",
joinColumns = #JoinColumn(name = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "CONTACT_ID"))
private List<User> contacts;
// ...
}
Can you tell me how to fix it? I am using Postgre 9.5. Thanks.
EDIT
For hibernate I have these properties:
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = create-drop
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Try to solve this task yourself.
Enable Hibernate SQL log
Create a database schema with Hibernate on a test database (a tablespace) or using H2 database. You can download this project and play with mappings using unit tests: https://github.com/v-ladynev/hibernate-experimental.
Check generated SQL.
Perhaps, you will notice that contact_id is made unique by uk_j9ggomsdbjte1eqfo5e61vh8a constraint. The reason is that for #OneToMany relation child must has only one parent.
I think you need #ManyToMany relation.

I assume that uk_j9ggomsdbjte1eqfo5e61vh8a is a leftover from previous version.
If it were so then just drop the constraint:
alter table USERS_CONTACTS drop constraint uk_j9ggomsdbjte1eqfo5e61vh8a;

There must not be an unique index on that table! Remove that index and it should work.

Related

Why hibernate generates insert and update for OneToMany mapping

I am trying to understand the one-to-many mapping in Hibernate with a small example. I have a Product with a set of Part's. Here are my entity classes:
Part.java
#Entity
public class Part {
#Id
#GeneratedValue
int id;
String partName;
//Setters & Getters
}
Product.java
#Entity
public class Product {
private String serialNumber;
private Set<Part> parts = new HashSet<Part>();
#Id
public String getSerialNumber() {
return serialNumber;
}
#OneToMany
#JoinColumn(name = "PRODUCT_ID")
public Set<Part> getParts() {
return parts;
}
// Setter methods
}
Then I tried to save some parts and products in my database and observed below queries generated by hibernate:
Hibernate: insert into Product (serialNumber) values (?)
Hibernate: insert into Part (partName, id) values (?, ?)
Hibernate: update Part set PRODUCT_ID=? where id=?
Here to add a record in Part table, hibernate generates 2 DML operations - insert and update. If a single insert command is sufficient to add a record in table then why hibernate uses both insert and update in this case? Please explain.
I know this is crazy old but I had the same problem and Google brought me here, so after fixing it I figured I should post an answer.
Hibernate will switch the insert/update approach to straight inserts if you make the join column not nullable and not updatable, which I assume in your case it is neither anyways:
#JoinColumn(name = "PRODUCT_ID", nullable = false, updatable = false)
If Part as composite element list then only two query will come. Please check and revert.
If its not a composite element , hibernate try to insert individual as a separate query and it will try to create relationship between them.
In earlier case hibernate will insert with relationship key.
**Hibernate: insert into Product (serialNumber) values (?)
Hibernate: insert into Part (partName, id) values (?, ?)**
In these two queries hibernate is simply inserting a record into the database.
At that stage hibernate is not creating any relationship between the two entities.
Hibernate: update Part set PRODUCT_ID=? where id=?
Now after making entity tables,hibernate is going to make a relationship between the two
by using the above third query...
The association is uni-directional, so Product is the owning side (because it's the only side).
Make the association bidirectional and make Part the association owner. That way you will avoid redundant updates because the foreign key values will be specified as part of insert statements for Part.

How to cascade delete entities with unidirectional 'ManyToOne' relationship with JPA

I have two entity classes 'User' and 'Department' with unidirectional 'ManyToOne' relationship as below.
public class User{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "DEPARTMENT_ID", nullable = true)
private Department department;
}
public class Department{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
If I want to delete some users and cascade remove the referenced departments if not any user references the department, is there any features of JPA to use please?
You can use CascadeType.DELETE, however this annotation only applies to the objects in the EntityManager, not the database. You want to be sure that ON DELETE CASCADE is added to the database constraint. To verify, you can configure JPA to generate a ddl file. Take a look at the ddl file, you'll notice that ON DELETE CASCADE is not part of the constraint. Add ON DELETE CASCADE to actual SQL in the ddl file, then update your database schema from the ddl. This will fix your problem .
This link shows how to use ON DELETE CASCADE on for CONSTRAINT in MySQL. You do this on the constraint. You can also do it in a CREATE TABLE or ALTER TABLE statement. It's likely that JPA creates the constraint in an ALTER TABLE statement. Simply add ON DELETE CASCADE to that statement.
Note that some JPA implementors do provide a means for this functionality.
Hibernate does supply this functionality using the #OnDelete annotation.
You can tell hibernate to delete 'orphan' entries with;
#Cascade({ org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })

Hibernate OneToMany with JoinTable ID generation

Could somebody help me in setting appropriate annotation in hibernate for following case:
I have three tables:
Account, Card and AccountCard.
AccountCard is joining table for OneToMany relationship between Card and Account (account has many cards, card is attached to only one account).
I need to add to Account a List cards property and to Card model Account account property. This is the easy thing.
The problem is that I get "Cannot insert null value to AccountCard.id" while persisting Account with Cards.
Also I need to use sequence to generate IDs for joining table but don't know how.
Any help would be very appreciated.
Here is the code in Card:
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "account_card", joinColumns = #JoinColumn(name = "crd_id"), inverseJoinColumns = #JoinColumn(name = "acc_id"))
private Account account;
I don't want to have a mapping in Account class so List cards is not added.
In your #JoinTable annotation, I see reference to an account_name table and not AccountCard. Is there actually an AccountCard table somewhere?
A join table usually doesn't need an id key of its own, and if you have hibernate autogenerate your table DDL it won't include one.
If you do indeed need an id on the join table, I don't think there's a way or a need to make hibernate aware of it, but you should make the column NOT NULL AUTO_INCREMENT in your SQL DDL.
Did you try to generate the tables first in the database (in my case mysql) and then create the entity with an ide like nebans? An auto increment id column in mysql then ends with:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
Check out http://netbeans.org/kb/docs/javaee/ecommerce/entity-session.html for a sample how to use netbeans to create entities from database

Hibernate Annotation - 1 to 1 with no auto_increment

How do I proceed in terms of Hibernate Annotation (JPA) when a table's primary key column is a foreign key to another table with no auto_increment (MySQL).
Thanks.
Something like the following should work (not tested for exact syntax, but should be close):
#Id
private int id;
#OneToOne
#JoinColumn(name = "id", updatable = false, insertable = false)
private RelationEntity other;
You would need to manually set your 'id' field before you persist it, and can't remember if you can set the 'other' entity before the initial save, hibernate may complain if you do that. However, if your 'id' column is set then when you load the entity back out you should have the relationship loaded as well.

How to do bulk delete in JPA when using Element Collections?

I am having trouble working out how to do a bulk delete of a Person object using JPA, when the Person objects contain data stored using an #ElementCollection. Any ideas on how to do this would be much appreciated.
#Entity
#Table(name="at_person")
public class Person implements Comparable<Person> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private long id = 0;
#Column(name="name", nullable = true, length = 128)
private String name = "";
#ElementCollection
#Column(name = "email")
#CollectionTable(name = "person_email", joinColumns = #JoinColumn(name = "person_id"))
private Set<String> email = new HashSet<String>();
}
What I am doing at the moment is this, and it fails with a foreign key constraint error:
Query query=em.createQuery("DELETE FROM Person");
Caused by: java.sql.SQLException: integrity constraint violation:
foreign key no action; FKCEC6E942485388AB table: PERSON_EMAIL
If it can be a pure JPA annotation rather than a Hibernate annotation that would be a bonus!
I'll let you interpret the part of the JPA 2.0 specification that mentions that a bulk delete operation is not cascaded:
4.10 Bulk Update and Delete Operations
...
A delete operation only applies to
entities of the specified class and
its subclasses. It does not cascade to
related entities.
And the fact is that Hibernate won't cascade a delete to a collection table either. This has been reported in HHH-5529 and the suggested approaches are:
You could also (a) clean up the collection table yourself or (b) use cascading foreign keys in the schema.
In other words, (a) use native SQL or (b) use a cascade delete constraint at the database level - and you'll have to add it manually, I don't think you can use #OnDelete with the #ElementCollection annotation (same story as HHH-4301 IMO).

Categories

Resources