2

In my Spring Data application I ran into (N+1) selects issue.

I have a following Spring Data entities:

@Entity
@Table(name = "card_categories")
public class CardCategory extends BaseEntity implements Serializable {

    @Id
    @SequenceGenerator(name = "card_categories_id_seq", sequenceName = "card_categories_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "card_categories_id_seq")
    private Long id;

    private String name;
...

}

@Entity
@Table(name = "levels")
public class Level extends BaseEntity implements Serializable {

    @Id
    @SequenceGenerator(name = "levels_id_seq", sequenceName = "levels_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "levels_id_seq")
    private Long id;

    private String name;
...
}

@Entity
@Table(name = "card_categories_levels")
public class CardCategoryLevel extends BaseEntity implements Serializable {

    @Id
    @SequenceGenerator(name = "card_categories_levels_id_seq", sequenceName = "card_categories_levels_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "card_categories_levels_id_seq")
    private Long id;

    @OneToOne
    @JoinColumn(name = "card_category_id")
    private CardCategory cardCategory;

    @OneToOne
    @JoinColumn(name = "level_id")
    private Level level;
...

}

and empty Spring Data repository:

@Repository
public interface CardCategoryLevelRepository extends JpaRepository<CardCategoryLevel, Long> {
}

When I try to fetch all CardCategoryLevel entities by cardCategoryLevelRepository.findAll() method it produces 3 SELECTs for an each row in my card_categories_levels table.

In order to use one single JOIN instead of N+1 SELECTs I have reimplemented my CardCategoryLevelRepository:

@Repository
public interface CardCategoryLevelRepository extends JpaRepository<CardCategoryLevel, Long> {

    @Query(value = "SELECT ccl FROM CardCategoryLevel ccl LEFT JOIN FETCH ccl.cardCategory cc LEFT JOIN FETCH ccl.level l where cc = :cardCategory and l = :level")
    CardCategoryLevel findByCardCategoryAndLevel(@Param("cardCategory") CardCategory cardCategory, @Param("level") Level level);

    @Override
    @Query(value = "SELECT ccl FROM CardCategoryLevel ccl LEFT JOIN FETCH ccl.cardCategory LEFT JOIN FETCH ccl.level")
    List<CardCategoryLevel> findAll();

}

but I'm not sure I did it in a right optimal way.

Please validate my approach and tell it is optimal workaround for (N+1) SELECTs issue with OneToOne associations in Spring Data or no and what is the best way to solve it.

Should I leave it as is or may be move to some other abstraction.. for example like QueryDSL or something like that ?

alexanoid
  • 24,051
  • 54
  • 210
  • 410
  • The entity graphs provide a good solution for the N+1 problem, just in case you use JPA 2.1: http://www.thoughts-on-java.org/jpa-21-entity-graph-part-1-named-entity/ – Aritz Mar 15 '16 at 20:51
  • @XtremeBiker thanks for this great article. One more question - how does it work with Spring Data .. I mean - do I need to use EntityManager directly or it can be integrated with Spring Data Repositories in a more abstract way ? – alexanoid Mar 15 '16 at 21:08
  • Latest version of Spring Data ship with JPA 2.1 compliant implementations, as [Hibernate 4.3+ or 5.0+](http://stackoverflow.com/questions/14558931/which-version-of-hibernate-support-jpa-2-1). The underlaying implementation takes care about it, not Spring itself. – Aritz Mar 15 '16 at 21:13
  • Thanks, this is definitely the best thing I have ever seen in JPA ! – alexanoid Mar 16 '16 at 08:14
  • Would it be right for you to have it as an answer? That way we could leave the question answered. – Aritz Mar 16 '16 at 08:15
  • Sure, please leave it as an answer and I'll accept it – alexanoid Mar 16 '16 at 08:16

1 Answers1

1

Thanks to Xtreme Biker I have re-implemented my solution with entity graphs and QueryDSL

Community
  • 1
  • 1
alexanoid
  • 24,051
  • 54
  • 210
  • 410
  • Could you please provide a working example of how you interacted with the EntityManager in Spring Data JPA? – Oliver Hernandez Jan 19 '18 at 02:46
  • It turns out Spring Data JPA has annotations for using entity graphs without getting to the underlying EntityManager. I found an example here: https://codingexplained.com/coding/java/spring-framework/fetch-query-not-working-spring-data-jpa-pageable. – Oliver Hernandez Feb 02 '18 at 13:39