Hibernate many to many - join table is not filled on save - java

I have the following mapping:
#Entity
public class A{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer aId;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "A_to_B",
joinColumns = {#JoinColumn(name = "aId", referencedColumnName = "aId")},
inverseJoinColumns = {#JoinColumn(name = "bId", referencedColumnName = "bId")})
private Set<B> bList;
and I have the B class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.STRING)
public abstract class B {
#Id
#Column(name = "bId")
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;
}
No referenct of A in B.
I have the following code:
List<A> someAs = .... // Generated
B b = ... // also generated
b.setAList(someAs);
Session session = sessionFactory.openSession();
for (A a : someAs) {
session.save(a);
}
session.save(b);
session.close();
The result is All the A instances are saved. the B instance saved too.
But the join table is not filled so if query for the B instance with left join fetch... I get empty A list.
What can I do?

Since you have defined A as the owning side of the relationship by using #JoinTable, you will have to add the B instances to A in order for them to be saved in the join table:
a.setBList(someBs);
Alternatively, you can change the owning side to B and leave the code as it is.

Related

Load entity without associations

Let's say I have the following entities and associations:
Entity A:
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL, orphanRemoval = true)
private List<B> b;
}
Entity B:
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
#OneToMany(mappedBy = "b", cascade = CascadeType.ALL, orphanRemoval = true)
private List<C> c;
}
Entity C:
#Entity
public class C {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "b_id")
private B b;
}
Using the .findAll() method of the CrudRepository for entity A, it will return each A with its associated B's in a list. Also, each B will have each of its associated C's in a list.
My question is: If I in some cases only want to load all A's with their B's, but I don't want the C's in the B's, would that be possible? Could I create a custom query to do that, or is there another way? I hope it is clear what I want to achieve.
I think your problem is mappedBy values in one side of OneToMany relationships.
mappedBy value must be the the name of variable in the other side. So in your cases, you can do this:
In Entity A: change mappedBy = "citizen" to mappedBy = "a"
In Entity B: change mappedBy = "citizen" to mappedBy = "b"
I know Entity a,b,c is just an example, but you should follow above pattern when designing your models relationships.

Hibernate querying on ghost column

I have two entities that used to be linked by a one to many relation but now they are linked by a many to many relation declared as follow :
SalesTeam entity :
#Entity
#Table(name = "SALES_TEAMS")
public class SalesTeam {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.REFRESH, CascadeType.PERSIST})
#JoinTable(name = "WORKFLOW_FOR_SALESTEAM", inverseJoinColumns = {
#JoinColumn(name = "WFC_ID")
})
private List<WorkFlowCode> workFlowCodes = new ArrayList<>();
}
And the WorkFlowCode entity :
#Entity
#Table(name = "WORK_FLOW_CODE")
public class WorkFlowCode {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.REFRESH, CascadeType.PERSIST})
#JoinTable(name = "WORKFLOW_FOR_SALESTEAM", inverseJoinColumns = {
#JoinColumn(name = "ST_ID")
})
private List<SalesTeam> salesteam = new ArrayList<>();
}
As I said the relation use to be one SalesTeam for several workflow codes but the requirement change and now it need to be a many to many relation. So I had a relation table and remove the former SALES_TEAM_ID column from the WORK_FLOW_CODE table. The problem is that now I always get an error when I try to get the WorkFlowCode from a SalesTeam. It appears that hibernate still adds the removed column to the query thus the relation had changed and nothing is left from the former relation description.
Here is the hibernate generated query :
select workflowco0_.SALES_TEAMS_ID as SALES_TE3_13_0_, workflowco0_.WFC_ID as WFC_ID4_16_0_, workflowco1_.ID as ID1_17_1_ from WORKFLOW_FOR_SALESTEAM workflowco0_ inner join WORK_FLOW_CODE workflowco1_ on workflowco0_.WFC_ID=workflowco1_.ID where workflowco0_.SALES_TEAMS_ID=?
As you can see the former SALES_TEAM_ID from WORK_FLOW_CODE table is still there.
How can I remove it ?
Thx

#ManyToMany JPA relation using a 2nd degree relationship in join table and UK

I have this entity model (simplified):
#Entity
class A {
#Id
String id;
Collection<B> bs;
}
#Entity
class B {
#Id
String id;
#ManyToOne
#JoinColumn(name = "c_id", nullable = false)
C c;
}
#Entity
class C {
#Id
String id;
}
What would be the best approach to add a join table between A and B (many-to-many relation) and enforce a composite UK using A.id and B.c.id?
I tried something like this:
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable
(name = "a_b",
joinColumns = #JoinColumn(name = "a_id"),
inverseJoinColumns = {
#JoinColumn(name = "b_id", referencedColumnName = "id"),
#JoinColumn(name = "c_id", referencedColumnName = "c_id")},
uniqueConstraints = #UniqueConstraint(name = "uk_a_c", columnNames = {"a_id", "c_id"}))
but I get a MultipleBagFetchException, which is very strange. It must be related to the fact that c_id is not part of the primary key. If I remove c_id from inverseJoinColumns it works as expected, but it's not what I need.
If between B and C it's a one-to-one association, you could share the primary key between those two, so that
B.id = C.id = B.c.id
Then the join table unique key can be between A.id and B.id.
My final mapping looks like this:
#Entity
#EntityListeners(ABListener.class)
#Table(name = "a_b", uniqueConstraints =
#UniqueConstraint(name = "uk_rel_a_b", columnNames = {"a_id", "c_id"}))
public class AB extends BaseEntity<Long> {
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "a_id")
private A a;
#ManyToOne(optional = false)
#JoinColumn(name = "b_id")
private B b;
#ManyToOne(optional = false)
#JoinColumn(name = "c_id")
private C c;
public AB(A a, B b) {
this.a = a;
this.b = b;
}
/* rest of the class */
}
And a listener to handle setting of C:
public class ABListener {
#PrePersist
#PreUpdate
void handleCUpdate(AB ab) {
if (ab.getB() != null) {
ab.setC(ab.getB().getC());
}
}
}

JPA #SecondaryTable foreign key violation

I want to map two entities in a one to many fashion.
A->[B, B]
I want to add to the join table more fields. Pojos looks like:
#Entity
#Table(name = "A", schema = "examples")
#SecondaryTable(name = "A_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "a_id", referencedColumnName = "id"))
public class A
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Basic
private String name;
#Basic
private Integer field1;
#Column(table = "A_B", name = "field2")
private Integer field2;
#OneToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "A_B", joinColumns = {#JoinColumn(name = "a_id")}, inverseJoinColumns = {#JoinColumn(name = "b_id")})
private List<B> datastores;
}
#Entity
#Table(name = "B", schema = "examples")
#SecondaryTable(name = "A_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "b_id", referencedColumnName = "id"))
public class B
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Basic
private String field1;
#Basic
private int field2;
#Column(table = "A_B", name = "field3")
private int field3;
}
Thing is that in order to add I had to remove the foreign key on A_B table. How do I solve the mapping to allow the foreign keys ?
Thanks.
I am missing something, but I don't see why both Entity A and Entity B are mapping to table "A_B". By adding it to Entity A as a secondary table, you are stating that every time an insert to Table a occurs, an insert to table A_B must also occur - creating a strict 1:1 relation between rows in the two tables. Except that you do the same thing to entity B, so you will end up with rows in A_B with A_id=somevalue, and B_id= null and others with a_id=null while b_id=somevalue. Table "A_B" looks like a relation table, so this probably isn't what you want.
If A_B is a relationtable you should map it using a ManyToMany as you have for the "A_B" table. If there are extra fields that need to be populated, create a AB Entity, and create a OneToMany from A->AB and B->AB, and ManyToOne from AB->A and AB->B.

Can't delete element from #OneToMany collection

i have an entity Entity1 that have one to many relation with Entity2 as follows:
1- Entity1:
#Entity
#Table(name = "Entity1", catalog = "mydb")
public class Entity1 implements java.io.Serializable {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "entity1", cascade = javax.persistence.CascadeType.ALL)
#OrderBy("id")
private Set<Entity2> collection = new HashSet<Entity2>(
0);
}
2- Entity2: (equals and hashcode method overridden)
#Entity
#Table(name = "entity2", catalog = "advertisedb")
public class Entity2 implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "pkid", unique = true, nullable = false)
#Basic(fetch = FetchType.EAGER)
private long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "fk_entity1", nullable = false)
private Entity1 entity1;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "apid", nullable = false)
private Entity3 entity3;
}
3- Here's how i am removing the entity from the collection:
entity1Obj.getEntity2().remove(entity2);
log.debug("size after remove: "+ entity1Obj.getEntity2().size()); // size decreases correctly, so the entity is removed from the collection
entity1Dao.updateEntity1(entity1);
4- DAO method:
public void updateEntity1(Entity1 entity1) {
getCurrentSession().update(getCurrentSession().merge(entity1));
}
Problem: what i get in the console, is a select query for the entity2 that should be removed, and no delete query, and nothing is getting deleted.
please advise how to fix this issue.
i replaced cascade = CascadeType.ALL with orphanRemoval = true and it works fine now.

Categories

Resources