spring-bootspring-dataspring-data-jpaspring-data-commons

Spring boot 1.3.0 upgrade and JPA custom method not working


After upgrading to spring boot version 1.3.0.RELEASE, I see following failure when trying to start.

Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'productRepository': Invocation of init
method failed; nested exception is
org.springframework.data.mapping.PropertyReferenceException: No
property true found for type boolean! Traversed path: Product.active.
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
        ... 64 more Caused by: org.springframework.data.mapping.PropertyReferenceException: No property true found for type boolean! Traversed path: Product.active.
        at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:75)
        at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327)

Here is relevant snippet of the code that causes issue. No change was made to any repository or data model.

public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByActiveTrue_productInfoActiveTrue();
}

@Entity
@DiscriminatorValue(value = "0")
public class Product extends BaseProduct {

    public boolean isSpecial() {
        return special;
    }

    public void setSpecial(boolean special) {
        this.special = special;
    }
}

@Entity(name = "products")
@DiscriminatorColumn(name = "matchtype", discriminatorType = DiscriminatorType.INTEGER)
public abstract class BaseProduct implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column(name = "isspecial")
    protected boolean special;

    @Id
    @GeneratedValue
    @Column(name = "productid")
    private Long productId;

    @Column(name = "active")
    private boolean active;

    @OneToMany(mappedBy = "productId", fetch = FetchType.EAGER)
    private Set<ProductInfo> productInfo;

    @Entity(name = "products_ranges")
    public static class ProductInfo {

        @Id
        @Column(name = "prodrangeid")
        private Long id;

        @Column(name = "product_id")
        private Long productId;

        @Column(name = "active")
        private boolean active;
    }

Any help around this issue would be appreciated. I tried going back to older version of spring-data-common but that doesn't work with new spring-data-jpa.

Thanks!


Solution

  • If that has ever worked, that was a bug. findByActiveTrue_productInfoActiveTrue(…) is not a valid query method. According to your domain type the method consists of two predicates: active = true and productInfo.active = true. Those to predicates need to be concatenated using a keyword, which in your case I assume will probably be And. findByActiveTrueAndProductInfoActiveTrue(…) not only reads better, it also should work.

    To make sure you understand the error message this it what happens:

    1. We strip of the prefix until the first By -> ActiveTrue_productInfoActiveTrue
    2. We strip of the keyword -> ActiveTrue_productInfoActive
    3. We now try to find properties in the domain object starting with the complete match, shortening it from the right.
    4. We end up finding the very left Active with a remaining tail of True_productInfoActive.
    5. We repeat the process starting at 3 with the resolved property type (Boolean) and end up failing to find something, true being the last segment tried.

    Generally speaking, I'd be interested to hear what made you using underscores in the first place because the underscore is essentially a "traverse the nested property to right" command to the parser, which clearly can't be the intention of your method.