I have Two Entities. A and B. Relationship between A and B is #ManyToMany. So I have introduced Third entity C for #ManyToMany relationship as it needed for project.
My Entity classes are look like following.
#Entity
class A
{
#OneToMany(cascade = CascadeType.ALL, mappedBy = "a")
List<C> cList;
}
#Entity
class B
{
#OneToMany(cascade = CascadeType.ALL, mappedBy = "b")
List<C> cList;
}
#Entity
class C
{
#ManyToOne
#JoinColumn(name = "ref_a")
A a;
#ManyToOne
#JoinColumn(name = "ref_b")
B b;
}
Now, I want to delete record of entity A or B then it should delete respective record from C.
But when I delete record of A or B it shows
Cannot delete or update a parent row: a foreign key constraint fails
What other configuration it need to delete record from A or B and it will also delete respective record from C?
You don't have to create an entity to map the Many To Many table. The ManyToMany JPA annotation is there. Here is a sample of how to do it.
#Entity
public class Team {
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, mappedBy="teams")
private List<Match> matches;
}
#Entity
public class Match {
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST })
#JoinTable(
name="MATCH_TEAM",
joinColumns={#JoinColumn(name="MATCH_ID", referencedColumnName="ID")},
inverseJoinColumns={#JoinColumn(name="TEAM_ID", referencedColumnName="ID")})
private List<Team> teams;
}
Related
Very simple problem, but it looks like is impossible to achieve what I want
The entities:
public class C {
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "column")
private Set<B> cards = new HashSet<>();
}
public class B {
#ManyToOne(fetch = FetchType.EAGER, optional = false, cascade = CascadeType.DETACH)
#JsonIgnore
#JoinColumn(name="column_id", nullable = false)
private C column;
}
#Repository
public interface BRepository extends JpaRepository<B, Long> {
}
I want to delete the B entity without use the C Repository.
if I do something like:
final C column = columnService.create(board, new C(board, "column name", 1)); //works
final B card = cardService.create(column, new B(column, "card name", 2)); //works
bRepository.delete(card); //NOTHING HAPPENS
Absolutely nothing happens on delete query, no log, data isn't removed from DB, nothing.... doesn't matter if I'm within or out a #transaction.
WHAT I'VE TRIED:
1 - if i change Set cards to FetchType.LAZY, the delete works, [but i really wanted it to be eager]
2 - if create a custom query on repository like:
#Modifying
#Query("DELETE FROM Card c where c.id = :id")
public void deleteById(#Param("id") long id);
then the delete works BUT, I've EntityListeners for this entity, and as per JPA documentation it wont work on custom query... so i need this component working
Is there a way to delete the ONE side of relationship with EAGER fetch without custom query and without loading the other side of relationship?
I have a couple of related entities in a relational DB:
#Entity
public class A {
#Id
#Column(name = "id" ...)
private UUID id;
#OneToMany(mappedBy = "a")
private Set<B> bs;
}
and
#Entity
public class B {
#Id
#Column(name = "id" ...)
private UUID id;
#ManyToOne
#JoinColumn(name = "a_id")
private A a;
}
So I have a B Repository
#Repository
public interface BRepository extends JpaRepository<B, UUID> {
List<B> findByA(A a);
}
And id like to have a query by which I obtain the all Bs with the same A. I'm used to have an a_id column in B, not the whole entity. I don't know how to manage this, nor with a JPA query nor with a NamedQuery.
Many thanks in advance!
Luis
As already answered, the proposed code should already work but it didn't... The fix that worked for me was
List<B> findByA_Id(UUID id);
Table_Field references field field to table Table.
I try to delete an entity which is a child of another entity (one-to-many).
The problem is: If the parent has set a cascade type, I am not able to delete a child directly. The delete command is ignored (using JpaRepository). Only if I remove the cascade setting I am able to delete child.
Is there a way to do this without a native SQL statement?
Parent Entity:
#Entity
public class ExplorerItem {
...
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "explorerItem")
private Set<UserACL> userAcls = new HashSet<>();
...
}
Child Entity:
#Entity
public class UserACL {
...
#ManyToOne
private ExplorerItem explorerItem;
...
}
I'm using JpaRepositories created by Spring Boot:
public interface UserACLRepository extends JpaRepository<UserACL, Long> {
void deleteByUser(User user);
}
You can set orphanRemoval="true" in your #OneToMany annotation. Setting orphanRemoval to true automatically removes disconnected references of entities. On the other hand, if we specify only CascadeType.Remove, no action is taken as it will only disconnect from the association, which is not equivalent of deleting an object.
Eg.
#Entity
public class ExplorerItem {
...
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval="true", mappedBy = "explorerItem")
private Set<UserACL> userAcls = new HashSet<>();
...
}
I have a model that looks like this:
#Entity
#Table(name = "A")
class A {
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "A_categories", joinColumns = {
#JoinColumn(name = "A_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "category_id",
nullable = false, updatable = false) })
private List<Category> categories;
}
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
#Column(name = "category_name")
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
private List<A> a;
}
So there is a many-to-many relationship between A and Category. Now categories are static, and cannot be changed by a user. From the UI, the user will try to save an entity A, and each can have one or more categories. So the JSON that comes back looks a little like this:
{A: {categories: [{id: 1}, {id: 2}, {id: 3}]}}
Now when I try to save this A object (after jackson has unmarshalled to java), I just want entries to be made in the join table, A_categories, for each category the new entity has.
However, the Category entity itself also gets updated. So if you notice, the JSON does not have any category_name, and so the database entry for each Category will also get updated to a null entry for the name.
How can I prevent this from happening?
Two different approaches:
1) Set managed categories before merging.
a.setCategories(readAllByIds(a.getCategories()))
private Collection<Category> readAllByIds(Collection<Category> categories) {
Collection<Category> result = new ArrayList();
for (Category category : categories) {
result.add(entityManager.getReference(Category.class, category.getId()));
}
return result;
}
EntityManager.getReference returns proxy, so the additional benefit is that no database round trips are executed for reading the associated categories.
With this solution you are not merging the deserialized categories into the persistence context, thus Hibernate will not synchronize their state with the database.
2) Do not cascade any operations from A to categories (remove cascade attribute).
This way, neither PERSIST nor MERGE will be cascaded and Hibernate will just use ids of the detached Category instances to store the data into the relationship table.
Sidenote: Generally, cascading REMOVE or ALL in a many-to-many association makes no sense (if you remove an A you probably don't want to remove all the categories it belongs to).
#Column has the attributes insertable and updatable. You can set them to false:
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id", insertable=false, updatable=false)
#GeneratedValue
private Integer id;
#Column(name = "category_name", insertable=false, updatable=false)
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
private List<A> a;
}
You can try adding this
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
#Column(name = "category_name")
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories", cascade=CascadeType.DETACH)
private List<A> a;
}
with the cascade.DETACH should not save changes when you save A entity, but let me know if is not working to make an example modifying the ManyToMany relationship with this DETACH action
how to add one-to-one mapping for the self entity. Like in this example. I want to have parent-child relationship for the Person itself.
#Entity
#Table(name="PERSON")
public class Person {
#Id
#Column(name="personId")
private int id;
#OneToOne
#JoinColumn()
private Person parentPerson;
}
Here is example of bidirectional self mapping #OneToOne (I change column names to SQL notation):
#Entity
#Table(name="PERSON")
public class Person {
#Id
#Column(name="person_id")
private int id;
#OneToOne
#JoinColumn(name = "parent_person_id")
private Person parentPerson;
#OneToOne(mappedBy = "parentPerson")
private Person childPerson;
}
But, I don't understand why you want to use #OneToOne in this case.
I am using it like this:
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "PARENT_ID", nullable = true)
private Person parent;
In order to add parent from your service layer, you need to already have at least one Person in the database.
Lets presume you do. Then create a new person. For e.g.:
#Transactional
public void createPerson() {
Person parent = //get your parent
Person child = new Person();
if (parent != null) {
child.setParent(parent);
}
}
If this is what you mean..