Spring Query DSL提供空指针异常

用户名

我为此原型使用Spring Boot和Hibernate文件数据库。

这是application.properties的摘录(显然省略了用户/密码。)注:我已将fave设置为String而不是布尔值,因此我可以在Query DSL Bindings中使用String:

spring.jpa.generate-ddl=true
spring.datasource.url=jdbc:h2:file:~/db
spring.datasource.driver-class-name=org.h2.Driver
hibernate.hbm2ddl.auto=update
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.show_sql=true

这是带有City类的模型,因为它非常简单:

@Data
@AllArgsConstructor
@Entity
@Table(name = "matches")
@EqualsAndHashCode(of = "id")
public class FilterMatch {

    @SequenceGenerator(name = "MATCHES_SEQ_GENERATOR", sequenceName = "MATCHES_SEQ",
                       initialValue = 1, allocationSize = 1)

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "MATCHES_SEQ_GENERATOR")
    @Column
    private Long id;

    @Column
    private String fave;

    @Column
    private int age;

    @Column
    private int cmHeight;

    @Column
    private int contactsExchanged;

    @Column
    private float compatScore;

    @Column
    private String displayName;

    @Column
    private String jobTitle;

    @Column
    private String mainPhoto;

    @Column
    private String religion;

    @OneToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
    private City city;

    public FilterMatch() {} 

    // getters/setters omitted
}

这是根据需要扩展JPARepository的存储库:

public interface FilterMatchRepository extends JpaRepository<FilterMatch, Long>,
    QueryDslPredicateExecutor<FilterMatch>,QuerydslBinderCustomizer<QFilterMatch> {


    @Override
    default public void customize(QuerydslBindings bindings, QFilterMatch root) {
        bindings.bind(String.class)
                .first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
        bindings.excluding(root.religion,root.jobTitle);
    }

}

我的SearchCriteria类:

public class SearchCriteria {

    private static final Logger logger = LogManager.getLogger(SearchCriteria.class);

    private String key;
    private String operation;
    private Object value;

    public SearchCriteria(String key, String operation, Object value)  {
        this.key = key;
        this.operation = operation;
        this.value = value;
    }
    //getters & setters omitted.
}

我的MatchBuilderPredicates类和MatchPredicates类:

public class MatchPredicatesBuilder {

    private static final Logger logger = LoggerFactory.getLogger(MatchPredicatesBuilder.class);

    private List<SearchCriteria> parameters;

    public MatchPredicatesBuilder() {
        parameters = new ArrayList<>();
    }

    public MatchPredicatesBuilder with(String key, String operation, Object value) {

        parameters.add(new SearchCriteria(key, operation, value));
        return this;
    }

    public BooleanExpression build() {

        if (parameters.size() == 0) {
            return null;
        }

        List<BooleanExpression> predicates = new ArrayList<>();
        MatchPredicate predicate;

        for (SearchCriteria param : parameters) {

            predicate = new MatchPredicate(param);
            BooleanExpression expression = predicate.getPredicate();

            if (Objects.nonNull(expression)) {
                predicates.add(expression);
            }
        }

        BooleanExpression result = predicates.get(0);

        for (int i = 1; i < predicates.size(); i++) {

            result = result.and(predicates.get(i));
        }
        return result;
    }   

}

public class MatchPredicate {

    private static final long serialVersionUID = 8621295752447527269L;
    private static final Logger logger = LoggerFactory.getLogger(MatchPredicate.class);

    private SearchCriteria criteria;

    public MatchPredicate(SearchCriteria criteria) {
        this.criteria = criteria;
    }

    public BooleanExpression getPredicate() {

        PathBuilder<FilterMatch> entityPath = new PathBuilder<>(FilterMatch.class, "FilterMatch");

        if (FilterUtils.isNumeric(criteria.getValue().toString())) {

            NumberPath<Double> path = entityPath.getNumber(criteria.getKey(), Double.class);
            double value = Double.parseDouble(criteria.getValue().toString());

            switch (criteria.getOperation()) {
                case ":":
                    return path.eq(value);
                case ">":
                    return path.goe(value);
                case "<":
                    return path.loe(value);
            }
        } else {

            StringPath path = entityPath.getString(criteria.getKey());

            if (criteria.getOperation().equalsIgnoreCase(":")) {
                return path.containsIgnoreCase(criteria.getValue().toString());
            }
        }
        return null;
    }   
}

因此,此集成测试将引发NPE:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class JpaQueryDslIntegrationTest {

    private static final Logger logger = LoggerFactory.getLogger(JpaQueryDslIntegrationTest.class);

    @Autowired
    private FilterMatchRepository matchRepository;

    private FilterMatch match1;
    private FilterMatch match2;

    /**
     * @throws java.lang.Exception
     */
    @Before
    public void setUp() throws Exception {

        match1 = new FilterMatch();
        match1.setDisplayName("Emma");
        match1.setAge(40);
        match1.setJobTitle("Banker");
        match1.setCmHeight(150);

        City leeds = new City();
        leeds.setName("Leeds");
        leeds.setLon(-1.548567);
        leeds.setLat(53.801277);

        match1.setCity(leeds);
        match1.setMainPhoto("http://thecatapi.com/api/images/get?format=src&type=gif");
        match1.setCompatScore(0.73f);
        match1.setContactsExchanged(0);
        match1.setFave(Constants.FALSE.getValue());
        match1.setReligion("Christian");
        matchRepository.save(match1);

        match2 = new FilterMatch();
        match2.setDisplayName("Diana");
        match2.setAge(44);
        match2.setJobTitle("Consultant");
        match2.setCmHeight(153);

        City london = new City();
        london.setName("London");
        london.setLat(51.509865);
        london.setLon(-0.118092);

        match2.setCity(london);
        match2.setMainPhoto("http://thecatapi.com/api/images/get?format=src&type=gif");
        match2.setCompatScore(0.50f);
        match2.setContactsExchanged(0);
        match2.setFave(Constants.TRUE.getValue());
        match2.setReligion("Atheist");
        matchRepository.save(match2);
    }

    @Test
    public final void testMultipleMatch() {

        MatchPredicatesBuilder builder = new MatchPredicatesBuilder().with("displayName", ":", "Emma");

        Iterable<FilterMatch> results = matchRepository.findAll();
        results = matchRepository.findAll(builder.build()); // This is the line that throws the NPE. i've tested and builder.build() isn't null  
        assertThat(results, Matchers.containsInAnyOrder(match1, match2));
    }
}

我打印出了builder.build()的内容,它是:

containsIc(FilterMatch.displayName,Emma)

这是堆栈跟踪减少了一点。

java.lang.NullPointerException
    at java.lang.String$CaseInsensitiveComparator.compare(String.java:1192)
    at java.lang.String$CaseInsensitiveComparator.compare(String.java:1186)
    at java.util.TreeMap.getEntryUsingComparator(TreeMap.java:376)
    at java.util.TreeMap.getEntry(TreeMap.java:345)
    at java.util.TreeMap.get(TreeMap.java:278)
    at org.hibernate.dialect.function.SQLFunctionRegistry.findSQLFunction(SQLFunctionRegistry.java:45)
    at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.findSQLFunction(SessionFactoryHelper.java:369)
    at org.hibernate.hql.internal.ast.tree.IdentNode.getDataType(IdentNode.java:374)
    at org.hibernate.hql.internal.ast.HqlSqlWalker.lookupProperty(HqlSqlWalker.java:654)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.addrExpr(HqlSqlBaseWalker.java:5003)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1286)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4707)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.functionCall(HqlSqlBaseWalker.java:2733)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1365)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4707)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:4319)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2138)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:815)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:609)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:313)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:261)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:266)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
    at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153)
    at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:553)
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:662)
    at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
    at com.sun.proxy.$Proxy58.createQuery(Unknown Source)
    at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:101)
    at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:94)
    at com.querydsl.jpa.impl.AbstractJPAQuery.fetch(AbstractJPAQuery.java:201)
    at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.findAll(QueryDslJpaRepository.java:105)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:520)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:505)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:477)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

我有一些问题:

1)我的MatchPredicatesBuilder返回一个BooleanExpression,因为我遵循的教程说我应该这样做,但是我不知道为什么编译器不会对此感到cho恼。我去过Java文档,这仍然是个谜。

2)为什么会抛出NPE?builder.build()不为null。从堆栈跟踪很难分辨。

用户名

答案最终是,将实体表命名为除类本身名称之外的任何其他名称都无法解决。当您让休眠自动创建数据库和架构时,会发生这种情况。

我将MatchPredicate类中的行从以下位置更改:

    PathBuilder<FilterMatch> entityPath = new PathBuilder<>(FilterMatch.class, "FilterMatch");

至:

    PathBuilder<FilterMatch> entityPath = new PathBuilder<>(FilterMatch.class, "matches");

这导致了其他异常。所以现在回到小写的“ filtermatch”,它也与实体类的Table注释名称相同:

@Table(name="filtermatch")

我也改变了:

@SequenceGenerator(name = "MATCHES_SEQ_GENERATOR", sequenceName = "MATCHES_SEQ",
                   initialValue = 1, allocationSize = 1)

至:

@GenericGenerator(
        name = "MATCH_SEQ_GENERATOR",
        strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
        parameters = {
                @Parameter(name = "sequence_name", value = "MATCH_SEQ"),
                @Parameter(name = "initial_value", value = "1"),
                @Parameter(name = "increment_size", value = "1")
        })

它消除了启动日志中一些不推荐使用的错误。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章