Announcement Announcement Module
Collapse
No announcement yet.
Spring Data JPA, custom methods and inheritance Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring Data JPA, custom methods and inheritance

    I wanted to have some a DAO (MyDAO) extending a base DAO interface (BaseDAO). When using Spring Data JPA (1.0.0M2 or the last nightly build) to implement the DAO, I get the following error:

    Code:
    You have custom methods in interface test.MyDAO but not provided a custom implementation!
    I can workaround this by overriding the method in MyDAO but this is not really nice as it'll require to redefine the methods from BaseDAO in all actual DAOs.

    Is there anyway to force SDJ not to consider my base methods as regular methods, not custom ones?
    Last edited by gabuzo; May 12th, 2011, 03:55 AM.

  • #2
    You can extend your SDJ DAO with some method.
    Read the "1.4. Custom implementations" section of the reference documentation http://static.springsource.org/sprin...eference/html/

    If you have a problem, paste exemple code so that we can help more efficiently.

    Comment


    • #3
      My issue, with code crumbs ...

      I read the section on custom implementation and my issue is that this customization detection is intrusive.

      Entity:

      Code:
      @Entity
      @NamedQueries({ @NamedQuery(name = "MyEntity.findAllIds", query = "select id from MyEntity") })
      public class MyEntity {
          
          @Id
          @GeneratedValue(strategy = GenerationType.AUTO)
          private Long id;
          
          public Long getId() {
              return id;
          }
      }
      The DAO (first version):

      Code:
      public interface MyDAO
          extends JpaRepository<MyEntity, Long> {
          Set<Long> findAllIds();
      }
      In this configuration everything is working fine (at least from the nightly build) thanks to the named query in the entity.

      Now I want to make MyDAO a child of the following interface:
      Code:
      @NoRepositoryBean
      public interface BaseDAO {
          Set<Long> findAllIds();
      }
      So I change MyDAO to the following form:
      Code:
      public interface MyDAO
          extends BaseDAO, JpaRepository<MyEntity, Long> {
      }
      I commented removed the findAllIds method as it is already defined in BaseDAO.

      In this case I get the following error at runtime:
      Code:
      Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myDAO': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: You have custom methods in interface test.MyDAO but not provided a custom implementation!
          [snip]
      Caused by: java.lang.IllegalArgumentException: You have custom methods in interface test.MyDAO but not provided a custom implementation!
      	at org.springframework.data.repository.support.RepositoryFactorySupport.validate(RepositoryFactorySupport.java:226)
      	at org.springframework.data.repository.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:130)
      	at org.springframework.data.repository.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:107)
      	at org.springframework.data.repository.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:36)
      	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
      	... 9 more
      Of course I can workaround this by keeping the findAllIds declaration in both BaseDAO and MyDAO but this is definitely redundant and error prone.

      Comment


      • #4
        Have you defined a class 'MyDaoImpl that implements BaseDAO' in the same package as MyDao ?
        Did you scan/add this bean into spring configuration?

        Comment


        • #5
          Originally posted by gwa View Post
          Have you defined a class 'MyDaoImpl that implements BaseDAO' in the same package as MyDao ?
          Did you scan/add this bean into spring configuration?
          No I didn't but that's my point, the standard implementation fits me so I don't want to be forced to define a custom implementation.

          Comment


          • #6
            So you want to declare a method in BaseDAO which not extends JpaRepository and that this method will be generated automatically by spring data jpa...

            Comment


            • #7
              Originally posted by gwa View Post
              So you want to declare a method in BaseDAO which not extends JpaRepository and that this method will be generated automatically by spring data jpa...
              It's OK for me to have BaseDAO extend JpaRepository (probably better actually) but it does not change anything. I get the same error after changing BaseDAO to:

              Code:
              @NoRepositoryBean
              public interface BaseDAO
                  extends JpaRepository<MyEntity, Long> {
                  Set<Long> findAllIds();
              }

              Comment


              • #8
                Adding functionality to all repository proxies requires some more effort. It's documented in detail here: http://static.springsource.org/sprin...l-repositories

                Comment


                • #9
                  Originally posted by Oliver Gierke View Post
                  Adding functionality to all repository proxies requires some more effort. It's documented in detail here: http://static.springsource.org/sprin...l-repositories
                  Yup but I don't want to create any custom implementation. The default implementation through the use of named queries fits me really fine. I just want to find a way to make Spring-Data-JPA create the normal implementation even if the method is defined only in the parent class.

                  Comment


                  • #10
                    I am with gabuzo. I understand the steps required to implement a common method by creating an interface with a common method, then implementation and finally configuring the repository factory to create an instance of the the custom implementation containing the common method. The question that remains is what if the custom method is a finder that is normally automatically implemented for me?

                    In other words, instead of having T findByXX<String s) in every one of my repository interfaces, I want to move this finder to the base interface, but still have the standard repository behavior implementing the findByXX for me automagically.

                    I want to go from this:
                    Code:
                    @Repository("brandRepository")
                    @Transactional
                    public interface BrandRepository extends JpaRepository<Brand, Long> {
                      @Transactional
                      public Brand findByCode(String code);
                    }
                    to this:
                    Code:
                    @Repository("brandRepository")
                    @Transactional
                    public interface BrandRepository extends BaseRepository<Brand> {
                    }
                    where BaseRepository looks like this:

                    Code:
                    @Transactional
                    public interface BaseRepository<T extends BaseModel> extends JpaRepository<T, Long> {
                      @Transactional
                      public T findByCode(String code);
                    }
                    Can I still somehow use the automatic finder generation to implement findByCode, or do I have to write my own QUERY implementation?

                    Thanks.
                    -AP_

                    Comment


                    • #11
                      You'll have to declare the finder in each class to get the automatic generation, else you need to write your own query implementation.

                      Comment


                      • #12
                        I basically just need a method in the base class so that I can create a BaseRepository<T> to work with my BaseService<T>. I will make my own implementation, however, it's curious that even when I declared T findByCode(String) in my BaseRepository and then extended that and included MyClass findByCode(String), I got the same message exception.

                        Perhaps that could be an enhancement for future releases.

                        -AP_

                        Comment


                        • #13
                          Simple answer is: currently we don't support that interface structuring model. We only check the methods declared in the concrete repository interface and assume any method not declared in it and not implemented by the class backing the proxy (SimpleJpaRepository in the JPA case) being a custom method.

                          The reason we do that is to be able to fail a bit faster as the actual potential custom implementation class is handed in at a later stage (which we would need to discover custom methods more strictly). This is nothing set in stone so I encourage you to simply open an issue in our JIRA as this seems to be a very reasonable feature request.

                          Thanks for reporting the issue in that detail already!

                          Ollie

                          Comment

                          Working...
                          X