Spring Data JPA And NamedEntityGraphs

前端 未结 5 554
挽巷
挽巷 2020-11-28 04:44

currently I am wrestling with being able to fetch only the data I need. The findAll() method needs to fetch data dependant on where its getting called. I do not want to end

相关标签:
5条回答
  • 2020-11-28 05:32

    Can you try create EntiyGraph name with child that you will request and give same name to the find all method. Ex:

    @EntityGraph(value = "fetch.Profile.Address.record", type = EntityGraphType.LOAD)
     Employee getProfileAddressRecordById(long id);
    

    For your case:

    @NamedEntityGraph(name="all.Customer.handling_employee.genre", attributeNodes = {
            @NamedAttributeNode("customer"),
            @NamedAttributeNode("handling_employee"),
            @NamedAttributeNode("genre")
    })
    

    method name in repository

    @EntityGraph(value = "all.Customer.handling_employee.genre" , type=EntityGraphType.FETCH)
     findAllCustomerHandlingEmployeeGenre
    

    This way you can keep track of different findAll methods.

    0 讨论(0)
  • 2020-11-28 05:36

    We had the same issue and built a Spring Data JPA extension to solve it :

    https://github.com/Cosium/spring-data-jpa-entity-graph

    This extension allows to pass named or dynamically built EntityGraph as an argument of any repository method.

    With this extension, you would have this method immediatly available:

    List<Complaint> findAll(Sort sort, EntityGraph entityGraph);
    

    And be able to call it with an EntityGraph selected at runtime.

    0 讨论(0)
  • 2020-11-28 05:36

    Use @EntityGraph together with @Query

    @Repository
    public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
    
       @EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
       @Query("SELECT c FROM Complaint ORDER BY ..")
       @Override
       List<Complaint> findAllJoinsButMessages();
    
       @EntityGraph(value = "allJoins" , type=EntityGraphType.FETCH)
       @Query("SELECT c FROM Complaint ORDER BY ..")
       @Override
       List<Complaint> findAllJoin();
    
       ...
    

    }

    0 讨论(0)
  • 2020-11-28 05:41

    We ran into a similar problem and devised several prospective solutions but there doesn't seem to be an elegant solution for what seems to be a common problem.

    1) Prefixes. Data jpa affords several prefixes (find, get, ...) for a method name. One possibility is to use different prefixes with different named graphs. This is the least work but hides the meaning of the method from the developer and has a great deal of potential to cause some non-obvious problems with the wrong entities loading.

    @Repository
    @Transactional
    public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
        @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
        User findByUserID(int id);
    
        @EntityGraph(value = "User.membershipYears", type = EntityGraphType.LOAD)
        User readByUserId(int id);
    }
    

    2) CustomRepository. Another possible solutions is to create custom query methods and inject the EntityManager. This solution gives you the cleanest interface to your repository because you can name your methods something meaningful, but it is a significant amount of complexity to add to your code to provide the solution AND you are manually grabbing the entity manager instead of using Spring magic.

    interface UserRepositoryCustom {
        public User findUserWithMembershipYearsById(int id);
    }
    
    class UserRepositoryImpl implements UserRepositoryCustom {
        @PersistenceContext
        private EntityManager em;
        @Override
        public User findUserWithMembershipYearsById(int id) {
            User result = null;
            List<User> users = em.createQuery("SELECT u FROM users AS u WHERE u.id = :id", User.class)
                    .setParameter("id", id)
                    .setHint("javax.persistence.fetchgraph", em.getEntityGraph("User.membershipYears"))
                    .getResultList();
            if(users.size() >= 0) {
                result = users.get(0);
            }
            return result;
        }
    }
    
    @Repository
    @Transactional
    public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
        @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
        User findByUserID(int id);
    }
    

    3) JPQL. Essentially this is just giving up on named entity graphs and using JPQL to handle your joins for you. Non-ideal in my opinion.

    @Repository
    @Transactional
    public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
        @EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
        User findByUserID(int id);
    
        @Query("SELECT u FROM users WHERE u.id=:id JOIN??????????????????????????")
        User findUserWithTags(@Param("id") final int id);
    }
    

    We went with option 1 because it is the simplest in implementation but this does mean when we use our repositories we have have to look at the fetch methods to make sure we are using the one with the correct entity graph. Good luck.

    Sources:

    • JPA EntityGraph with different views using Spring
    • https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods

    I don't have enough reputation to post all of my sources. Sorry :(

    0 讨论(0)
  • 2020-11-28 05:42

    Using the @EntityGraph annotation on a derived query is possible, as I found out from This article. The article has the example:

    @Repository
    public interface ArticleRepository extends JpaRepository<Article,Long> {
       @EntityGraph(attributePaths = "topics")
       Article findOneWithTopicsById(Long id);
    }
    

    But I don't think there's anything special about "with" and you can actually have anything between find and By. I tried these and they work (this code is Kotlin, but the idea is the same):

    interface UserRepository : PagingAndSortingRepository<UserModel, Long> {
        @EntityGraph(attributePaths = arrayOf("address"))
        fun findAnythingGoesHereById(id: Long): Optional<UserModel>
    
        @EntityGraph(attributePaths = arrayOf("address"))
        fun findAllAnythingGoesHereBy(pageable: Pageable): Page<UserModel>
    }
    
    

    The article had mentioned the caveat that you can't create a method similar to findAll which will query all records without having a By condition and uses findAllWithTopicsByIdNotNull() as an example. I found that just including By by itself at the end of the name was sufficient: findAllWithTopicsBy(). A little more terse but maybe a little more confusing to read. Using method names which end with just By without any condition may be in danger of breaking in future versions in Spring since it doesn't seem like an intended use of derived queries name.

    It looks like the code for parsing derived query names in Spring is here on github. You can look there in case you're curious about what's possible for derived queries repository method names.

    These are the spring docs for derived queries.

    This was tested with spring-data-commons-2.2.3.RELEASE

    0 讨论(0)
提交回复
热议问题