Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default accessors on projection interfaces must not be considered input properties [DATACMNS-967] #1423

Closed
spring-projects-issues opened this issue Dec 23, 2016 · 10 comments
Assignees
Labels
in: core type: bug
Milestone

Comments

@spring-projects-issues
Copy link

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Sebastian Staudt opened DATACMNS-967 and commented

Recently, I tried to transform my projection interfaces into closed projections to optimize database queries. Before, I used @Value annotations aka. open projections in various places:

public interface ContactPersonProjection {

    @Value("#{target.contactPerson?.fullName}")
    String getContactPersonFullName();

    @Value("#{target.contactPerson?.login}")
    String getContactPersonLogin();
}

I thought that Java 8's default method implementations might be a good way to workaround this limitation. My new projection looks like this:

public interface ContactPersonProjection {

    @JsonIgnore
    Person getContactPerson();

    default String getContactPersonFullName() {
        return getContactPerson() == null ? null : getContactPerson().getFullName();
    }

    default String getContactPersonLogin() {
        return getContactPerson() == null ? null : getContactPerson().getLogin();
    }
}

But it seems the query lookup strategy tries to create queries for the default methods, too.

java.lang.IllegalArgumentException: Unable to locate Attribute  with the the given name [fullName] on this ManagedType [com.example.models.Person]
	at org.hibernate.jpa.internal.metamodel.AbstractManagedType.checkNotNull(AbstractManagedType.java:128)
	at org.hibernate.jpa.internal.metamodel.AbstractManagedType.getAttribute(AbstractManagedType.java:113)
	at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:566)
	at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:573)
	at org.springframework.data.jpa.repository.query.JpaQueryCreator.complete(JpaQueryCreator.java:161)
	at org.springframework.data.jpa.repository.query.JpaQueryCreator.complete(JpaQueryCreator.java:137)
	at org.springframework.data.jpa.repository.query.JpaQueryCreator.complete(JpaQueryCreator.java:49)
	at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88)
	at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73)
	at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.<init>(PartTreeJpaQuery.java:118)
	at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:69)
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:103)

It would be great to have this working out of the box.

PS: Default methods work like expected on open projections (e.g. when there's already one @Value method present)


Affects: 1.13 RC1 (Ingalls), 2.0 M1 (Kay), 1.12.6 (Hopper SR6)

Referenced from: commits 9137dc7, c31714c, a946d65

Backported to: 1.13 GA (Ingalls), 1.12.7 (Hopper SR7)

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Oliver Drotbohm commented

Can you please share a bit more code? The rough outline of the domain type that's about to be projected, the repository query method you're trying to use this with. The exception suggests your Person class simply doesn't have a fullName property. Or at least Hibernate is not able to find it

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Sebastian Staudt commented

That's right, Person doesn't have a fullName property (Hibernate-wise):

public class Person {

    @Column(name = "given_name")
    private String givenName;

    @Id
    @Column(name = "login")
    private String login;

    @Column(name = "sur_name")
    private String surName;

    public String getFullName() {
        return String.format("%s, %s", this.surName, this.givenName);
    }

    public String getGivenName() {
        return this.givenName;
    }

    public void setGivenName(String givenName) {
        this.givenName = givenName;
    }

    public String getLogin() {
        return this.login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getSurName() {
        return this.surName;
    }
    
    public void setSurName(String surName) {
        this.surName = surName;
    }
}

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Oliver Drotbohm commented

What does the repository method look like? From the stack trace you seem to be trying to query on that attribute, which — as you just stated — doesn't really make sense then

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Sebastian Staudt commented

An example repository method that's failing looks like this:

List<ContactPersonProjection> findTop10OrderByNameAsc();

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Oliver Drotbohm commented

Ah okay, it looks like we're trying to derive a path (contactPerson.fullName in your case) from the default methods just like we do for the regular accessors that are backed by a property. AFAICS those methods shouldn't actually be considered input properties in the first place so it's something more abstract we have to fix. I'll move this to Spring Data Commons as it seems it needs to be fixed there (effectively ReturnedType.getInputProperties()) to avoid downstream errors.

Thanks for the quick feedback!

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Oliver Drotbohm commented

Oh, I just realized. You should be able to workaround that issue by just moving away from the property syntax for the method name: contactPersonFullName should do the trick

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Oliver Drotbohm commented

Binaries with fixes available. Feel free to give them a spin. Proper tests only on 2.0.x unfortunately as we can't declare default methods in 1.x branches due to JDK 6 compatibility requirement

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Sebastian Staudt commented

The workaround works, but I stumbled upon another issue as closed projections seem to generate broken HQL queries for entity subclasses.
I guess this is a whole new issue, though. Do you have any information about problems with closed projections on subclasses entities or should I open a new issue?

Where can I get the binaries for testing the fix?

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 23, 2016

Oliver Drotbohm commented

Just use our snapshot repository and point to ….BUILD-SNAPSHOT versions in your POM. Or use the BOM. Or Boot and redeclare spring-data-releasetrain.version to the names described in the BOM sample

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Dec 30, 2016

Sebastian Staudt commented

The fix works. Tested with 1.12.7.BUILD-SNAPSHOT

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core type: bug
Projects
None yet
Development

No branches or pull requests

2 participants