Search code examples
javahibernatecascadecascading-deletes

Saving element of collection in hibernate


I have class ResponsedAd:

@Entity
@Table(name = "RESPONSED_AD_TEST")
@NamedQueries({
 @NamedQuery(name = "responsedAdsByAd",query="from ResponsedAd where ad = :ad")
})
@Component
public class ResponsedAd {

@Id
@SequenceGenerator(name = "standard", initialValue = 1)
@GeneratedValue(generator = "standard", strategy = GenerationType.SEQUENCE)
private long id;

@ManyToOne(fetch = FetchType.EAGER)
@Cascade(CascadeType.REMOVE)
@JoinColumn(name = "AD",nullable = false)
private Ad ad;

@Column(nullable = false)
private LocalDateTime dateTimeOfResponse;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "CLIENT",nullable = false)
private Client client;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TRANSLATOR",nullable = false)
private Translator translator;

@Column(nullable = false)
@Enumerated(EnumType.STRING)
private ResponsedAdStatus status;


public ResponsedAd(){}

He has @ManyToOne relationships to classes:Client,Ad,Translator

Client:

    @Entity
@NamedQueries({
    @NamedQuery(name =  "clientByEmail",
                query = "from Client client where client.email = :email")
})
@Table(name = "CLIENT_TEST")
@PrimaryKeyJoinColumn(name= "client_id")
public class Client extends User{

    /**
     * Version of this class in production 
     */
    private static final long serialVersionUID = 1L;

    @OneToMany(fetch = FetchType.EAGER,orphanRemoval = true,mappedBy = "client")
    @Cascade(CascadeType.ALL)
    public List<Ad> ads = new ArrayList<>();

    @OneToMany(fetch = FetchType.EAGER,orphanRemoval = true,mappedBy = "client")
    @Cascade(CascadeType.ALL)
    private List<ResponsedAd> responsedAds = new ArrayList<>();

    public Client(){}

    public static long getSerialversionuid() {
        return serialVersionUID;
    }
    public List<Ad> getAds() {
        return ads;
    }

    public void setAds(List<Ad> ads) {
        this.ads = ads;
    }

    public void addAd(Ad ad){
        ads.add(ad);
        ad.setClient(this);
    }

    public void removeAd(Ad ad){
        ads.remove(ad);
        ad.setClient(null);
    }

    public List<ResponsedAd> getResponsedAds() {
        return responsedAds;
    }

    public void addResponsedAd(ResponsedAd responsedAd){
        responsedAds.add(responsedAd);
        responsedAd.setClient(this);
    }

    public void removeResponsedAd(ResponsedAd responsedAd){
        responsedAds.remove(responsedAd);
        responsedAd.setClient(null);
    }

Ad:

    @Entity
@NamedQueries({
    @NamedQuery(name =  "getAllAds",
            query = "from Ad"),
    @NamedQuery(name =  "deleteById",
    query = "delete from Ad where id = :id")
})
@FieldNotMatch(first = "initLanguage",second = "resultLanguage", message = "Languages must be different")
@Table(name = "AD_TEST")
public class Ad  implements Serializable{

    /**
     * Version of this class in production 
     */
    private static final long serialVersionUID = 1L;

    @Id
    @SequenceGenerator(name = "standard", initialValue = 1)
    @GeneratedValue(generator = "standard", strategy =GenerationType.SEQUENCE)
    @Column(name = "AD_ID")
    private long id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "CLIENT",nullable = false)
    private Client client;

    @NotBlank
    @Column(name = "AD_NAME", nullable = false)
    private String name;

    @NotBlank
    @Column(name = "AD_DESC",nullable = false,length = 1000)
    @Lob
    @Size(min = 0, max = 1000)
    private String description;

    @Column(name = "AD_COUNTRY", nullable = false)
    private String country;

    @Column(name = "AD_CITY", nullable = false)
    private String city;


    /**
     * Добавить проверку валидности даты
     */
    @DateTimeFormat(iso = ISO.DATE,pattern = "dd.MM.yyyy")
    @Column(name = "AD_END_DATE",nullable = false)
    private LocalDate endDate;

    @Column(name = "AD_INIT_LANGUAGE",nullable = false)
    @Enumerated(EnumType.STRING)
    private Language initLanguage;

    @Column(name = "AD_RESULT_LANGUAGE",nullable = false)
    @Enumerated(EnumType.STRING)
    private Language resultLanguage;

    @Column(name = "AD_TRANSLATE_TYPE",nullable = false)
    @Enumerated(EnumType.STRING)
    private TranslateType translateType;

    @Lob
    @Column(name = "AD_FILE")
    private byte[] file;

    @Column(name = "AD_COST",nullable = false,precision = 2)
    private double cost;

    @Column(name = "AD_CURRENCY",nullable = false)
    @Enumerated(EnumType.STRING)
    private Currency currency;

    @Column(name = "AD_PUBLICATIO_DATE_TIME",nullable = false)
    private LocalDateTime publicationDateTime;

    @Column(name = "AD_STATUS",nullable = false)
    @Enumerated(EnumType.STRING)
    private AdStatus status;

    @OneToMany(fetch = FetchType.EAGER,orphanRemoval = true,mappedBy = "ad")
    @Cascade(CascadeType.ALL)
    private List<ResponsedAd> responsedAds = new ArrayList<>();

    public Ad(){}

It is method for saving ResponsedAd:

@Override
    public void saveResponsedAd(String email,long adId) throws NonExistedAdException {
        Ad ad = adDao.get(adId);
        if(ad == null){
            throw new NonExistedAdException();
        }

        ResponsedAd responsedAd = new ResponsedAd();

        Translator translator = translatorDao.getTranslatorByEmail(email);
        Client client = ad.getClient();

        translator.addResponsedAd(responsedAd);
        client.addResponsedAd(responsedAd);
        ad.addResponsedAd(responsedAd);

        responsedAd.setStatus(ResponsedAdStatus.SENDED);
        responsedAd.setDateTimeOfResponse(LocalDateTime.now());

        responsedAdDao.save(responsedAd);

    }

After saving I have appropriate row in Responsed_Ad_Test table.

when I want to delete Ad I have a bug, my client object from method below has 3 the same ResponsedAd, and I have exception: deleted object would be re-saved by cascade (remove deleted object from associations).

And I don't know why my client has repeated ResponsedAd object, while saving my client object has only 1 ResponsedAd object

@Override
    public void deleteById(long id) throws NonExistedAdException,UnacceptableActionForAcceptedAd{
        Ad ad = get(id);
        if(ad.getStatus().equals(AdStatus.ACCEPTED)){
            throw new UnacceptableActionForAcceptedAd();
        }
        List<ResponsedAd> list = new ArrayList<>(ad.getResponsedAds());
        list.stream().forEach(rad->{
            Translator translator = rad.getTranslator();
            Client client = rad.getClient();
            translator.removeResponsedAd(rad);
            ad.removeResponsedAd(rad);
            client.removeResponsedAd(rad);
        });

        Client client = ad.getClient();
        client.removeAd(ad);
    }

I understand that problem is in saving ResponsedAd object, but I can't understand creating several the same ResponsedAd objects for client. Help please:)


Solution

  • Problem was in eager initialization of collections. Because this initialization load duplicated objects. I changed FetchType from EAGER to LAZY and problem is solved)