0

I have a Spring boot application, and I'm trying to insert a movie that is related to countries, but after a few minutes an exception is thrown. Simplifying the entities:

Movie:

public class Movie implements Serializable {

    ...

    @ManyToMany(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    })
    @JoinTable(name = "movies_countries",
        joinColumns = @JoinColumn(name = "id_movie"),
        inverseJoinColumns = @JoinColumn(name = "id_country")
    )
    private Set<Country> countries = new HashSet<>();

    public Set<Country> getCountries() {
        return this.countries;
    }

    public void setCountries(Set<Country> countries) {
        this.countries = countries;
    }

    public void addCountry(Country country) {
        getCountries().add(country);
        country.getMovies().add(this); // Point where it gets blocked
    }
    ...
}

Country:

public class Country implements Serializable {

    ...

    @ManyToMany(mappedBy = "countries")
    private Set<Movie> movies = new HashSet<>();

    public Set<Movie> getMovies() {
            return movies;
    }

    public void setMovies(Set<Movie> movies) {
        this.movies = movies;
    }

    ...

}

And the method that relates the movie to the countries:

private void insertCountries(final List<String> countries, final EntityManager entityManager, final Movie movie) {
    for (String strCountry: countries) {
        Country country = this.getCountry(entityManager, strCountry);
        movie.addCountry(country);
    }
}

Exception:

Exception in thread "Thread-6" java.lang.OutOfMemoryError: Java heap space
    at io.netty.buffer.PooledDirectByteBuf$1.newObject(PooledDirectByteBuf.java:35)
    at io.netty.buffer.PooledDirectByteBuf$1.newObject(PooledDirectByteBuf.java:32)
    at io.netty.util.Recycler.get(Recycler.java:158)
    at io.netty.buffer.PooledDirectByteBuf.newInstance(PooledDirectByteBuf.java:40)
    at io.netty.buffer.PoolArena$DirectArena.newByteBuf(PoolArena.java:786)
    at io.netty.buffer.PoolArena.allocate(PoolArena.java:145)
    at io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:332)
    at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:185)
    at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:176)
    at io.netty.channel.nio.AbstractNioChannel.newDirectBuffer(AbstractNioChannel.java:448)
    at io.netty.channel.nio.AbstractNioByteChannel.filterOutboundMessage(AbstractNioByteChannel.java:275)
    at io.netty.channel.AbstractChannel$AbstractUnsafe.write(AbstractChannel.java:881)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.write(DefaultChannelPipeline.java:1365)
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738)
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:730)
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:816)
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:723)
    at io.netty.handler.logging.LoggingHandler.write(LoggingHandler.java:249)
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738)
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:730)
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:816)
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:723)
    at io.netty.channel.ChannelDuplexHandler.write(ChannelDuplexHandler.java:106)
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738)
    at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:730)
    at io.netty.channel.AbstractChannelHandlerContext.access$1700(AbstractChannelHandlerContext.java:38)
    at io.netty.channel.AbstractChannelHandlerContext$AbstractWriteTask.write(AbstractChannelHandlerContext.java:1127)
    at io.netty.channel.AbstractChannelHandlerContext$WriteAndFlushTask.write(AbstractChannelHandlerContext.java:1174)
    at io.netty.channel.AbstractChannelHandlerContext$AbstractWriteTask.run(AbstractChannelHandlerContext.java:1098)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:495)
2020-03-02 12:41:54,741 WARN  [http-nio-8090-exec-2] com.zaxxer.hikari.pool.PoolBase: HikariPool-1 - Failed to validate connection com.mysql.jdbc.JDBC4Connection@35e38de8 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.

To try, I have tried to increase the memory from 1 to 2 Gb, but it still occurs. It doesn't happen in all film inserts.

Any suggestions?

Christine
  • 5,617
  • 4
  • 38
  • 61
Alberto
  • 745
  • 1
  • 6
  • 25

2 Answers2

1

Many to many relationships are tricky, don't use cascade configuration with them, cascade has to do with saving entities, here you don't have an entity, just an association, this association is managed under the cover by the orm monitoring what's going on in the underlying set, if you take a look at the implementation this set is really some kind of bag that is instrumentalized to detect changes in it. The save must be done in the owning side: the side without the mappedBy annotation.

Hans Poo
  • 804
  • 1
  • 8
  • 17
1

I finally got it to work. I have modified the Set by List and now it doesn't stand on line country.getMovies().add(this);.

I add the exact change: private Set<Country> countries = new HashSet<>(); by private List<Country> countries = new ArrayList<>();

Alberto
  • 745
  • 1
  • 6
  • 25
  • Can you explain how this should work? Or did you just do trial and error? – Christine Mar 02 '20 at 15:14
  • 1
    I really don't know why it works with a `List` and not a `Set`. The server doesn't make any exception, only OutOfMemory. It also does not show any data collection queries that could saturate the server. – Alberto Mar 02 '20 at 15:17
  • I had a similar issue with a ManyToMany mapping. There seemed to be an endless recursion while getting the elements. I changed many things including definition of mapping table in JPA to have OneToMany / ManyToOne mappings ... always the same result. Changing from Set to List solved the issue (strange). – Karsten Voigt May 17 '22 at 10:10