How to save an entity with a nested entity in Hibernate when both have id?

378 Views Asked by At

We are using old Hibernate on our project (version 3.6.10), upgrade is not an option at this moment unfortunately.

I'm trying to save entities with assigned ids that have the following structure:

I have a Base class that contains id field. Please note generator-class="assigned"

public abstract class Base {

    private Long id;

    // contructor

    /**
     * @hibernate.id generator-class="assigned" unsaved-value="null"
     */
    public Long getId() {
        return id;
    }

    // setter
}

Then I have a class Nested

public class Nested extends Base {

    private Main main;

    // constructor

    /**
     * @hibernate.many-to-one column="main_id" not-null="true" fetch="join"
     * lazy="false" cascade="all"
     * class="Main"
     */
    public Main getMain() {
        return main;
    }
    
    // setter
}

And class Main:


public class Main extends Base {

    private List<Nested> nested;

    /**
     * @hibernate.bag cascade="all-delete-orphan" inverse="true" batch-size="10" lazy="false"
     * @hibernate.key column="main_id" on-delete="cascade"
     * @hibernate.one-to-many class="Nested"
     */
    public List<Nested> getNested() {
        return nested;
    }
    // setter
}

When I construct Main object with a list of Nested object (all of them have ids) and try to save it (getHibernateTemplate().save(main)), I see in generated SQL queries:

insert into main...

and then

update nested ... I assume that's because nested records have ids already.

How can I make Hibernate insert records for Nested instances?

I know that I can save each entity separately, but I'd prefer to avoid that.

1

There are 1 best solutions below

3
Pancham Goyal On

If you don't want to save each Nested entity separately, you could use a custom interceptor to modify the Hibernate behaviour for saving the entities.

Below is a sample implementation that could be used for the same

  1. Implement a custom interceptor that extends org.hibernate.EmptyInterceptor
import org.hibernate.EmptyInterceptor;

public class CustomSaveInterceptor extends EmptyInterceptor {

    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        if (entity instanceof Main) {
            Main main = (Main) entity;
            for (Nested nested : main.getNested()) {
                getSession().save(nested); // Manually save each nested entity
            }
        }
        return super.onSave(entity, id, state, propertyNames, types);
    }
}
  1. Configure the custom interceptor in Hibernate configuration
SessionFactory sessionFactory = configuration.buildSessionFactory(
    new CustomSaveInterceptor()); // Pass your interceptor instance here
  1. Use the configured SessionFactory to save your entities
Main main = new Main();
// Set properties for the Main object

Nested nested1 = new Nested();
// Set properties for the Nested object 1

Nested nested2 = new Nested();
// Set properties for the Nested object 2

main.getNested().add(nested1);
main.getNested().add(nested2);

sessionFactory.getCurrentSession().save(main); // Trigger saving with the interceptor

This approach involves intercepting saving process for Main entities using custom interceptor. When a Main entity is saved, the interceptor will manually save each associated Nested entity before proceeding with the normal process for Saving Main entity.

P.S.: This solution can affect the behaviour of the Hibernate session globally. As always, make sure to thoroughly test the change to make sure it works correctly for your specific use case.