I am developing REST application using spring boot and I am trying to optimize the performance of the queries. I am currently using findAll
from the repositories which is causing performance issues. Code is given below:
Person Entity
@Entity
@Table(name = "cd_person")
@Data
@NoArgsConstructor
public class Person {
....
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "password_id")
@Fetch(FetchMode.JOIN)
private Password password;
....
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable(name = "cd_person_role",
joinColumns = @JoinColumn(name = "person_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
@Fetch(FetchMode.JOIN)
private Set<Role> roles = new HashSet<>();
}
Password Entity
@Entity
@Table(name = "cd_password")
@Data
@NoArgsConstructor
public class Password {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Column(name = "password_hash", nullable = false)
private String passwordHash;
.......
}
Role Entity
@Entity
@Table(name = "cd_role")
@Data
@NoArgsConstructor
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "role_type")
@Enumerated(EnumType.STRING)
private RoleType roleType;
....
}
Person Repository
public interface PersonRepository extends CrudRepository<Person, Long> {
Optional<Person> findByEmail(String email);
}
When I do a personRepository.findAll()
there are select queries fired for each row in the person table to fetch the password and roles when I access the person. I know I can use @Query
annotation with JOIN FETCH
in the repository to make it force generate the single query but I was wondering if there was any other way to do so. I am looking for something which we can do at the entity level to reduce queries.
Using spring boot 2.1.5-RELEASE version and related dependencies.
PS. The @Data
and @NoArgsConstructor
are Lombok annotations.
The most minimal code change is to use the ad-hoc EntityGraph feature from spring data . Just override PersonRepository
's findAll()
and use @EntityGraph
to configure the graph. All entities in this graph will be fetched together.
public interface PersonRepository extends CrudRepository<Person, Long> {
@EntityGraph(attributePaths = { "password", "roles" })
public List<Person> findAll();
}
Behind scene it works like JOIN FETCH
. Only single SQL with LEFT JOIN will be generated.
I would leave the Entity as is and override the findAll
method in the repository with an @Query
annotation.
This way, the code refactor is minimal (only one repository change instead of an entity change).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With