Home Do I have to set both sides for a bidirectional relationship?
Reply: 0

Do I have to set both sides for a bidirectional relationship?

user4886
1#
user4886 Published in May 21, 2018, 10:49 pm
@Entity
public class A {
    @GeneratedValue
    @Id
    private long id;

    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    @OneToMany(mappedBy = "a")
    List<B> bs;

    public List<B> getBs() {
        return bs;
    }

    public void setBs(final List<B> bs) {
        this.bs = bs;
    }
}
@Entity
public class B {
    @GeneratedValue
    @Id
    private long id;

    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    @ManyToOne
    @JoinTable
    A a;

    public A getA() {
        return a;
    }

    public void setA(final A a) {
        this.a = a;
    }
}

To establish the relationship, I have to call

b.setA(a);
a.getBs().add(b);

Why is both necessary, Why is it not sufficient to do only

b.setA(a);

or

a.getBs().add(b);

?

The relationship is stored in a join table, and b.setA(a) will update that join table.

But when I do a query afterwards, a.getBs() is empty. Why is that?

Here is a Test case that illustrates the question. Note that the very last assert fails.

public class QuickTestAB2 {

    private static String dbUrlBase = "jdbc:derby:testData/db/test.db";

    private static String dbUrlCreate = dbUrlBase + ";create=true";

    private static String dbUrlDrop = dbUrlBase + ";drop=true";

    private EntityManagerFactory factory;

    private EntityManager em;

    public Map<String, String> createPersistenceMap(final String dbUrl) {
        final Map<String, String> persistenceMap = new HashMap<>();
        persistenceMap.put("javax.persistence.jdbc.url", dbUrl);
        return persistenceMap;
    }

    public void dropDatabase() throws Exception {
        if (em != null && em.isOpen()) {
            em.close();
        }
        if (factory != null && factory.isOpen()) {
            factory.close();
        }
        try (Connection conn = DriverManager.getConnection(dbUrlDrop)) {

        } catch (final SQLException e) {
            // always

        }
    }

    public void deleteDatabase() throws Exception {
        dropDatabase();
        final File file = new File("testData/db/test.db");
        if (file.exists()) {
            FileUtils.forceDelete(file);
        }
    }

    public void createNewDatabase() throws SQLException, IOException {

        FileUtils.forceMkdir(new File("testData/db"));
        try (Connection conn = DriverManager.getConnection(dbUrlCreate)) {

        }
    }

    @BeforeClass
    public static void setUpBeforeClass01() throws Exception {
        Tests.enableLog4J();
        JPATests.enableJPA();

    }

    @AfterClass
    public static void tearDownAfterClass01() throws Exception {

    }

    @Before
    public void setUp01() throws Exception {

        deleteDatabase();
        createNewDatabase();
        final Map<String, String> map = createPersistenceMap(dbUrlCreate);
        factory = Persistence.createEntityManagerFactory("pu", map);

    }

    @After
    public void tearDown01() throws Exception {
        if (em != null && em.isOpen()) {
            em.close();
        }
        em = null;
        if (factory != null && factory.isOpen()) {
            factory.close();
        }
        factory = null;
    }

    @Test
    public void test01() throws Exception {
        em = factory.createEntityManager();
        final A a = new A();
        final B b = new B();
        b.setA(a);
        try {
            em.getTransaction().begin();
            em.persist(a);
            em.persist(b);
            em.getTransaction().commit();
        } finally {
            em.close();
        }
        em = factory.createEntityManager();
        B b2;
        A a2;
        try {
            em.getTransaction().begin();
            Query q = em.createQuery("SELECT b FROM B b");
            b2 = (B) q.getSingleResult();
            q = em.createQuery("SELECT a FROM A a");
            a2 = (A) q.getSingleResult();
            em.getTransaction().commit();
        } finally {
            em.close();
        }
        assertThat(a2, is(not(nullValue())));
        assertThat(b2, is(not(nullValue())));

        assertThat(b2.getA(), is(not(nullValue())));
        assertThat(a2.getBs().isEmpty(), is(false));

    }

}

Motivation: It can be useful to change a bidirectional relationship by changing "only one side", when the number of a.Bs gets large. In this case, an UPDATE SELECT query on the owning side is much faster than to call a.getBs().remove(b) See also here.

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.32039 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO