3

Have an entity class (Song) with a @OneToMany mapping to another entity (CoverArt), and cascade set to ALL because it seemed easier to just have to save the main entiy and let it take care of persisting the cover art

@Audited
@Entity
public class Song
{
    @Id
    @GeneratedValue
    private Integer recNo;

    @Version
    private int version;

    @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
    private List<CoverArt> coverArts;

    ....

}

But I found at a later point in the code if I just retrieved and instance of the class from the database and then within session modified just one field in the Song entity that would cause it to update all the cover art entities linked to that song even though nothing had changed for the cover art, why is it doing this ?

Also, I don't think it causes the problem but I am using Envers and the (seemingly) needless extra updates to the CoverArt table have the knock effect of causing Envers to create unneccessary Audit Tables as well.

If I remove the CascadeType annotation modifying the one field does not cause the cover art entities to be updated and everything works okay as long as I add the extra logic for when I do add cover art but I was hoping I didn't need to do this.

skaffman
  • 398,947
  • 96
  • 818
  • 769
Paul Taylor
  • 13,411
  • 42
  • 184
  • 351
  • do you somehow change the instance of coverArts collection? It could be that hibernate loses changetracking. Also if CoverArt has a backreference to Song it's better to have inverse = true on the @OneToMany – Firo Feb 29 '12 at 13:02
  • What does that mean, I dont make any changes to coverart. It does not have a backreference, – Paul Taylor Feb 29 '12 at 14:28
  • Hibernate somehow thinks that the CoverArts have changed. Either the reference to the parent if the collection Hibernate sets to the field is exchanged or if the values of all properties are not equal because they are changed somehow. set dynamicupdate=true to CoverArt to see which fields are actually changing. – Firo Feb 29 '12 at 15:26
  • Hi, I seemed to have fixed the problem I was using he anti-pattern of creating a new session and then closing it whenever I retrieved anything from the db rather than passing the method an existing session and only closing the session once Ive finished with the object, fixing this has fixed the problem. – Paul Taylor Mar 07 '12 at 17:32
  • I am looking to do the actual opposite, I would want (in your case) the `Song` entity to be updated with a new revision once any `CovertArt` is modified (or even a children of `CovertArt` is updated). Do you know if there is any way to do that natively with Envers without having to code extra logic? – dominicbri7 Jan 30 '17 at 10:46

2 Answers2

1

I have the exact issue with my own application. I have 3 one to many with cascade = {CascadeType.ALL}

Can someone give me an example that work with proper session reuse. My code :

public class HibernateUtil {

private static final SessionFactory sessionFactory = buildSessionFactory();

private static SessionFactory buildSessionFactory() {
    try {
        // Create the SessionFactory from hibernate.cfg.xml
        Configuration conf = new Configuration().configure();
        ServiceRegistry sr = new StandardServiceRegistryBuilder().applySettings(conf.getProperties()).build();
        SessionFactory sf = conf.buildSessionFactory(sr);
        return sf;   
    }
    catch (HibernateException ex) {
        // Make sure you log the exception, as it might be swallowed
        System.err.println("Initial SessionFactory creation failed." + ex);
        throw new ExceptionInInitializerError(ex);
    }
}

public static SessionFactory getSessionFactory() {
    return sessionFactory;
}

}


public groupelti.lims.persistence.vo.LotEchantillonRegulier modifier(groupelti.lims.persistence.vo.LotEchantillonRegulier ler) throws DAOException {

    // validation
    if (ler == null) {
        throw new IllegalArgumentException();
    }
    if (ler.getId() == null) {
        throw new IllegalArgumentException();
    }
    if (ler.getId() <= 0) {
        throw new IllegalArgumentException();
    }

    // traitement
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();

    try {
        session.update(ler);
        session.getTransaction().commit();
    } catch (PropertyValueException e) {
        logger.info("" + e.getMessage());

        session.getTransaction().rollback();
        throw new DAOException("Voir log.", e);
    } catch (ConstraintViolationException e) {
        logger.info("" + e.getMessage());

        session.getTransaction().rollback();
        throw new DAOException("Voir log.", e);
    } catch (GenericJDBCException e) {
        logger.info("" + e.getMessage());

        session.getTransaction().rollback();
        throw new DAOException("Voir log.", e);
    } catch (TransientObjectException e) {
        logger.info("" + e.getMessage());

        session.getTransaction().rollback();
        throw new DAOException("Voir log.", e);
    } finally {
        try {
            session.close();
        } catch (SessionException e) {
            //do nothing
        }
    }

    return ler;
}

contact genest@gmail.com Regards, Mathieu

1

I seemed to have fixed the problem I was using the anti-pattern of creating a new session and then closing it whenever I retrieved anything from the DB rather than passing the method an existing session and only closing the session once I've finished with the object, fixing this has fixed the problem.

Piotr Nowicki
  • 17,914
  • 8
  • 63
  • 82
Paul Taylor
  • 13,411
  • 42
  • 184
  • 351