Cosium / spring-data-jpa-entity-graph

Spring Data JPA extension allowing full dynamic usage of EntityGraph on repositories
MIT License
466 stars 51 forks source link

Throw when using @Lock and default graph #60

Closed slkmikhail closed 1 year ago

slkmikhail commented 2 years ago

What steps will reproduce the problem?

  1. Create custom jpa repository method and put @Lock
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    Optional<Person> findAndLockById(UUID id)
  2. Add field to Person
    @OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
    private List<Address> addresses
  3. Add default graph to Person (Person.default) with
    ....
    @NamedAttributeNode(address)
  4. Do query by findAndLockById

What is the expected output? Find and lock without entity graph What happens instead?

org.postgresql.util.PSQLException: ERROR: FOR UPDATE cannot be applied to the nullable side of an outer join

Environment:

Is there some way not to use default entity graph for some jpa methods?

reda-alaoui commented 2 years ago

The default entity graph concept introduced a long time ago by this library is not needed anymore (see https://github.com/Cosium/spring-data-jpa-entity-graph/issues/44#issuecomment-642469698 for the rationale). So I would just tell you to stop using the default entity graph feature.

slkmikhail commented 2 years ago

The default entity graph is a good staff to avoid a suggestion which EG should be used for standard jpa methods every time when should be fetched additional data (less code) More flexibale feature would be to use the default entity graph as is, but have capability to:

  1. extend the default entity graph dynamicly by passing additional EG to some method PersonEntityGraph.____(APPEND) .....
  2. disable the default entity graph for some methods by an annotation or another way (in my case)
reda-alaoui commented 2 years ago

The default entity graph is a good staff to avoid a suggestion which EG should be used for standard jpa methods every time when should be fetched additional data (less code)

You can now achieve the same behaviour by only relying on your entity attributes annotations. Here is an example:

public class Product {
  @ManyToOne(fetch = FetchType.LAZY)
  private Brand brand;
  @ManyToOne(fetch = FetchType.EAGER)
  private Manufacturer manufacturer;
}

In this example, the default entity graph is (manufacturer).

slkmikhail commented 2 years ago

As I know, unfortunately, the FetchType.EAGER strategy is also prone to N+1 query issues. That is not a good solution for fetch a lot of data. In additionally, using FetchType.EAGER during

@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<Person> findAndLockById(UUID id)

I get left join, but I don't want to fetch any additional data for that moment. The solution is to declare empty grapth with FETCH type not to use FetchType.EAGER, but, as I know, I can not use empty graph

reda-alaoui commented 1 year ago

You can use default interface methods to pass "default" entity like this:

default Optional<Person> findByName(UUID id) {
  return findByName(id, NamedEntityGraph.loading("foo.default"));
}

Optional<Person> findByName(UUID id, EntityGraph entityGraph);

See also https://github.com/Cosium/spring-data-jpa-entity-graph/issues/73#issue-1330079585