I am using JPA2 (EclipseLink) and my entity objects look like this:
#Entity
public class Dashboard
{
#Id
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy="dashboard", orphanRemoval = true)
private List<LogResult> logResults;
//getter-setters
}
#Entity
public class LogResult
{
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
private Dashboard dashboard;
//getter-setters
}
and I have code like this to delete a dashboard:
EntityManager em = PersistenceInitializer.newEntityManager();
em.getTransaction().begin();
em.remove(dashboard);
em.getTransaction().commit();
However when I execute the code above, I get an error msg regarding a foreign key constraint on the logresult table.
Why is this so? Doesn't using CascadeType.ALL mean all that should be handled for me?
Update here is the actual error output on the console
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`crs`.`logresult`, CONSTRAINT `FK_LOGRESULT_DASHBOARD_NAME` FOREIGN KEY (`DASHBOARD_NAME`) REFERENCES `dashboard` (`NAME`))
Error Code: 1451
For EclipseLink implementation, see the EclispeLink JPA Extensions guide: http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/toc.htm
Explicitly add cascade = CascadeType.DELETE_ORPHAN as well. When parent entity is deleted first, all the child objects become orphan. CascadeType.DELETE_ORPHAN cascade type makes JPA to remove those orphan objects.
e.g. below:
#OneToMany(cascade={CascadeType.ALL, CascadeType.DELETE_ORPHAN})
Related
I am simply trying to perform an update of an entity. However hibernate attempts 2 SQL statements, one to perform the correct update and an unwanted second to update the ID alone to null, which causes my application to fail.
I am using Spring Data alongside Hibernate and when performing an update of an Entity, I see the expected update SQL is performed, however when running the application with SQL Server, a subsequent update is attempted which does the following:
update my_table set id=null where id=?
This fails obviously.
Cannot update identity column 'ID'.
Running the same code with H2 I do not see this second update triggered.
Any idea what might be the cause of this behaviour?
I am extending JpaRepository and using the default save().
Here is a snippet of my entity:
#Table(name = "MY_TABLE")
#Entity
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String anotherValue;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name="id")
private List<ChildEntity> children = new ArrayList<>();
// getters, builder, private default constructor ...
Snippet building my entity:
MyEntity.newBuilder()
.withId(id)
.withAnotherValue(valueUpdate)
.build();
Repository:
public interface MyRepository extends JpaRepository<MyEntity, Long>
Saving:
myRepository.save(myUpdatedEntity);
As i think of probable cause for this is if you associate two entities with their IDs as foreign keys then hibernate may try to update ID of parent as foreign key of other entity. Its not correct way to associate.
In a one-to-many relation add a foreign key in the many side entity, that have to reference the primary key of the one side entity class.
#Entity
public class MyEntity {
..
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name="id", referencedColumnName = "MYENTITY_ID")
private List<ChildEntity> children = new ArrayList<>();
}
I have parent Filter entity and one direction List<Ad>ads as children with #OneToManyrelation. I try delete ads where is more then one week old using Hibernate hql query but get:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (gecars.filters_ads, CONSTRAINT FK_gcri6h0918u8o2ybd6yfquk79 FOREIGN KEY (ads_id) REFERENCES ads (id))
User:
#Entity
#Table(name="users")
public class User {
...
#OneToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="user")
#OrderBy("id")
private Set<Filter> filters = new HashSet<Filter>(0);
...
Filter:
#Entity
#Table(name="filters")
public class Filter {
...
// I also tried use #OneToMany(CascadeType.REMOVE, orphanremoval=true)
// or #OneToMany(cascade=CascadeType.ALL,orphanRemoval=true)
#OneToMany
private Set<Ad> ads;
...
Ad:
#Entity
#Table(name="ads")
public class Ad {
...
private Date insertTime = new Date();
...
DAO delete method:
public void deleteOldAds(Date date){
String hql = "delete from Ad where insertTime < :date";
session.createQuery(hql).setParameter("date", date).executeUpdate();
}
The cause of the issue is that a join table is used if #OneToMany association side is the owner of the relationship.
To specify that a join column (foreign key) in the ads table is used for the association instead of a separate join table, simply map the association with a #JoinColumn:
#OneToMany
#JoinColumn
private Set<Ad> ads;
It is occurs because Hibernate will not delete entity Ads from relationship.
So you need to remove yourself Ad from relationship before delete.
Or you can use #OneToMany through mappedBy it will not create additional table filters_ads for relationship therefor will not create constrain.
This has already been asked a number of times, but I don't find any good answers so I'll ask it again.
I have parent-children unidirectional relation as follows:
#Entity
#Table(name = "PARENT")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long parentId;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinTable(name = "CHILD", joinColumns = #JoinColumn(name = "parent_id"), inverseJoinColumns = #JoinColumn(name = "ID"))
private List<Child> children;
}
#Entity
#Table(name = "CHILD")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "PARENT_ID")
private Long parentId;
//some other field
}
I create an instance of the parent, assign a list of children to it and try to persist it:
Parent p = new Parent();
List<Child> children = new ArrayList<Child>();
Child c = new Child();
children.add(c);
p.addChildren(children);
em.merge(p);
When the code runs, I get the following exception:
MySQLIntegrityConstraintViolationException: Cannot add or update a
child row: a foreign key constraint fails
(testdb.child, CONSTRAINT parent_fk
FOREIGN KEY (parent_id) REFERENCES parent (id) ON
DELETE NO ACTION ON UPDATE NO ACTION)
I'm assuming this is due to the fact that the parent is not fully inserted when the child is being attempted to be inserted.
If I don't add the children to the parent, the parent gets inserted just fine.
I tried switching the GeneratedValue generation strategy but that did not help.
Any ideas how to insert the parent & the children at the same time?
Edit: Even if I persist the parent first, I'm still getting the same error. I determined it's because the parent_id is not set in the child; the child is default constructed and thus the parent_id is set to 0 which does not exist thus the foreign key constraint validation.
Is there a way to get jpa to automatically set the parent_id of the children that are assigned to the parent instance?
Your relationship does not have to be bi-directional. There is some mis-information in the comments here.
You also said that you had added the field "parentId" into the Child entity because you assumed that JPA needs to "know" about the parent field so that it can set the value. The problem is not that JPA does not know about the field, based on the annotations that you have provided. The problem is that you have provided "too much" information about the field, but that information is not internally consistent.
Change your field and annotation in Parent to:
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinColumn(name = "parent_id")
private List<Child> children;
Then remove the "parentId" from the Child entity entirely.
You had previously specified a JoinTable annotation. However, what you want is not a JoinTable. A JoinTable would create an additional third table in order to relate the two entities to each other. What you want is only a JoinColumn. Once you add the JoinColumn annotation onto a field that is also annotated with OneToMany, your JPA implementation will know that you are adding a FK into the CHILD table. The problem is that JPA has a CHILD table already defined with a column parent_id.
Think of it that you are giving it two conflicting definitions of both the function of the CHILD table and the parent_id column. In one case, you have told you JPA that it is an entity and the parent_id is simply a value in that entity. In the other, you have told JPA that your CHILD table is not an entity, but is used to create a foreign key relationship between your CHILD and PARENT table. The problem is that your CHILD table already exists. Then when you are persisting your entity, you have told it that the parent_id is explicitly null (not set) but then you have also told it that your parent_id should be updated to set a foreign key reference to the parent table.
I modified your code with the changes I described above, and I also called "persist" instead of "merge".
This resulted in 3 SQL queries
insert into PARENT (ID) values (default)
insert into CHILD (ID) values (default)
update CHILD set parent_id=? where ID=?
This reflects what you want perfectly. The PARENT entry is created. The CHILD entry is created, and then the CHILD record is updated to correctly set the foreign key.
If you instead add the annotation
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinColumn(name = "parent_id", nullable = false)
private List<Child> children;
Then it will run the following query when it inserts the child
insert into CHILD (ID, parent_id) values (default, ?)
thus setting your FK propertly from the very beginning.
Adding updatable=false to the parent entity solved the problem with both an insert and an updated being executed on the child table. However, I have no clue why that's the case and in fact, I don't think what I am doing is correct because it means I cannot update the child table later on if I have to.
I know persisting a new parent with children works for me using em.persists(...).
Using em.merge(...), really I don't know, but it sounds like it should work, but obviously you are running into troubles as your JPA implementation is trying to persists children before parent.
Maybe check if this works for you : https://vnageswararao.wordpress.com/2011/10/06/persist-entities-with-parent-child-relationship-using-jpa/
I don't know if this plays a role in your problem, but keep in mind that em.merge(p); will return a managed entity... and p will remain un-managed, and your children are linked to p.
A) try em.persists(...) rather than em.merge(...)
if you can't
B) you are merging parent... and you cascade is set to CascadeType.PERSIST. Try changing it to
cascade=CascadeType.ALL
or
cascade={CascadeType.PERSIST, CascadeType.MERGE}
I know merge will persists newly created entities and should behave as persists, but these are my best hints.
What you wantto achieve you can achieve with this code.
#Entity
#Table(name = "PARENT")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long parentId;
#OneToMany(cascade = CascadeType.PERSIST)
private List<Child> children;
}
#Entity
#Table(name = "CHILD")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#ManyToOne
Parent parent;
}
Please consider the following mySQL tables:
CREATE TABLE storeman.user (
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(50) NOT NULL,
display_name VARCHAR(50),
password CHAR(32),
...
PRIMARY KEY (id),
UNIQUE INDEX (email)
);
CREATE TABLE storeman.user_preferences (
id INT NOT NULL AUTO_INCREMENT,
notify_login BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (id),
CONSTRAINT fk_user_preferences FOREIGN KEY (id) REFERENCES user (id) ON DELETE CASCADE
);
As you may see there's a one-to-one relationship between the two tables.
When running the Hibernate Code Generator, I get the follwoing (relevant part only)
#Entity
#Table(name = "user", catalog = "storeman", uniqueConstraints = #UniqueConstraint(columnNames = "email"))
public class User implements java.io.Serializable {
...
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
public UserPreferences getUserPreferences(){
return this.userPreferences;
};
...
}
The issue with that is when I save the User entity, the linked UserPreferences is not saved automartically. Solving this is easy:
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
#Cascade({CascadeType.ALL})
public UserPreferences getUserPreferences(){
return this.userPreferences;
};
However, If for any reason I will have to re run the Hibernate Code Generator again, the #Cascade({CascadeType.ALL}) will be gone, and this is dangerous as I'm relying on the fact that linkled tables are automatically saved in my code.
So the question is: is there a way to modify mySQL script on top so that, while running hibernate reverse engineering code generation, the #Cascade annotation is automatically added?
mySql physical table doesn't say anything about cascading other then the foreign key. Only it can add ON DELETE CASCADE ON DELETE UPDATE.
Then you run Hibernate Code Generator, CascadeType and #Cascade definitions are not translated into DDL
Try to use jpa annotations much as possible.
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user",cascade= CascadeType.ALL)
public UserPreferences getUserPreferences(){
return this.userPreferences;
};
I have following unidirectional ManyToOne relation:
#Entity
#Table(name = "Child")
public class Child {
#Id
private Integer id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Parent parent;
}
#Entity
#Table(name = "parent")
public class Parent{
#Id
private Integer id;
}
When I am trying to delete parent Entity from database I have constraint violation.
ORA-02292: integrity constraint violated - child record found
What I need is that parent Entity is deleted even if it has children, but children Entity should stay.
How do I change this relation?
You can't with JPA if using a relationship. Making it a ManyToOne indicates that a value in the foreign key field will exist in the Parent table. JPA will not be able to distinguish between a null fk value and there being a fk value that just doesn't have an associated row in the Parent table.
If it really must be done (and it shouldn't IMO), you would need to map the Integer foreign key value in Child with a basic mapping instead of the ManyToOne. This allows it to be set independently of there being an existing Parent entity - null means null, a value means a value. You can then query for the associated Parent entity if the entity itself is needed.
Maybe an optional=true parameter on the ManyToOne would help?
#Entity
#Table(name = "Child")
public class Child {
#Id
private Integer id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = true)
private Parent parent;
}
I'm assuming you create the schema straight from Hibernate. The goal is to have the foreign key field nullable in the database.
Actually, it can be done.
#JoinColumn(foreignKey = #ForeignKey(name = "none"))
It's only logical to specify a foreign key as lacking referential integrity constraint when you are describing the table that contains this foreign key column.
Surely it's not a best practice to remove the referential integrity constraints in your presistence layer when you are developing some OLTP system; but for data warehouse-alike solutions (first-load-data-then-check-it-as-a-whole) this may be the correct approach.