I have a Table A , Table AB , TABLE B , Table AB has foreign key references to Table A and Table B. There is a One to One relation between Table A and Table AB.and Many to one between Table B and Table AB.
My question is if i am saving domain for table B, it is saving data in table AB but not in A Please let me know if this is the expected behavior or can i save all data in all other tables just by calling save on Object B
Class A
{
#OneToOne(mappedBy="abpk.a")
#Cascade({ org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
private AB ab;
}
#Entity
#AssociationOverrides({
#AssociationOverride(name = "abpk.a", joinColumns = #JoinColumn(name = "colA", referencedColumnName = "colA")),
#AssociationOverride(name = "abpk.b", joinColumns = #JoinColumn(name = "Colb", referencedColumnName = "colB")) })
Class AB
{
ABPK abpk = new ABPK();
A a;
B b;
//inner class
class ABPK
{
A a;
B b;
#OneToOne(fetch = FetchType.LAZY)
#Cascade({ org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
#JoinColumn(name="b")
//Getter for A
GetA();
#ManyToOne(fetch = FetchType.LAZY)
//Getter for B
GetB();
}
}
Class B
{
#OneToMany(fetch = FetchType.LAZY, mappedBy = "abpk.b")
#Cascade({ org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
private List<AB> abList;
}
That depends on how you have set your cascade policies. If you have set it to CASCADE_TYPE ALL, then theoretically it should persists.
But in any case, it is better to wire up both sides of a relationship when you are trying to persist associations.
Related
I have 3 entities like this:
#Entity
public class A {
#Id
private String id;
}
#Entity
#IdClass(B.BPK.class)
public class B {
#Id
private String id;
#Id
#ManyToOne
private A a;
}
#Entity
public class C {
#Id
private int refOne;
#Id
private int refTwo;
#Id
private A a;
#ManyToMany
private Set<B> bs;
}
I have a question about the table generated for the relation #ManyToMany of the entity C. There are 5 columns generated: refOne, refTwo, a_id, b_id, b_a_id.
As you could see, a_id and b_a_id are foreign key on the same column and in my model, it's not possible that a_id is different from b_a_id.
Is there a solution to not duplicate the column?
EDIT:
I insist on this point, A is part of the id of C, and bs in C could be empty.
I think about several solutions but none of them are really satisfying:
Use a Converter that used the b_id and a_id to recover bs entities
I tried to manage the columns with #JoinTable like this:
JoinTable approach:
#JoinTable(name = "bs", joinColumns = {
#JoinColumn(name = "refOne", referencedColumnName = "refOne"),
#JoinColumn(name = "refTwo", referencedColumnName = "refTwo"),
#JoinColumn(name = "a_id", referencedColumnName = "a_id")
}, inverseJoinColumns = {
#JoinColumn(name = "b_id", referencedColumnName = "b_id"),
#JoinColumn(name = "b_a_id", referencedColumnName = "b_a_id", insertable = false, updatable = false)
But I face theses problems Mixing insertable and non insertable columns in a property is not allowed orParameter index out of range`
})
Since C has already a reference to A, you can fetch A.bs, without having to declare a collection of bs inside C. You need, however, to have a Set<B> in entity A.
Unless, of course, you can't change the mapping...
You could annotate private A a; with #JoinColumn that references b_a_id. There are also other annotations which let you you configure the mapping even more. Yet there is no issue with having 2 FK columns containing the same value.
Also you should always create your tables by hand. DOn't let it be generated in production.
For more info read the docs. Maybe you have to search for a more specific problem, but resources are out there
So nothing I tried seems to work. I would like to have something like this:
class A {
B foo;
B bar;
}
class B {
A baz;
}
What I tried in class A is as follows:
#OneToOne(targetEntity = B.class)
#JoinColumn(name = "foo_id")
#Cascade(CascadeType.ALL)
public B getFoo() {
return foo;
}
#OneToOne(targetEntity = B.class)
#JoinColumn(name = "bar_id")
#Cascade(CascadeType.ALL)
public B getBar() {
return bar;
}
which does not seem to work. I always end up where foo_id and bar_id is same for a reason I do not understand.
So when I inspect table "A" in my DB for row with id 1, I would like to have:
foo_id = 1
bar_id = 2
and in Table B, I should have 2 entities with id 1 and 2, where both have baz_id = 1;
Is baz_id intended to be a FK back to A? Because I think the database mapping to model is wrong in that case. You've already established the FK relationship from the PK of B to either A.foo_id or A.bar_id.
Also be careful with your cascading rules on a relationship like this. SQL Server will reject two FKs to the same table unless the DB action for cascading is "no action".
I do happen to know that what you're trying to do is possible in JPA, since I just recently did it on an entity myself:
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
#JoinColumn(name = "portal_logo_id", referencedColumnName = "id", nullable = true)
private PortalResourceModel logo;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
#JoinColumn(name = "portal_favicon_id", referencedColumnName = "id", nullable = true)
private PortalResourceModel favicon;
I also don't have a mapping in PortalResourceModel for logo or favicon, because that side of the relationship doesn't know how it is being used. And I can't have a generic mapping from multiple relationships on the owning side to a single relationship on the mappedBy side.
Recently I created a project and in my models I have ManyToMany back references.
My model is like below:
#Entity
public class A {
#ManyToMany(mappedBy = "parents", fetch = FetchType.EAGER)
private Set<A> children = new HashSet<>();
#ManyToMany
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#JoinTable(
name = "link_a_recursion",
joinColumns = #JoinColumn(name = "child_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "parent_id", referencedColumnName = "id")
)
private Set<A> parents = new HashSet<>();
//I removed the rest ( setter, getter and other fields )
}
When I fetch this model and I want to load children it throws StackOverFlowException error ( Recursive exception )
I want to know is there any way to say to hibernate just load one level of associate and don't go deep.
For clarify:
A.children[0].children[0].children should be null to stop recursion
I want to load the first children not all the children inside of the other children
Edited:
I add another entity:
#Entity
public class B {
#OneToMany(mappedBy = "b")
private Set<A> entities = new HashSet<>();
//setter, getter
}
and add below to A entity:
#ManyToOne
private B b;
then I changed below:
#ManyToMany(mappedBy = "parents", fetch = FetchType.EAGER)
to
#ManyToMany(mappedBy = "parents", fetch = FetchType.LAZY)
and in my BService my findOne function is like below:
#Transactional(readOnly = true)
public B findOne(Long id) {
B b = repository.findOne(id);
for (A a: b.getEntities()) {
a.getChildren().size();
}
return b;
}
but again I'm getting error :(
Try lazy fetch instead
#ManyToMany(mappedBy = "parents", fetch = FetchType.LAZY)
How do you create an annotation for the following case:
Table A
id
name
Table B
id
name
Table C
id
name
Table AB
a_id
b_id
Table ABC
id
a_id
b_id
c_id
name
How can you represent table ABC on hibernate using annotations?
Table AB is not an object it is used as a table join between tables A and B.
The annotation for A and B would look something like this:
For A:
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="AB",
joinColumns={#JoinColumn(name="a_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="b_id", referencedColumnName="id")})
private Set bs;
For B:
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="AB",
joinColumns={#JoinColumn(name="b_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="a_id", referencedColumnName="id")})
private Set as;
Thanks in Advance.
I would have done something like that :
A:
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
#JoinTable(name = "AB", joinColumns = #JoinColumn(name = "A_ID",
referencedColumnName = "ID"), inverseJoinColumns = #JoinColumn(name = "B_ID",
referencedColumnName = "ID"))
private List<B> bs;
Same for B.
ABC:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "A_ID")
private A a;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "B_ID")
private B b;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "C_ID")
private C c;
I am using Hibernate 4.1.10.Final as my JPA provider, Spring and Spring MVC. There are two entities:
#Entity
#Table(name = "a")
public class A {
#Id
private String id;
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<B> bs;
}
#Entity
#Table(name = "b")
public class B {
#Id
private String id;
#ManyToOne
#JoinColumn(name = "fk_a_id")
private A a;
}
I need to get an A and it's bs, so I use the find(A.class,id) of EntityManager.
A a1 = em.find(A.class, id);
a1.getBs().size();
For which the result is: the size of bs is zero (which means that there is no associated B).
But I'm sure that there are many associated Bs in the database, and indeed the data can been loaded from database while checking via the console.
When I use Query:
Query query = em.createQuery("SELECT a FROM A AS a WHERE a.id = ?1",A.class);
query.setParameter(1, id);
A a= (A) query.getSingleResult();
a.getBs().size(); // = 22
I instead get a size = 22.
What's wrong?
Since you used the mappedBy property in your #OneToMany, the owner of the relation is B and not A. That's why when you load an instance of A, the corresponding Bs are not loaded. Try modifying your annotations with the following :
In class A :
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name="fk_a_id")
private Set<B> bs;
In class B :
#ManyToOne
#JoinColumn(name = "fk_a_id", insertable=false, updatable=false)
private A a;