Git Product home page Git Product logo

query-validator's Introduction

Hibernate logo

Hibernate Query Validator

Compile time validation for queries written in HQL, JPQL, and Panache.

Requirements

This project now requires at least JDK 11, but JDK 15 or above is preferred.

Building

Type ./gradlew from this project directory.

This produces an artifact with the Maven coordinates org.hibernate:query-validator:2.0-SNAPSHOT in your local Maven repository.

It also creates a far jar query-validator-2.0-SNAPSHOT-all.jar in the build/libs directory of this project.

Usage

The persistent entity classes must be annotated with the basic JPA metadata annotations like @Entity, @ManyToOne, @Embeddable, @MappedSuperclass, @ElementCollection, and @Access. You may use XML-based mappings to specify database mapping information like table and column names if that's what you prefer. But entities mapped completely in XML will not be discovered by the query validator.

  1. Put query-validator-2.0-SNAPSHOT-all.jar in the compile-time classpath of your project. (Or depend on org.hibernate:query-validator:2.0-SNAPSHOT.)
  2. Annotate a package or toplevel class with @CheckHQL.

Usage with plain Hibernate or JPA

The validator will check any static string argument of

  • the createQuery(), createSelectionQuery(), and createMutationQuery() methods,
  • the @NamedQuery() annotation, or
  • the @HQL annotation

which occurs in a package, class, or interface annotated @CheckHQL.

Usage with Panache

Inside a Panache entity or repository, the following queries will be checked:

  • list(), find(), and stream(),
  • count(),
  • delete(), and
  • update()

Errors and warnings

The purpose of the query validator is to detect erroneous query strings and query parameter bindings when the Java code is compiled, instead of at runtime when the query is executed.

Errors

A compile-time error is produced if:

  • the query has syntax errors,
  • an entity name in the query doesn't reference a persistent entity class,
  • a member name in the query doesn't reference a mapped field or property of the entity, or
  • there is some other typing error, for example, incorrect function argument types.

Warnings

Additionally, any JPA Query instance that is created and immediately invoked in a single expression will have its parameter bindings validated. A warning is produced if:

  • the query string has a parameter with no argument specified using setParameter(), or
  • an argument is specified using setParameter(), but there is no matching parameter in the query string.

All Panache queries have their parameters validated.

Usage from command line

When using a command line compiler, gradle, or mvn, errors from the query validator are displayed in the compiler output alongside other compilation errors.

javac and ECJ

Just compile your code with javac, or even with ECJ (java -jar ecj-4.6.1.jar), with the query validator jar in the classpath:

-classpath query-validator-2.0-SNAPSHOT-all.jar

Of course, you'll also need Hibernate core on the classpath.

Gradle

In principle, it's enough to declare dependencies on Hibernate core and on the query validator, just like this:

dependencies {
    implementation 'org.hibernate.orm:hibernate-core:6.3.0-SNAPSHOT'
    annotationProcessor 'org.hibernate:query-validator:2.0-SNAPSHOT'
}

Unfortunately, this often results in some quite annoying warnings from javac. Get rid of them by also declaring an implementation dependency on the Query validator:

dependencies {
    implementation 'org.hibernate:query-validator:2.0-SNAPSHOT'
    annotationProcessor 'org.hibernate:query-validator:2.0-SNAPSHOT'
    implementation 'org.hibernate:query-validator:2.0-SNAPSHOT'
}

Maven

Maven handles annotation processors correctly. Just declare the dependency on the query validator:

<dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>query-validator</artifactId>
        <version>2.0-SNAPSHOT</version>
        <optional>true</optional>
    </dependency>
<dependencies>

Usage in IDEs

Both IntelliJ and Eclipse require that annotation processing be explicitly enabled.

IntelliJ

Select Enable annotation processing in IntelliJ IDEA preferences under Build, Execution, Deployment > Compiler > AnnotationProcessors.

IntelliJ Screenshot 1

You do not need to do this if you're using Gradle to build your project.

IntelliJ only runs annotation processors during a build (that is, when you Run your code or explicitly Build Project). So you won't see errors in your Java editor as you're typing.

Eclipse

Eclipse IDE doesn't load annotation processors from the project classpath. So you'll need to add the query validator manually.

  1. In Project > Properties go to Java Compiler > Annotation Processing and select Enable annotation processing.
  2. Then go to Java Compiler > Annotation Processing > Factory Path and click Add External JARs... and add build/libs/query-validator-2.0-SNAPSHOT-all.jar from this project directory.

Your project properties should look like this:

Eclipse Screenshot 1 Eclipse Screenshot 2

Eclipse runs annotation processors during every incremental build (that is, every time you Save), so you'll see errors displayed inline in your Java editor.

Eclipse Screenshot 3

If the query validator doesn't run, please ensure that:

  • Eclipse itself is running on a compatible JDK.
  • Your project is set up to compile with a compatible Java compiler, and the compiler compliance level is set to at least 1.8.

Compatibility

The query validator was developed and tested with:

  • JDK 15, JDK 17, JDK 20
  • Hibernate 6.3.0
  • ECJ 3.33.0
  • Eclipse IDE with JDT Core 3.33.0

Other versions of javac, ECJ, and Hibernate may or may not work. The query validator depends on internal compiler APIs in javac and ECJ, and is therefore sensitive to changes in the compilers.

Caveats

Please be aware of the following issues.

HQL is a superset of JPQL

Queries are interpreted according to Hibernate's flavor of JPQL (i.e. HQL), which is a superset of the query language defined by the JPA specification. Queries accepted by the query validator may not execute correctly on other implementations of JPA.

Explicit entity names are not supported in Eclipse/ECJ

In ECJ, don't use @Entity(name="Whatever"), since, during an incremental build, the processor won't be able to discover the entity named Whatever. Just let the entity name default to the name of the class.

Ugly error messages

Please report ugly, confusing, or badly-formatted error messages as bugs.

query-validator's People

Contributors

fromage avatar gavinking avatar sanne avatar sebersole avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

query-validator's Issues

New module layout required

So, if we add this APT processor in the classpath, we pull in all our deps in the user's classpath, including Groovy and our specific version of Hibernate.

In Maven, we can specify an APT processor module as part of the compiler plugin config (see https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#annotationProcessorPaths) which doesn't add it to the user's classpath, which is probably cleaner.

But it also means the user won't have the @CheckSQL annotation in his classpath. So if we want to support that use-case, we'd need to extract the annotation to its own module.

UserTypes

We need to check that Hibernate UserTypes work.

Want query-validator lib

Hello Gavin
I am looking for below dependency however it is not available on maven repo. Can you please give me jar file instead. Thank you very much.


org.hibernate
query-validator
1.0-SNAPSHOT
true

Unknown dependency: org.codehaus.groovy:groovy:2.5.6:indy

The gradle build is referring to org.codehaus.groovy:groovy:2.5.6:indy, which I don't seem able to find.

The build seems to work if I replace this with org.codehaus.groovy:groovy:2.5.6, but I don't really know if that's fine.

boolean literals

As reported by @FroMage, boolean literals like false break the query validator in Eclipse.

He already has a fix.

Build issues

maven-shade-plugin

The processor has a few dependencies now, which aren't really a problem when using Maven, nor even when using a command-line compiler, but are really a problem in the case of Eclipse where AFAICT, a processor can only really be a single jar because you have to add it explicitly in the annotation processing preferences page. (A crap requirement.)

So anyway I'm using the shade plugin in Maven to package a fat jar. However, that's fragile unless I do package renaming, but my initial experiments with <relocation/> was that it broke not only Groovy but also my own code.

META-INF/services

So I was using Google's @AutoService to produce the META-INF/services/javax.annotation.processing.Processor file, which is kind-of a requirement when simultaneously working with Maven and IntelliJ (at least if you want to be able to debug your tests). And anyway, that was working fine.

But then I added the Groovy compiler and shit went pear-shaped. The annotation processor for auto-service breaks the Groovy compiler (at least in Maven). So now I'm stuck commenting-out the Groovy bit of the build and un-commenting the @AutoService annotation every time I want to debug tests in IntelliJ. This is painful.

I don't know how to solve this problem. Perhaps it's solvable using Gradle but Gradle is just an unknown quantity to me. One imagines that Gradle is quite good at compiling Groovy though.

identifier types

Entities have a hardcoded @Id type. This doesn't seem to break anything. Still, it would be cleaner to do it properly.

treat() operator

The treat() operator isn't working because MockEntityPersister doesn't know about the types of properties of its subclasses. Not quite sure how to fix that given the current lazy approach.

@CheckHQL(whitelist={functionNames})

The @SuppressWarnings approach has not worked out very well because ECJ warns for unknown warning types.

Anyway a much better idea is to ask the user to register the names of SQL functions they're using in their queries.

JDK 9-12

With the introduction of the module system in JDK 9, quite a few things change for us, including some of the javac compiler APIs we're using.

I've managed to get the javac tests working on JDKs 9, 10, 11, and 12. And I've pushed that work to the jdk10 branch.

Note that:

  • The ECJ tests are broken but I have not bothered to investigate this one yet, since I doubt it's anything very deep.
  • My patches aren't sufficient to work with JDK13 because of some sort of incompatibility with Groovy that I don't have time to investigate.

check concatenated string literals

Sometimes it's necessary to split a query string across multiple lines of code using +, since Java doesn't have multiline strings.

We should be able to validate such "broken" string literals.

: and ? in quoted strings

I'm using a regex to find parameters in the query string. This is rubbish, of course, and so we need to use the real HQL lexer.

typecheck function arguments

Enhance Hibernate to allow typechecking of function and operator arguments, according to the type system of the JPA spec.

queries with unclosed string literals

For some reason it seems that if the query contains an unterminated string literal, we get no error at all, not even for other errors that precede the unterminated string.

JPA strict mode

HQL has a tradition of just passing things (functions, for example) that it doesn't recognize straight through to the database and letting the SQL blow up. This isn't great for queries checked at compile-time. However, JPA doesn't require this behavior, and allows a much greater level of typechecking.

A further difference is that, whereas Hibernate's type system is open-ended, and so function arguments aren't checked. However JPA does sort of have a somewhat well-defined type system and it's function arguments can in principle be checked.

Did you mean?

Add suggestions when a name is wrong. At least for property names. Probably impossible for entity names.

typecheck parameter names

@lukaseder suggested typechecking named query names, for example:

em.createNamedQuery("Person.byName")

and parameter names, for example:

em.createNamedQuery("from Person p where p.name = :name")
  .setParameter("name", name)

checking named query names is going to be a bit hard because:

  1. in an incremental build I can't scan the whole codebase for a matching named query, and
  2. anyway, if this is what you want, use a static final String, not @NamedQuery!

On the other hand, typechecking parameter names looks pretty easy to do and quite useful.

how to check hibernate annotation syntax ?

can query-validator, or any other tool detect, and possibly suggest fixes for hibernate 4 errors like

org.hibernate.MappingException: Unable to find column with logical name: CODE_IND in org.hibernate.mapping.Table(INDICATORS_OD) and its related supertables and secondary tables

Release it

I think we should do a release of this project to Maven. WDYT @emmanuelbernard @FroMage @Sanne ? Should we do it? Precisely how can I put jars in Maven? This is something I have never once done before...

enum references in nested packages

I have examples of enum values like test.Sex.MALE in the test suite. But it turns out that they're broken for nested packages like test.example.Sex.MALE.

respect the AccessType and mapped superclasses

The JPA AccessType is determined by the position of the @Id annotation in the entity or its superclass, or by the @Access annotation. We should use the AccessType when searching for properties. Also, we should ignore non-@Entity, non-MappedSuperclass superclasses when searching for properties.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.