79

It's my first question here on stack, so please be gentle :D

I'm trying to create hibernate OneToMany relationship. When I try to fetch some data from my DB, I'm getting StackOverflowError. But when i remove OneToMany part, everything goes normally. This is part of my REST Service, for now it runs on VMware vFabric Server and MySQL DB.

Fetch example:

@Inject
private EntityManager entityManager;
...
entityManager.find(League.class, 1);
...
entityManager.find(Team.class, 1);

MySQL script:

CREATE TABLE league (
    id int(11) NOT NULL AUTO_INCREMENT,
    name varchar(20) COLLATE utf8_unicode_ci NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE team (
    team_id int(11) NOT NULL AUTO_INCREMENT,
    name varchar(20) COLLATE utf8_unicode_ci NOT NULL,
    fk_leagueId int(11) NOT NULL,
    PRIMARY KEY (team_id),
    FOREIGN KEY (fk_leagueId) REFERENCES league(id)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Classes:

@XmlRootElement
@Entity
@Table(name = "team")
@Data
public class Team {
    @Id
    @GeneratedValue(generator = "increment")
    @GenericGenerator(name = "increment", strategy = "increment")
    @Column(name = "team_id")
    private int id;
    @Column(name = "name")
    private String name;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_leagueId", nullable = false)
    private League league;
}

@XmlRootElement
@Entity
@Table(name = "league")
@Data
public class League {
    @Id
    @GeneratedValue(generator = "increment")
    @GenericGenerator(name = "increment", strategy = "increment")
    @Column(name = "id")
    private int id;
    @Column(name = "name")
    private String name;
    //if I comment 2 lines below, there is no error, and everything works fine
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "league")
    private Set<Team> teams;
}

Error:

Hibernate: select league0_.id as id1_1_0_, league0_.name as name2_1_0_ from league league0_ where league0_.id=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Hibernate: select teams0_.fk_leagueId as fk3_1_1_, teams0_.team_id as team1_4_1_, teams0_.team_id as team1_4_0_, teams0_.fk_leagueId as fk3_4_0_, teams0_.name as name2_4_0_ from team teams0_ where teams0_.fk_leagueId=?
Exception in thread "tomcat-http--3" java.lang.StackOverflowError
    at org.jboss.logging.JDKLogger.translate(JDKLogger.java:73)
    at org.jboss.logging.JDKLogger.isEnabled(JDKLogger.java:85)
    at org.jboss.logging.JDKLogger.doLog(JDKLogger.java:41)
    at org.jboss.logging.Logger.debug(Logger.java:406)
    at org.hibernate.internal.CoreMessageLogger_$logger.debug(CoreMessageLogger_$logger.java:525)
    at org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:104)
    at org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:95)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:180)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:159)
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1858)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1835)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1815)
    at org.hibernate.loader.Loader.doQuery(Loader.java:899)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:341)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:311)
    at org.hibernate.loader.Loader.loadCollection(Loader.java:2234)
    at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:65)
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:674)
    at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:83)
    at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1849)
    at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:549)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:234)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124)
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:428)
    at com.lukaszb.motspe.webapp.model.League.hashCode(League.java:21)
    at com.lukaszb.motspe.webapp.model.Team.hashCode(Team.java:20)
    at java.util.HashMap.hash(HashMap.java:351)
    at java.util.HashMap.put(HashMap.java:471)
    at java.util.HashSet.add(HashSet.java:217)
...

Edit:

I was able to solve this out thanks to @Thihara and @KarIP. I have overriden toString() for Team and League like this:

@Override
public String toString() {
    return "League [id=" + id + ", name=" + name + "]";
}

@Override
public String toString() {
    return "Team [id=" + id + ", name=" + name + "]";
}

and was able to fetch data from DB as wanted. But then while parsing I got JAXB error with infinite cycles. So i annotated Team and League class with @XmlAccessorType(XmlAccessType.FIELD), so it won't look at methods, and Team league field as @XmlTransient, so it won't be parsed.

Here I can even remove my toString() implementations, and it still works. I'm not completely sure why. Problem is solved, but I would like to hear more precise explanation. I don't know why JAXB stopped data fetch, even if it's after whole DB communication process(or isn't?). To be more specific, I'm using Jersey for that:

@GET
@Path("search/id")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<League> searchById(@QueryParam("id") int id) {
return Arrays.asList(leagueDAO.getById(id));
}
Montaser Sobaih
  • 305
  • 2
  • 7
otocon
  • 1,029
  • 1
  • 8
  • 15

15 Answers15

142

I had a very similar issue. I was using Lombok's @Data annotation on my model objects to auto-generate getters, setters, and other standard methods. I believe the toString() method generated by Lombok introduced a circular dependency between my Team and League objects. When I tried to get the Set<teams> teams from my League object, I got a java.lang.StackOverflowError because Spring was calling the toString method for logging purposes.

I resolved this by getting rid of Lombok's toString() method. I replaced the @Data annotation with Lombok's @Getter and @Setter annotations. That way I still could benefit from free getters and setters without getting the toString() method.

Fabian
  • 3,310
  • 3
  • 26
  • 35
  • 33
    Thanks, this helped. Alternatively, you can still use the `@Data` annotation, but add the `@ToString` annotation and specify an excluded property like: `@ToString(exclude = { "propName" })` – Wet Noodles Jan 08 '18 at 16:29
  • 1
    We can retain the @ToString annotation and just exclude the fields that lead to recursion. This can be done by placing the Lombok ToString.Exclude annotation before every such field. See: https://projectlombok.org/features/ToString – Alok P Oct 03 '22 at 15:49
  • I'd just like to add that in my case I could see the circular dependency happening when hascode was called. I kept @Data and added @EqualsAndHashCode(of ={"id"}). – Denis Klein Dec 22 '22 at 20:29
38

The only way it can throw an StackOverFlow is when your Team's League is accessed recursively....

Team to League to Team to League

I'm guessing there's some function trying to convert your objects into some other representation reflectively or recursively resulting in an infinite loop.

Thihara
  • 7,031
  • 2
  • 29
  • 56
  • 1
    Thanks, that gave me a solution. – otocon Jul 03 '13 at 12:50
  • 11
    Thanks for the advice! For me it was _hashCode_ in a many-to-many id class – WoutVanAertTheBest Jul 11 '14 at 12:30
  • 6
    Stumbled upon this while facing a similar sort of problem. I pretty much had the exact relationship as mentioned in this question, the problem not only happen with toString() but also (in my case) with hashcode default implementation provided by LomBok. My immediate solution is to exclude the offending field from toString() and hashcode() but also looking into shifting owning the OneToMany relationship to the "One" side which might help overcome the problem, but this one can catch you off guard with no proper end to end tests around your application! – HackerMonkey Nov 10 '16 at 23:26
  • 4
    In my case toString() method was having recursive calling . – Vivek Panday Jan 03 '17 at 08:32
  • People, look at the Stack Trace, obviously it has something to do with `hashCode()`.. @SaganTheBest and @Hass figured it out. In my case, I was using Lombok, and the generated hasCode method didn't cut it, so I customized it to use the ID of the class, that resolved the vicious cycle. – W. Elbashier Nov 27 '18 at 17:36
  • 2
    Oh man. I have been trying to solve this since this morning. Didn't realize Lombok was the culprit. Thanks for saving my day. – Pavan Jadda Dec 10 '18 at 20:06
  • 1
    Got the same error @Jadda removing Data annotation and replace it with Getter + Setter + EqualsAndHashCode + ToString lombok annotations setting exclude to the OneToMany field solve my problem. – Guillaume Maka May 14 '20 at 07:55
35

Circular dependency can originate from Lombok's toString() autogenerated method, if you use @Data complex annotation. To exclude your circular dependecy for a certain field:

@Entity
@Data
public class Team {

  ...

  @ToString.Exclude
  @ManyToOne
  private League league;
}
Zon
  • 18,610
  • 7
  • 91
  • 99
  • 4
    +1 for using `@ToString.Exclude` (and `@EqualsAndHashCode.Exclude`) -- much nicer than the `@...(exclude = {"children"})` variety. – Josh M. Jun 23 '20 at 23:42
23

I had this error because I was parsing a list of objects mapped on both sides @OneToMany and @ManyToOne to json using jackson which caused an infinite loop.

If you are in the same situation you can solve this by using @JsonManagedReference and @JsonBackReference annotations.

Definitions from API :

  • JsonManagedReference (https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonManagedReference.html) :

    Annotation used to indicate that annotated property is part of two-way linkage between fields; and that its role is "parent" (or "forward") link. Value type (class) of property must have a single compatible property annotated with JsonBackReference. Linkage is handled such that the property annotated with this annotation is handled normally (serialized normally, no special handling for deserialization); it is the matching back reference that requires special handling

  • JsonBackReference: (https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonBackReference.html):

    Annotation used to indicate that associated property is part of two-way linkage between fields; and that its role is "child" (or "back") link. Value type of the property must be a bean: it can not be a Collection, Map, Array or enumeration. Linkage is handled such that the property annotated with this annotation is not serialized; and during deserialization, its value is set to instance that has the "managed" (forward) link.

Example:

Owner.java:

@JsonManagedReference
@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
Set<Car> cars;

Car.java:

@JsonBackReference
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "owner_id")
private Owner owner;

Another solution is to use @JsonIgnore which will just set null to the field.

Emeric
  • 6,315
  • 2
  • 41
  • 54
  • What can I do if I still want to have the Owner in the Car object when I fetch ? I need to have the Car in the Owner, but also the Owner in the Car, is this possible without doing some DTO and annoying mapping ? – Spialdor Oct 18 '20 at 11:42
  • 1
    @Jessy, You can use @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") instead which would replace the circular reference with just an id – DanielJ May 20 '21 at 03:16
  • This worked like a charm to solve circular dependency while entity to DTO conversion – Yash Joshi Aug 15 '21 at 15:55
10

For me, Both hashCode and toString default implementation provided by Lombok was causing this issue.

You can use this annotation to exclude members for both equalsAndHasCode with single annotation:

@EqualsAndHashCode(exclude = {"certificates", "payment"})

Also if you want to exclude just members from equals method, Lombok provide : @ToString.Exclude

@ToString.Exclude
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "shipment")
    private Set<Certificate> certificates;
Neeraj Jain
  • 7,643
  • 6
  • 34
  • 62
  • 1
    Great thank you this solved my issue, also we can use `@EqualsAndHashCode.Exclude` on property – Suge Sep 09 '19 at 07:54
  • Give this man a cookie. Thank you Sir. But what is with the Garbage Collection, do we have a problem to delete the objects? – Ivan Demin May 07 '20 at 10:59
9

I replaced the @Data annotation with Lombok's @Getter and @Setter annotations

  • 2
    you should make a full code example of what you changed, a description of why the old code did not work and what your solution does to fix it. – user254694 Jan 28 '20 at 11:43
  • Thank you, this change solved it for me! The Data annotation generates (among other things) toString methods taking all attributes intro account, which means that if you have it on both sides of the bidirectional relationship (say, OneToMany and ManyToOne), the toString method will recursively go from one entity to another, thus ending in StackOverflow error. Replacing it with Getter and Setter avoids the generation of this toString method which, in most cases, might not be necessary. – Cécile Fecherolle Dec 15 '20 at 09:26
3

I had similar issue and in my case nothing of this didnt help.

Helped this one approach:

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "order_id")
@Fetch(value = FetchMode.SUBSELECT)
private List<Product> orderLines; 

name = "order_id" is a foreign key column from Product table.

And i didnt put anything in Product entity.

S.Daineko
  • 1,790
  • 1
  • 20
  • 29
3

I struggled a very long time and thought it's a problem from Kotlin why it throws an Stackoverflow Exception. At the end it was Jackson, which is used by Spring Hibernate and causese the Stackoverflow.

You can solve that by setting @JsonIgnoreProperties on both sides. Example:

public class ProductEntity implements Serializable {

private List<ProductPriceEntity> refProductPriceEntities = new ArrayList<>();

@OneToMany(
        mappedBy = "product",
        fetch = FetchType.LAZY,
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        targetEntity = ProductPriceEntity.class)
  @JsonIgnoreProperties(value = "product", allowSetters = true)
  public List<ProductPriceEntity> getRefProductPriceEntities() {
    return refProductPriceEntities;
  }

  public void setRefProductPriceEntities(List<ProductPriceEntity> refProductPriceEntities) {
    this.refProductPriceEntities = refProductPriceEntities;
  }
}

and finally the other side, which caused the stackoverflow exception.

public class ProductPriceEntity {

private ProductEntity product;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
        @JoinColumn(name = "product_id", referencedColumnName = "id", nullable = false),
        @JoinColumn(name = "product_version_id", referencedColumnName = "version_id", nullable = false)
})
@JsonIgnoreProperties(value = "refProductPriceEntities", allowSetters = true)
public ProductEntity getProduct() {
    return product;
}

public void setProduct(ProductEntity product) {
    this.product = product;
}
}
Emanuel
  • 8,027
  • 2
  • 37
  • 56
2

Got this error in OneToOne mapping in hibernate, the Entity class was unable to convert toString.

Class A(Details.class)-
@OneToOne
@JoinColumn(name="billing_address_id")
private Address billingAddress;

@OneToOne
@JoinColumn(name="shipping_address_id")
private Address shippingAddress;


Class B(Address.class)-
@OneToOne(mappedBy="billingAddress")
private Details billingDetails;

@OneToOne(mappedBy="shippingAddress")
private Details shippingDetails;

Error- because Class A is calling Class B and similarly, class B is calling Class A. Need to remove one of the two. Then this issue will be resolved.

CodingBee
  • 1,011
  • 11
  • 8
1

When i was having this problem i was converting the entity to dto. I then realized i was calling the parent class annotated with onetomany from the bridge class annotated with manytoone. Because the parent already calls the bridge, it became cyclic and the collection recursed. Instead of calling the parent method that populates, i just initialized and populated the parent in the child.

Etch
  • 191
  • 3
  • 9
1

Also see: Hibernate throws StackOverflowError when querying

In my case, I was using the @IdClass annotation on an entity which had a foreign key as part of its primary key:

@IdClass(MyEntity.MyEntityKey.class)
public class MyEntity {
   public static class MyEntityKey {
       private Foo parent;
       private int count;
       // Getters, setters, constructor, ...
   }

   @Id
   private Foo parent;
   @Id
   private int number;
  
   //...
}

Now when loading Foo and eagerly loading all MyEntitys, Hibernate was again loading the parent entity Foo while building the key which resulted in the stack overflow.

I fixed the issue by modelling the id using explicit key properties (i.e. including the keys of Foo in MyEntityKey rather than just a reference to Foo) as shown in Composite key handling, using @Idclass annotation in Spring boot java (also applied to Non-Spring Hibernate). In short: Use the @JoinColumns annotation to specify the relationship between the navigation property (to parent) and the id columns.

Matthias
  • 12,053
  • 4
  • 49
  • 91
1

When I was having this problem, I could solve it with @JsonManagedReference and @JsonBackReference, however it meant that only one side of the entity had the child in it when deserializing into json. I.e. in your team and league example, Only the League would show teams, and the team wouldnt show which league it belonged to.

To allow both side, use @JsonIdentityInfo annotation in your entity and remove the @JsonBackReference and @JsonManagedReference. You entities will be something like:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Team {
...

This replaced the circular refrences with what I was looking for.

DanielJ
  • 729
  • 2
  • 7
  • 26
0

I find that using @EqualsAndHashCode(onlyExplicitlyIncluded = true) is a great place to start. In most cases, equality across objects should only be done on specific values. If you use this for every entity, it will force you to choose what you actually want to include in equality, and is likely to preven the circular memory reference.

Michael Rountree
  • 175
  • 2
  • 12
0

I had a similar problem using Lombok on a save and flush. It was in my @EqualsAndHashCode, so you can exclude child collections by property name:

@Data
@EqualsAndHashCode(callSuper = true, exclude = "childRowCollectionProp")
@Entity
@Table(name = "parent")
public class Parent extends BaseEntityWithId{
Randy
  • 729
  • 5
  • 14
0

I had the same issue in the combination of "Spring Data Rest" and Lomboks @Data annotation. Definitely use both @ToString.Exclude @EqualsAndHashCode.Exclude My setup:

@RepositoryRestResource(collectionResourceRel = "a", path = "a")
public interface ARepository extends JpaRepository<A, Long> {
}

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    @Entity
    @EntityListeners(AuditingEntityListener.class)
    @EqualsAndHashCode(callSuper = true)
    public class A extends AuditMetadata<String> {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
        private String x;
        private String y;

        @ToString.Exclude
        @EqualsAndHashCode.Exclude
        @ManyToOne
        private B b;
    
    }

@RepositoryRestResource(collectionResourceRel = "b", path = "b")
public interface BRepository extends JpaRepository<B, Long> {
}

    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    @Entity
    @EqualsAndHashCode(callSuper = true)
    public class B extends AuditMetadata<String> {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
        private String k;
        private String l;
        private String m;
        @OneToMany(mappedBy = "b", cascade = CascadeType.ALL)
        private Set<A> aList = new HashSet<>();
    }

This worked for me perfectly. Thanks Neeraj Jain.