How Does the Fetchmode Work in Spring Data JPA

How does the FetchMode work in Spring Data JPA

I think that Spring Data ignores the FetchMode. I always use the @NamedEntityGraph and @EntityGraph annotations when working with Spring Data

@Entity
@NamedEntityGraph(name = "GroupInfo.detail",
attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {

// default fetch mode is lazy.
@ManyToMany
List<GroupMember> members = new ArrayList<GroupMember>();


}

@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {

@EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
GroupInfo getByGroupName(String name);

}

Check the documentation here

Java Spring JPA FetchMode.JOIN not using JOIN

You seem to run into a misconception here: lazy/eager definitions on an object are only considered when loading an object via it's primary key (i.e. via EntityManager.find(id, type) or FeedAttributeRepository.findOne(id). Hibernate disables the global fetch plan for query executions.

For query methods you explicitly have to specify fetch joins in the query definition (which will require a manually declared query) or define an fetch graph to be used for the query method using @EntityGraph (see the reference documentation for details).

EAGER loading with one select doesn't work in Spring Data JPA

JpaReposistory

The easiest option would be to write a JpaRepository<T, Id>. This is still a custom repository. However, you do not have to write so much code. You mainly have to write a repository interface for each relevant class and annotate the findById(Long id) method with a graph. The advantage is that if you edit your entity, the repository method will not need any changes because you define the entity graph within the entity class itself.

@Entity
@NamedEntityGraph(name = "Department.detail",
attributeNodes = @NamedAttributeNode("employees"))
public class Department {

@Id
@GeneratedValue
private Long id;

private String name;

@OneToMany(fetch = FetchType.LAZY)
private List<Employee> employees;

// ...
}

public interface DepartmentRepository extends JpaRepository<Department, Long> {

@EntityGraph(value = "Department.detail", type = EntityGraphType.LOAD)
List<Department> findById(Long id);
}

As Spring data ignores the @Fetch(Fetchmode.JOIN) annotation or the information fetch = FetchType.EAGER, you cannot influence the join how you want it to be within the entity itself.

JPQL Query Where You Need It

Another option can be considered as a bad software engineering style: You can call the database queries directly where you need them. This means that you execute the code which you would usually write in the repository.

public ClassWithQueryResults {

@PersistenceContext
private EntityManager entityManager;

public void methodWhereYouNeedYourResults() {

TypedQuery<Department> query = entityManager.createQuery(
"SELECT DISTINCT d FROM Department d LEFT JOIN d.employees e",
Department.class);
List<Department> departments = query.getResultList();

// ...
}
}

Repository With JPQL, Generics and Reflection

Taking the previously suggested idea, you can create a custom repository which is valid for all your entities. The first step would be to create an attribute in your entity class in which you store the attribute which should be fetched.

public class Department extends AbstractEntity {

public static void String ATTRIBUTE_TO_FETCH = "employees";

...
}

With some tweaking, this can be extended to an array/list of all the fields which should be fetched. As this attribute is directly in your entity classes, the chance for any mistakes and future effort is low. Obviously, this attribute should have the same name in all your entities.

The next step would be to create the repository. I provide an example with the findAll() method. You have to pass it only the class name of the entities you want to have and the generics and reflection do the rest. (Consider what you want to do with the exceptions.)

public <T> List<T> findAll(Class<T> tClass) 
throws NoSuchFieldException, IllegalAccessException {

String className = tClass.getSimpleName();

String attributeToFetch = (String)
tClass.getDeclaredField("ATTRIBUTE_TO_FETCH").get(null);

String queryString = String.format("SELECT DISTINCT p FROM %s p LEFT JOIN p.%s c",
className, attributeToFetch);

TypedQuery<T> query = entityManager.createQuery(queryString, tClass);

return query.getResultList();
}

Depending on how you want to implement this, the modification/generation of a query through simple manipulation of a String can offer the possibility of SQL injection attacks.



Related Topics



Leave a reply



Submit