Announcement Announcement Module
Collapse
No announcement yet.
CrudRepository and FindOne Issue Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • CrudRepository and FindOne Issue

    Hi All,

    I have a very strange problem. Strange to me at least. I have a repository that I've added a findOne to.

    The Snapshot domain has 4 collections and some other properties, 1 set and 3 lists. I can call findAll() with no issues, but when I call findOne(Long id) the thread eats up all the heap memory and throws the Java heap exception. If I remove one of the collections, the set or one of the lists I do not get the error anymore and it can be anyone of the collections I remove. Like I said I can call findAll() then get the last item in the list and display those results with no issue. It's only when I actually try to call findOne(Long id) by ID it goes absolutely nuts consuming memory. I'm calling the methods from a controller. Very strange because findAll() uses very little memory. I've profiled and observed using VisualVM. Any ideas? This has been a frustrating one. Could there be a serious bug in the CrudRepository?

    Code and stack trace posted below.

    Code:
    
    import com.cortive.dispatchboard.domain.Snapshot;
    
    import org.springframework.data.repository.CrudRepository;
    
    public interface SnapshotRepository extends CrudRepository<Snapshot, Long> { 
    	List<Snapshot> findAll();
    	
    	Snapshot findOne(Long id);
    }
    Code:
    Caused by: java.lang.OutOfMemoryError: Java heap space
    	at com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:2123)
    	at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1900)
    	at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3401)
    	at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:483)
    	at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3096)
    	at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2266)
    	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2687)
    	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2719)
    	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
    	at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2318)
    	at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:56)
    	at org.hibernate.loader.Loader.getResultSet(Loader.java:2031)
    	at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1832)
    	at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1811)
    	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.loadEntity(Loader.java:2111)
    	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:3917)
    	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:1091)
    	at org.hibernate.internal.SessionImpl.access$2000(SessionImpl.java:174)
    	at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2473)
    	at org.hibernate.internal.SessionImpl.get(SessionImpl.java:987)
    	at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:807)
    	at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:781)

  • #2
    To do some basic exploring of the situation, I would set Hibernate's show_sql property to true. That would give you an idea of what SQL is being generated with and without the set or list. Is the generated native SQL what you would expect in the with and without set (or list) cases?

    I would also add a find method to the repository class which is annotated with @Query. The query would be native SQL and would select for records matching the ID. (Execute that query in MySQL's query tool to first see that you have the right query syntax.)

    Comment


    • #3
      Those where great suggestions. I did have showSql turned on for Spring JPA. I investigated that further and found that the query was ridiculously long. I added my own @Query to the repository and boom, works. Hibernate was trying to do inner joins and outer joins all over the place. Is this a bug or a case of hibernate trying to do what it thinks you are asking for? I lean a bit more towards bug, as it should probably do less rather than more when fetching from a DB by findOne.

      Great suggestion, I should of thought of that. But sometimes you just need an outside view. Thanks pfurbacher, Gold star for you.

      Comment


      • #4
        I'm wondering ... did you declare that the collections in your class should be loaded eagerly, either in a Hibernate config file or with annotations? That might explain the joins in the "findOne()" query, but maybe not since you mentioned earlier that if you do a "findAll()", you don't run out of memory.

        If you are loading the collections lazily, it is odd that the "findOne()" query should be so complex. (I have to try this in one of my repositories just for grins.)

        Perhaps you could paste in some elided (snipped) code for the Entity you were talking about. You can change the names of everything to protect the innocent, trade secrets, etc., such that the properties would be "Set<Blah> blahs" and so on.

        Finally, I'm not sure it is Hibernate which generates the query. My understanding (which could be wrong) according to a comment made by Oliver Gierke in some other thread or forum/blog discussion, is that Spring Data JPA generates the Query. That might then be translated by Hibernate (or whatever the JPA provider is) to SQL. (Again, I could be completely wrong in this description, and I hope Oliver or someone close to the codebase jumps in to clarify.)

        Comment


        • #5
          Yes they are loaded eagerly, so that probably had something to do with it. My understanding is that JPA is creating the Query, which would really make sense since I am using the CrudRepository as well.

          Here is an example of the Entity:

          Code:
          import java.util.Date;
          import java.util.List;
          import java.util.Set;
          
          import javax.persistence.Entity;
          import javax.persistence.FetchType;
          import javax.persistence.GeneratedValue;
          import javax.persistence.GenerationType;
          import javax.persistence.Id;
          import javax.persistence.OneToMany;
          import javax.persistence.OrderBy;
          import javax.persistence.Table;
          import javax.persistence.Temporal;
          import javax.persistence.TemporalType;
          
          import org.hibernate.annotations.IndexColumn;
          
          
          @Entity
          @Table(name="SomeEntity")
          public class SomeEntity {
          
          	@Id
          	@GeneratedValue(strategy = GenerationType.AUTO)
          	private Long id;
          
          	@Temporal(TemporalType.DATE)
          	private Date dateCreated;
          	
          	@Temporal(TemporalType.DATE)
          	private Date dateModified;
          	
          	@OneToMany(fetch = FetchType.EAGER, mappedBy = "mappingPojo")
          	@OrderBy("someRow")
          	private Set<List1> List1;
          	
          	@OneToMany(fetch = FetchType.EAGER, mappedBy = "mappingPojo")
          	@OrderBy("number")
          	private Set<List2> List2;
          	
          	@OneToMany(fetch = FetchType.EAGER, mappedBy = "mappingPojo")
          	@OrderBy("number")
          	private Set<List3> List3;
          	
          	
          	@OneToMany(fetch = FetchType.EAGER, mappedBy = "mappingPojo")
          	@IndexColumn(name="another_id")
          	private List<List4> List4;
          
          	
          }

          Comment


          • #6
            Originally posted by MichaelJS View Post
            ... My understanding is that JPA is creating the Query, which would really make sense since I am using the CrudRepository as well.
            ...
            I was able to track down the quote I mentioned earlier, from Oliver Gierke on what Spring Data JPA repositories do behind the scene:

            ... but were not generating any code at all. Were simply building a query meta model for the repository interfaces and create and execute the according queries from that on method invocation. For CRUD methods its even only delegating to an appropriate implementation class. So it boils down to a bit of proxy magic and intelligent delegation.
            in a reply to a blog article by Willie Wheeler, Dynamic DAOs and queries using Spring Data JPA. Reading it again, I'm not sure whether he was saying that the framework actually creates SQL queries, or just generates the meta model and passes that to the vendor (i.e., "appropriate implementation class"). As evidence of the latter, there's a bug in the EclipseLink implementation of "IN" clauses which adds an extra set of parens around the parameter list; this suggests that it's the vendor implementation which is taking the meta model and creating the SQL. ... But I could be wrong.


            Back to your entity and the eager loads: I'm now wondering whether the simplified @Query for your custom "findOne()" replacement is going to eager fetch. You'll probably find out soon enough if it doesn't.

            Comment


            • #7
              The simplified query I created does fetch eagerly and looks much closer to the findAll() query than the findOne(ID id) request with all the joins. For anyone that runs into this problem in the future creating a query like this in your repository will get you what you need:

              Code:
              	/* Specify a Query rather than use findOne due to excessive joins */
              	@Query("FROM YourEntity WHERE id=?1")
              	YourEntity getOneRequest(Long id);

              Comment


              • #8
                The shortest answer is doing. All things are difficult before they are easy.

                __________________
                Living without an aim is like sailing without a compass.
                Diablo 3 Gold|Buy MapleStory Mesos|cheap diablo 3 Gold

                Comment

                Working...
                X