Nuñito Calzada
Nuñito Calzada

Reputation: 1950

Deleting an object in a SpringBoot 2.0.5.RELEASE app using Spring Data JPA

I have a basic SpringBoot 2.0.5.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.

I have this class:

public class User implements Serializable {

    @OneToMany( cascade = CascadeType.ALL,orphanRemoval = true, 
                fetch = FetchType.EAGER,mappedBy = "user")
    @JsonIgnore
    private List<Wallet> wallets = new ArrayList<Wallet>();

..
}

and this one:

public class Wallet implements Serializable {

@ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "invoice_id")
    @JsonIgnore
    @NotNull
    private Invoice invoice;



    @OneToMany(mappedBy = "wallet", 
               cascade = CascadeType.ALL, 
               orphanRemoval = true, fetch = FetchType.LAZY)
    @JsonIgnore
    private Set<Purchase> purchases = new HashSet<>();


    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "user_id" , nullable=false)
    @JsonIgnore
    private User user;

..
}

and this other one:

public class Purchase implements Serializable {

        @ManyToOne(fetch = FetchType.EAGER)
            @JoinColumn(name = "wallet_id")
            @JsonIgnore
            Wallet wallet;

        ...

    }

But I delete a wallet form the controller that have an invoice and purchases and belong to a user the wallet is not deleted from the DB

walletService.delete(walletService.findById(id).get());

this is the service method:

@Transactional
    public void delete(Wallet wallet) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("deleting Wallet [ " + wallet + " ]");
        }


        wallet
            .getPurchases()
            .parallelStream()
            .forEach(p -> purchaseService.delete(p));

        walletRepository.delete(wallet);

    }

and

 @Transactional
     public void delete (Purchase purchase ) {
         purchaseRepository.delete (purchase);
     }

in the properties file:

spring.jpa.show-sql=true

and the last query I see in the console is this one:

select
    purchases0_.wallet_id as wallet_i8_13_0_,
    purchases0_.id as id1_13_0_,
    purchases0_.id as id1_13_1_,
    purchases0_.amount as amount2_13_1_,
    purchases0_.wallet_id as wallet_i8_13_1_ 
from
    t_purchase purchases0_ 
where
    purchases0_.wallet_id=?

and no delete and no Exceptions !!! !

Upvotes: 2

Views: 1219

Answers (4)

en Lopes
en Lopes

Reputation: 2133

try this in the controller:

    user.getWallets().remove(wallet);
    walletService.delete(wallet);       
    userService.save(user);

Upvotes: 1

Mohammadreza  Alagheband
Mohammadreza Alagheband

Reputation: 2230

Problem Description

The problem raise when you are trying to remove each children by itself while the parent (i.e. parent) still has a reference to it and since the reference has a cascade All set on it (including persist and merge), it causes child (i.e. purchase) to be revived in the context by undoing the remove.

  • Keep in mind that references mirror what should be happen in database, so you should null out references to objects you are removing otherwise you won't achieve the desire state you're expecting

source : JPA / Hibernate removing "child" entities


Solution

Get rid of removing children by making a stream on parent and change wallet class to have cascade remove type on purchases relationship :

@OneToMany(mappedBy = "wallet", 
           cascade = CascadeType.REMOVE,  // <--- here should be changed
           orphanRemoval = true, fetch = FetchType.LAZY)
@JsonIgnore
private Set<Purchase> purchases = new HashSet<>();

Then you just need to call walletRepository.delete(wallet); to delete parent and children altogether.

Upvotes: 0

polux
polux

Reputation: 98

Could this be possible solution: Hibernate configuration -> orphanRemoval = true will delete wallets only in case User entity is deleted?

Upvotes: 0

MyTwoCents
MyTwoCents

Reputation: 7622

I can think of 2 Cases here

  1. You have added mappedBy in Wallet Class so Purchase is owner entity and you need to delete from Purchases

    try changing delete Wallet method like this

    @Transactional
    public void delete(Wallet wallet) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("deleting Wallet [ " + wallet + " ]");
        }
    
        wallet.getPurchases().removeAll();
    walletRepository.delete(wallet);
    
    }
    
  2. Ideally you have configured CascadeType.ALL in Wallet so doing

    walletRepository.delete(wallet);

    should remove Set<Purchase> too.

Upvotes: 1

Related Questions