spring-projects / spring-data-jpa

Simplifies the development of creating a JPA-based data access layer.
https://spring.io/projects/spring-data-jpa/
Apache License 2.0
2.98k stars 1.41k forks source link

java.lang.NoSuchMethodError when a BooleanBuilder is passed as an argument to findAll method [DATAJPA-115] #540

Closed spring-projects-issues closed 11 years ago

spring-projects-issues commented 12 years ago

Mark Serrano opened DATAJPA-115 and commented

I have a Jpa repository that utilizes QueryDSL's fluent API:

public interface IUserRepository extends JpaRepository<User, Long>, QueryDslPredicateExecutor<User>

Then I have a service that reads records from the repository via the findAll() method. I have a requirement that the fields must be dynamic and the records are pageable:

@Service
@Transactional
public class UserService implements IUserService {

    @Autowired
    private IUserRepository userRepository;

    @Autowired
    private EntityManagerFactory emf;

    @Override
    public Page<User> readFromRepository(String username, Pageable page) {

        PathBuilder<User> entityPath = new PathBuilder<User>(User.class, "user");
        StringPath path = entityPath.get(new StringPath("username"));
        BooleanExpression hasUsername = path.eq(username);

        BooleanBuilder builder = new BooleanBuilder();
        builder.and(hasUsername);

        return userRepository.findAll(builder, page);
    }
...
}

However an exception is thrown whenever that method is called:


java.lang.NoSuchMethodError: com.mysema.query.jpa.JPQLQuery.offset(J)Lcom/mysema/query/Query;
    at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.applyPagination(QueryDslJpaRepository.java:168)
    at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.findAll(QueryDslJpaRepository.java:128)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:322)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:307)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy24.findAll(Unknown Source)
    at org.demo.service.UserService.readFromRepository(UserService.java:43)

I tried digging into the code and the source and I can't find the reasons why. The internal implementation doesn't show any missing methods. I have checked my jars but I didn't find any old dependencies. I tested my project both at home and at work (Ubuntu, Windows, proxied, non-proxied environments). But I keep getting the same exception.

When I check the following:

org.springframework.data.jpa.repository.support.QueryDslJpaRepository.applyPagination(QueryDslJpaRepository.java:168)
    protected JPQLQuery applyPagination(JPQLQuery query, Pageable pageable) {

        if (pageable == null) {
            return query;
        }

        query.offset(pageable.getOffset());
        query.limit(pageable.getPageSize());

        return applySorting(query, pageable.getSort());
    }

As a workaround, I did the following code:

@Service
@Transactional
public class UserService implements IUserService {

    @Autowired
    private IUserRepository userRepository;

    @Autowired
    private EntityManagerFactory emf;

...
    @Override
    public List<User> readFromEntityManager(String username, Pageable page) {

        PathBuilder<User> entityPath = new PathBuilder<User>(User.class, "user");
        StringPath path = entityPath.get(new StringPath("username"));
        BooleanExpression hasUsername = path.ne(username);

        BooleanBuilder builder = new BooleanBuilder();
        builder.and(hasUsername);

        EntityManager em = emf.createEntityManager();

        JPQLQuery result = new JPAQuery(em).from(entityPath).where(builder);

        if (page != null) {
            result.offset(page.getOffset());
            result.limit(page.getPageSize());
        }

        return result.list(entityPath);
    }
...
}

And it works perfectly. I tried to match as possible the original Spring JPA implementation. I don't really know what's wrong with the findAll() method, but directly calling the EntityManagerFactory somehow did the trick.

I have attached a strip-down version of my webapp. It's a Maven project with unit tests included. Just run Maven test to verify the error.

You need to have MySQL and create a db "testdb". No data is needed but you can prepopulate them. There's a data.sql inside the classpath. (I'm trying to initialize the db with jdbc:initialize-database but it keeps throwing an error. You might wanna take a peek of that error if you like).

By the way my Maven properties:

<properties>
     <spring.version>3.1.0.RC1</spring.version>
     <spring.data.jpa.version>1.1.0.M1</spring.data.jpa.version>
     <querydsl.version>2.2.3</querydsl.version>
</properties>

Thanks.


Affects: 1.0.1, 1.1 M1

Attachments:

Referenced from: commits https://github.com/spring-projects/spring-data-jpa/commit/02558854de16908a1db0c72edc653064dc57b123, https://github.com/spring-projects/spring-data-jpa/commit/6059ff40fe8f164c1fde7b0ab8afbbd9ee4f5271

spring-projects-issues commented 12 years ago

Mark Serrano commented

By the way, the reason why I have the following code is because the fields will come from JSON. The "username" name here is just a sample field

PathBuilder<User> entityPath = new PathBuilder<User>(User.class, "user");
StringPath path = entityPath.get(new StringPath("username"));
BooleanExpression hasUsername = path.eq(username);
spring-projects-issues commented 12 years ago

Oliver Drotbohm commented

We're currently building against Querydsl 2.2.0. Is there a chance you try running your code against that version? It seems there we're some breaking API changes that require recompilation against the newer version from our side. I'll consider this ticket as a general "upgrade to 2.2.3"

spring-projects-issues commented 12 years ago

Oliver Drotbohm commented

Fixed and deployed to both 1.1.x and 1.0.x branch

spring-projects-issues commented 12 years ago

Mark Serrano commented

Thanks for the quick action. I pulled the latest snapshot, and my app worked. I'm no longer getting java.lang.NoSuchMethodError. My test suite are now all green.

I believe the problem has been resolved