0

I load and entity A qui a one to many on a list of entities B. This is passed to m UI where in can add / remove / modify the list of entities B. Then I post back those entities modifications are not set in database after the transation. I would like to understand why and what I must do to achieve this (The only back solution I found is to remove all data and recreate ; I do not consider this as a reel solution).

Here are my entities :

Entity A

@Entity
@Table(name = "GL_RENTAL")
public class GlRental implements Serializable {
    private static final long serialVersionUID = 3721662952504926000L;

    @Id
    @Column(name = "PK_RENTAL")
    @GeneratedValue(generator = "TableGenerator")
    @GenericGenerator(name = "TableGenerator", strategy = "com......TableGenerator", parameters = { @Parameter(name = "segment_value", value = "GL_RENTAL") })
    private Integer pk;

    @Version
    @Column(name = "DATE_VERSION")
    private Date version;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "PK_UT")
    QuantificationRD quantification;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "PK_RENTAL", nullable = false)
    @OrderColumn(name = "INDEX_ORDER")
    private List<GlRentalPeriod> periods = new ArrayList<>();
...

Entity B

@Entity
@Table(name = "GL_RENTAL_PERIOD")
public class GlRentalPeriod implements Serializable {
    private static final long serialVersionUID = -8190230568391952427L;

    @Id
    @Column(name = "PK_RENTAL_PERIOD")
    @GeneratedValue(generator = "TableGenerator")
    @GenericGenerator(name = "TableGenerator", strategy = "com......TableGenerator", parameters = { @Parameter(name = "segment_value", value = "GL_RENTAL_PERIOD") })
    private Integer pk;

    @Version
    @Column(name = "DATE_VERSION")
    private Date version;

    @Column(name = "TYPE")
    private String type;

    @Column(name = "AMOUNT")
    private Double amount;

    @Column(name = "DATE_START")
    private Date dateStart;

    @Column(name = "DATE_END")
    private Date dateEnd;

    @Column(name = "FRANCHISE")
    private Double franchise;

    @Column(name = "FRANCHISE_TYPE")
    private String franchiseType;

    @Column(name = "FRANCHISE_UNIT")
    private String franchiseUnit;

    @Column(name = "PAID")
    private Boolean paid;
....

Here is the service

@Service
@Transactional
public class GlUtServiceImpl implements GlUtService {

    @Autowired
    ProduitService produitService;

    @Autowired
    ContractService contractService;

    @Autowired
    UtDao utDao;

    @Autowired
    GlUtDao glUtDao;

    ...

    @Override
    public void updateRental(ContexteService contexte, Integer pkUT, GlRentalBo rentalBo) {
        UniteTraitement uniteTraitement = utDao.rechercherUT(pkUT);
        QualificationUT qualif = uniteTraitement.getQualification();
        Declaration declaration = qualif.getDeclaration();
        Gestionnaire gestionnaire = gestionnaireDao.rechercherGestionnaire(contexte.getIdentifiantUtilisateur());
        verificationHabilitationService.verifierDroitTraitement(gestionnaire, uniteTraitement);
        verificationHabilitationService.verifierOuvertureUT(uniteTraitement);

        GlRental rentalDto = rentalBo.getRental();
        if (rentalDto.getPk() == null) {
            // Attach qualification with rental
            UtDao utDao = (UtDao) glUtDao;
            QuantificationRD quantif = utDao.rechercherQuantificationRD(pkUT);
            if (quantif == null) {
                throw new ExceptionPkEntiteInconnu(UT.class, pkUT);
            }
            if (rentalDto.getQuantification() != null) {
                throw new ExceptionConcurrenceOptimiste("Des informations de loyer existent déjà");
            }
            rentalDto.setQuantification(quantif);

            // Save
            glUtDao.createRental(rentalDto);
        }
        else {

            GlRental rental = glUtDao.getRental(pkUt)); // error is here, this is readonly request
            List<GlRentalPeriod> rentalPeriodRemovals = new ArrayList<>();
            for (GlRentalPeriod rentalPeriod : rental.getPeriods()) {
                GlRentalPeriod rentalPeriodToRemove = rentalPeriod;
                for (GlRentalPeriod rentalPeriodDto : rentalDto.getPeriods()) {
                    // Period already exists in database : modify date
                    if (rentalPeriodDto.getPk() != null && Integer.compare(rentalPeriodDto.getPk(), rentalPeriod.getPk()) == 0) {
                        rentalPeriodToRemove = null;
                        rentalPeriod.setAmount(rentalPeriodDto.getAmount());
                        rentalPeriod.setDateStart(rentalPeriodDto.getDateStart());
                        rentalPeriod.setDateEnd(rentalPeriodDto.getDateEnd());
                        rentalPeriod.setFranchise(rentalPeriodDto.getFranchise());
                        rentalPeriod.setFranchiseType(rentalPeriodDto.getFranchiseType());
                        rentalPeriod.setFranchiseUnit(rentalPeriodDto.getFranchiseUnit());
                        rentalPeriod.setPaid(rentalPeriodDto.getPaid());
                        rentalPeriod.setType(rentalPeriodDto.getType());
                        break;
                    }
                }
                // Period not found, to remove, set it in a list of periods to remove from database
                if (rentalPeriodToRemove != null) {
                    rentalPeriodRemovals.add(rentalPeriod);
                }
            }

            if (CollectionUtils.isNotEmpty(rentalPeriodRemovals)) {
                rental.getPeriods().removeAll(rentalPeriodRemovals);
            }

            // Additionnal periods
            List<GlRentalPeriod> rentalPeriodAdditionnals = rentalDto.getPeriods().stream().filter(period -> period.getPk() == null)
                .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(rentalPeriodAdditionnals)) {
                rental.getPeriods().addAll(rentalPeriodAdditionnals);
            }

            glUtDao.updateRental(rental);
        }
    }
}
Anthony
  • 191
  • 4
  • 13
  • Without seeing the code that actually persists the entities, presumably using an EntityManager, we can't tell what you're doing wrong. – DavidS Jan 27 '17 at 22:50
  • Possible duplicate of [JPA - Persisting a One to Many relationship](http://stackoverflow.com/questions/1795649/jpa-persisting-a-one-to-many-relationship) – DavidS Jan 27 '17 at 22:51
  • Ive updated with the code of the service. And thanks to you question I found the issue. It was so idiot I did no see it. In the UPDATE case, when PK of my object Rental is not null, I load this object in order to modify the list of data in. But I forgot the method called return READONLY data. So I fixed it with another method retrieving data directly witth PK of my object – Anthony Jan 27 '17 at 23:57

1 Answers1

0

Explaining my pb helped me to solve is. Pb was simple. In my service I call

GlRental rental = glUtDao.getRental(pkUt));

And this is a readonly method

@Override
    public GlRental getRental(Integer pkUt) {
        Session session = sf.getCurrentSession();
        Query query = session.createQuery("select rental from com.galian.claims.core.model.GlRental rental where rental.quantification.pk = :pkUt");
        query.setInteger("pkUt", pkUt);
        // to avoid saving any modification when transaction is closed
        //The Caller can perform modification but they must no be saved
        query.setReadOnly(true);
        GlRental rental = (GlRental) query.uniqueResult();
        return rental;
    }

(So bad I am) If data is retrieved as readonly, any modification on entity is not set. So I just do this and use this method

public GlRental getRentalByPk(Integer pkRental) {
        return (GlRental) sf.getCurrentSession().get(GlRental.class, pkRental);
    }

So now I just wonder if it's better to post DTO data, load entity, remove, update, modify its data, or post directly entity and "merge" it without having to pass and copy dto in the persistent entity.

Anthony
  • 191
  • 4
  • 13