- By default, a bidirectional OneToMany/ManyToOne association uses a foreign key column on the side which has the single-valued reference with @ManyToOne annotation.
- We can override this default by using an intermediate join table to persist the association.
- To achieve that we have to use @JoinTable annotation on the both sides.
- In this case, we don't have to use 'mappedBy' element of the @OneToMany annotation.
Example
@Entity
public class EntityA {
@Id
@GeneratedValue
private int myIdA;
private String strA;
@OneToMany
@JoinTable(name = "MY_JOIN_TABLE",
joinColumns = {@JoinColumn(name = "MY_ENTITY_A_FK", referencedColumnName = "myIdA")},
inverseJoinColumns = {@JoinColumn(name = "MY_ENTITY_B_FK", referencedColumnName = "myIdB")}
)
private List<EntityB> entityBList;
.............
}
@Entity
public class EntityB {
@Id
@GeneratedValue
private int myIdB;
private String strB;
@ManyToOne
@JoinTable(name = "MY_JOIN_TABLE",
joinColumns = {@JoinColumn(name = "MY_ENTITY_B_FK", insertable = false,
updatable = false, referencedColumnName = "myIdB")},
inverseJoinColumns = {@JoinColumn(name = "MY_ENTITY_A_FK", insertable = false,
updatable = false, referencedColumnName = "myIdA")}
)
private EntityA refEntityA;
.............
}
Note that we have to use insertable/updatable=false on @ManyToOne side, to avoid persisting same relationship twice from both sides. When insertable/updatable=false, the column is not included in the generated SQL INSERT/UPDATE statements.
Now let's see what table structure our association is mapped to:
public class ExampleMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test1");
try {
EntityManager em = emf.createEntityManager();
nativeQuery(em, "SHOW TABLES");
nativeQuery(em, "SHOW COLUMNS from EntityA");
nativeQuery(em, "SHOW COLUMNS from EntityB");
nativeQuery(em, "SHOW COLUMNS from MY_JOIN_TABLE");
emf.close();
} finally {
emf.close();
}
}
public static void nativeQuery(EntityManager em, String s) {
System.out.printf("-----------------------------%n'%s'%n", s);
Query query = em.createNativeQuery(s);
List list = query.getResultList();
for (Object o : list) {
if(o instanceof Object[]) {
System.out.println(Arrays.toString((Object[]) o));
}else{
System.out.println(o);
}
}
}
}
Here's a quick overview of the relationship:
Persisting and loading entities
public class ExampleMain2 {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test1");
try {
persistEntity(emf);
nativeQueries(emf);
loadEntityA(emf);
loadEntityB(emf);
} finally {
emf.close();
}
}
private static void nativeQueries(EntityManagerFactory emf) {
System.out.println("-- native queries --");
EntityManager em = emf.createEntityManager();
ExampleMain.nativeQuery(em, "Select * from EntityA");
ExampleMain.nativeQuery(em, "Select * from EntityB");
ExampleMain.nativeQuery(em, "Select * from MY_JOIN_TABLE");
}
private static void persistEntity(EntityManagerFactory emf) {
EntityManager em = emf.createEntityManager();
EntityB entityB1 = new EntityB();
entityB1.setStrB("testStringB");
EntityB entityB2 = new EntityB();
entityB2.setStrB("testStringB2");
EntityA entityA = new EntityA();
entityA.setStrA("testStringA");
entityA.setEntityBList(Arrays.asList(entityB1, entityB2));
entityB1.setRefEntityA(entityA);
entityB2.setRefEntityA(entityA);
System.out.println("-- persisting entities --");
System.out.printf(" %s%n entityA#entityBList: %s%n", entityA, entityA.getEntityBList());
System.out.printf(" %s%n entityB1#refEntityA: %s%n", entityB1, entityB1.getRefEntityA());
System.out.printf(" %s%n entityB2#refEntityA: %s%n", entityB2, entityB2.getRefEntityA());
em.getTransaction().begin();
em.persist(entityB1);
em.persist(entityB2);
em.persist(entityA);
em.getTransaction().commit();
em.close();
}
private static void loadEntityA(EntityManagerFactory emf) {
System.out.println("-- loading EntityA --");
EntityManager em = emf.createEntityManager();
List<EntityA> entityAList = em.createQuery("Select t from EntityA t").getResultList();
entityAList.forEach(e -> System.out.printf(" %s%n entityA#entityBList: %s%n", e, e.getEntityBList()));
em.close();
}
private static void loadEntityB(EntityManagerFactory emf) {
System.out.println("-- loading EntityB --");
EntityManager em = emf.createEntityManager();
List<EntityB> entityBList = em.createQuery("Select t from EntityB t").getResultList();
entityBList.forEach(e -> System.out.printf(" %s%n entityB#refEntityA: %s%n", e, e.getRefEntityA()));
em.close();
}
} Output-- persisting entities -- EntityA{myIdA=0, strA='testStringA'} entityA#entityBList: [EntityB{myIdB=0, strB='testStringB'}, EntityB{myIdB=0, strB='testStringB2'}] EntityB{myIdB=0, strB='testStringB'} entityB1#refEntityA: EntityA{myIdA=0, strA='testStringA'} EntityB{myIdB=0, strB='testStringB2'} entityB2#refEntityA: EntityA{myIdA=0, strA='testStringA'} -- native queries -- ----------------------------- 'Select * from EntityA' [3, testStringA] ----------------------------- 'Select * from EntityB' [1, testStringB] [2, testStringB2] ----------------------------- 'Select * from MY_JOIN_TABLE' [3, 1] [3, 2] -- loading EntityA -- EntityA{myIdA=3, strA='testStringA'} entityA#entityBList: [EntityB{myIdB=1, strB='testStringB'}, EntityB{myIdB=2, strB='testStringB2'}] -- loading EntityB -- EntityB{myIdB=1, strB='testStringB'} entityB#refEntityA: EntityA{myIdA=3, strA='testStringA'} EntityB{myIdB=2, strB='testStringB2'} entityB#refEntityA: EntityA{myIdA=3, strA='testStringA'}
Example ProjectDependencies and Technologies Used: - h2 1.4.193: H2 Database Engine.
- hibernate-core 5.2.8.Final: The core O/RM functionality as provided by Hibernate.
Implements javax.persistence:javax.persistence-api version 2.1 - JDK 1.8
- Maven 3.3.9
|
|