Remove

Voor dat wij een entiteit uit onze databank kunnen verwijderen via JPA, moet die entiteit in de Persistence Context staan. Bovendien moet het, via een transactie, beveiligd doorgevoerd worden voor het daadwerkelijk verwijderd is geweest van onze databank.

Als wij dus een entiteit willen verwijderen die nog niet in de Persistence Context staat, dan moeten wij die eerst erin gaan slepen, bij voorbeeld met een find(). Deze heeft een PK nodig om een entiteit in onze databank op te sporen. Als hij hem vindt, dan geeft hij ons een entiteit-object terug en, nog belangrijker, die entiteit staat dan in onze Persistence Context – dan kunnen wij hem verwijderen via remove().

    public static void main(String[] args) {
        BankAccount retrievedBankAccount = findBankAccountById(1L);
        em.remove(retrievedBankAccount);
    }

    private static BankAccount findBankAccountById(Long id){
        BankAccount retrievedBankAccount = em.find(BankAccount.class, id);
        if(retrievedBankAccount == null){
            throw new BankAccountRepositoryException("No bank account with id " + id + " found");
        } else {
            System.out.println("Bank account with id " + id + " found!");
        }
        return retrievedBankAccount;
    }

Als wij nu onze databank raadplegen zien wij dat hij er nog staat:

Dit komt omdat wij een remove() hebben uitgevoerd, wat daadwerkelijk onze entiteit verwijdert heeft uit onze cache-databank (Persistence Context), maar die veranderingen zijn wij vergeten om beveiligd door te voeren via een transactie en een commit.

    public static void main(String[] args) {
        BankAccount retrievedBankAccount = findBankAccountById(1L);
        em.getTransaction().begin();
        em.remove(retrievedBankAccount);
        em.getTransaction().commit();
    }

    private static BankAccount findBankAccountById(Long id){
        BankAccount retrievedBankAccount = em.find(BankAccount.class, id);
        if(retrievedBankAccount == null){
            throw new BankAccountRepositoryException("No bank account with id " + id + " found");
        } else {
            System.out.println("Bank account with id " + id + " found!");
        }
        return retrievedBankAccount;
    }

Als wij nu onze databank bekijken zien we dat hij daadwerkelijk leeg is.

Onze code kan wel een refactor gebruiken. Zo kunnen wij, bij voorbeeld, alle “remove” logica in een aparte methode plaatsen om herbruikbaarheid en leesbaarheid naar omhoog te doen:

    public static void main(String[] args) {
        removeBankAccountById(1L);
    }
    private static void removeBankAccountById(Long id){
        BankAccount retrievedBankAccount = findBankAccountById(id);
        em.getTransaction().begin();
        em.remove(retrievedBankAccount);
        em.getTransaction().commit();
    }

    private static BankAccount findBankAccountById(Long id){
        BankAccount retrievedBankAccount = em.find(BankAccount.class, id);
        if(retrievedBankAccount == null){
            throw new BankAccountRepositoryException("No bank account with id " + id + " found");
        } else {
            System.out.println("Bank account with id " + id + " found!");
        }
        return retrievedBankAccount;
    }

Soms kan het ook nuttig zijn om een record te laten verwijderen op basis van een object in plaats van een id.

    public static void main(String[] args) {
        BankAccount bankAccount = new BankAccount();
        bankAccount.setId(1L);
        bankAccount.setIban("BE11 1111 1111 1111");
        bankAccount.setOwnerName("Doctor Evil");
        bankAccount.setBalance(1_000_000);
        
        removeBankAccount(bankAccount);
    }
    private static void removeBankAccount(BankAccount bankAccount){
        BankAccount managedBankAccount = em.merge(bankAccount);
        em.getTransaction().begin();
        em.remove(managedBankAccount);
        em.getTransaction().commit();
    }

Dit is alvast een mooie kans om merge uit te leggen. Je ziet op regel 2 dat wij een nieuw BankAccount object aanmaken. Deze is dan een doodgewone POJO, en is wordt nog niet beheerd door de EntityManager, en staat niet in de Persistence Context. Toch geven wij het mee op regel 8, want wij willen nu een verwijder-operatie laten gebeuren via een object die wij in handen hebben, en niet per se een id.

Wij hebben al twee manieren gezien om een zogenaamde “detached entity” (een entiteit-object die nog niet in de Persistence Context staat) in de Persistence Context te slepen: persist() en find(). Nu find() gaan wij hier niet gebruiken want het is net de bedoeling dat wij het via een object doen en niet een id (anders hadden wij removeBankAccountById() gebruikt). Persist() zou nog werken, maar die dient om nieuwe entiteiten toe te voegen als nieuwe record op de databank. Hier gaat het over het binnenslepen van een bestaande entiteit op basis van een object, en dat kunnen wij doen met merge(). Zo wordt, op basis van de PK van jouw object, een entiteit opgezocht in de databank en in de Persistence Context geslopen (je krijgt een managed entity terug als return-waarde).

Eens wij een managed entity hebben (met andere woorden, die staat in de Persistence Context), kunnen wij het gaan verwijderen via remove(). Wel onthouden dat wij een transactie moeten starten en een commit uitvoeren om het in onze daadwerkelijke databank zo te zien.