1

I have the following table schema, where a simulation has many searches and any search has many properties.

table schema

Since I would like to persist a Simulation entity with its searches and their properties all at once, I mapped my entity like this:

Simulation.java

@Data
@EqualsAndHashCode(of = "id")
@ToString(exclude = "searches")
@Entity
@Table(name = "SIMULATION")
public class Simulation implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "simulation_generator")
    @SequenceGenerator(name = "simulation_generator", sequenceName = "SIMULATION_SEQ", allocationSize = 1)
    private Long id;
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "SIMULATION_ID")
    private Set<SimulationSearch> searches = new HashSet<>(0);

    // other fields
}

SimulationSearch.java

@Data
@EqualsAndHashCode(of = "id")
@ToString(exclude = "properties")
@Entity
@Table(name = "SIM_SEARCH")
public class SimulationSearch implements Serializable {

    @EmbeddedId
    private SimulationSearchId id;
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumns({
            @JoinColumn(name = "SIMULATION_ID", referencedColumnName = "SIMULATION_ID"),
            @JoinColumn(name = "POSITION", referencedColumnName = "POSITION")
    })
    private Set<SimulationSearchProperty> properties = new HashSet<>(0);

    // other fields...

    @Data
    public static class SimulationSearchId implements Serializable {
        @ManyToOne
        @JoinColumn(name = "SIMULATION_ID", insertable = false, updatable = false)
        private Simulation simulation;
        private int position;
    }
}

SimulationSearchProperties.java

@Data
@EqualsAndHashCode(of = "id")
@Entity
@Table(name = "SIM_SEARCH_PROPERTY")
public class SimulationSearchProperty implements Serializable {
    @EmbeddedId
    private SimulationSearchPropertyId id;
    private String value;

    @Data
    public static class SimulationSearchPropertyId implements Serializable {
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumns({
                @JoinColumn(name = "SIMULATION_ID", referencedColumnName = "SIMULATION_ID", insertable = false, updatable = false),
                @JoinColumn(name = "POSITION", referencedColumnName = "POSITION", insertable = false, updatable = false)
        })
        private SimulationSearch search;
        private String label;
    }
}

What happens is that Hibernate keeps printing the following query untill it goes on StackOverflowError.

select simulation0_.*, searches1_.*, properties5_.*
  from simulation simulation0_ 
  left outer join sim_search searches1_ on simulation0_.id = searches1_.simulation_id
  left outer join sim_search_property properties5_ on searches1_.position = properties5_.position and searches1_.simulation_id = properties5_.simulation_id
 where simulation0_.id = ?

While mapping between Simulation and SimulationSearch is very similar to SimulationSearch and SimulationSearchProperty mapping, this error started happening when I set ManyToOne annotation of SimulationSearch#properties as lazy fetch and didn't stop even if I set SimulationSearchPropertyId#search as lazy too.

What am I missing?

UPDATES

I'm using Hibernate 4.2.6.Final

Partial stacktrace log:

java.lang.StackOverflowError
    at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:148)
    at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:104)
    at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:81)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2114)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:82)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:72)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3927)
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:460)
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:429)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:206)
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:262)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:150)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1092)
    at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1019)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:672)
    at org.hibernate.type.EntityType.resolve(EntityType.java:490)
    at org.hibernate.type.ComponentType.resolve(ComponentType.java:667)
    at org.hibernate.type.ComponentType.nullSafeGet(ComponentType.java:349)
    at org.hibernate.type.ManyToOneType.hydrate(ManyToOneType.java:190)
    at org.hibernate.type.ComponentType.hydrate(ComponentType.java:642)
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:775)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:708)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:943)
    at org.hibernate.loader.Loader.doQuery(Loader.java:911)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:342)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:312)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2121)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:82)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:72)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3927)
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:460)
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:429)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:206)
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:262)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:150)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1092)
    at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1019)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:672)
    at org.hibernate.type.EntityType.resolve(EntityType.java:490)
    at org.hibernate.type.ComponentType.resolve(ComponentType.java:667)
    at org.hibernate.type.ComponentType.nullSafeGet(ComponentType.java:349)
    at org.hibernate.type.ManyToOneType.hydrate(ManyToOneType.java:190)
    at org.hibernate.type.ComponentType.hydrate(ComponentType.java:642)
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:775)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:708)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:943)
    at org.hibernate.loader.Loader.doQuery(Loader.java:911)
    ...

I've just updated a bit the entity mapping, removing mappedBy annotation attribute and adding instead @JoinColumns annotations. Now persistence is working fine, but the StackOverflowError is still there when I try to load a single simulation.

I've also cleaned up Hibernate generated sql removing uninteresting pieces of information.

noiaverbale
  • 1,550
  • 13
  • 27
  • 2
    Please also include the stacktrace of the stackoverflow exception. – samabcde Jun 29 '20 at 14:07
  • What hibernate version do you use? – SternK Jun 29 '20 at 14:08
  • I've updated my question, btw the exception has just internal calls, and I'm using Hibernate 4.2.6 – noiaverbale Jun 29 '20 at 14:26
  • 1
    It looks like your annotations are not correct... Hibernate does not recognize that you have a bidirectional relationship and treats your relations both as unidirectional. I'll try to form a proper answer later... – Uwe Plonus Jun 29 '20 at 14:30
  • It doesn't seems to be an hash or toString recursion causing the stackoverflow error: I've tried removing `@Data` in favor of `@Getter` and `@Setter` in `SimulationSearchPropertyId` but nothing changed. – noiaverbale Jun 30 '20 at 14:56

1 Answers1

0

You forgot to annotate you relationship to be bidirectional.

For your first class the change should be

@Data
@EqualsAndHashCode(of = "id")
@ToString(exclude = "searches")
@Entity
@Table(name = "SIMULATION")
public class Simulation implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "simulation_generator")
    @SequenceGenerator(name = "simulation_generator", sequenceName = "SIMULATION_SEQ", allocationSize = 1)
    private Long id;
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "SimulationSearchId")
    private Set<SimulationSearch> searches = new HashSet<>(0);

    // other fields
}

Without the attribute mappedBy a JPA provider assumes that you have 2 unidirectional relationship, which leads to an infinite circle reference between your data.

Reference

JSR 338: Java TM Persistence API, Version 2.1

11.1.40 OneToMany Annotation

4th paragraph, 2nd sentence

If the relationship is bidirectional, the mappedBy element must be used to specify the relationship field or property of the entity that is the owner of the relationship.

Uwe Plonus
  • 9,803
  • 4
  • 41
  • 48
  • I'v added the `mappedBy` attribute on both `OneToMany` annotations, but I get an Hibernate exception: `org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn: it.netechgroup.fastcheck.simulation.model.SimulationModel.searches` – noiaverbale Jul 01 '20 at 06:19
  • Then, if I remove the `JoinColumn` annotations, I'm back to the original question state and the StackoverflowError is still there. – noiaverbale Jul 01 '20 at 06:31
  • The `@JoinColumn` annotations at the `@ManyToOne` should/could stay. Only at the `@OneToMany` end they are not needed. – Uwe Plonus Jul 01 '20 at 06:57
  • I only removed the `@JoinColumn` on the `@OneToMany`, but apparently nothing changed. – noiaverbale Jul 01 '20 at 07:13