6

Assume there is an entity

@Entity
public class Article {
 // ...
  private String uri;

  public void setUri(URI uri) {
    this.uri = uri.toString();
  }

  public URI getUri() {
    return URI.create(uri.toString();
  }
}

and a spring data jpa repository

public interface ArticleRepository extends CrudRepository<Article, Long> {
    Optional<Article> findByLink(URI link);
}

but using the findByLink(URI) method fails with InvalidDataAccessApiUsageException: Parameter value [http://foo.de/foo.html] did not match expected type [java.lang.String (n/a)]. This can ofcourse be avoided by changing the parameter type to string. But then every URI has to be converted to a string before querying.

What is the best way to handle properties of type URI if you have to query them with spring data jpa?

gregor
  • 4,733
  • 3
  • 28
  • 43

1 Answers1

10

In your current configuration you are trying to apply a URI parameter to a string field. That fails for obvious reasons

Option 1

You change your field to take an URI as well and register a converter to convert a URI to the DB representation.

That would look something like this:

@Converter(autoApply = true)
public class UriPersistenceConverter implements AttributeConverter<URI, String> {

    @Override
    public String convertToDatabaseColumn(URI entityValue) {
        return (entityValue == null) ? null : entityValue.toString();
    }

    @Override
    public URI convertToEntityAttribute(String databaseValue) {
        return (StringUtils.hasLength(databaseValue) ? URI.create(databaseValue.trim()) : null);
    }
}

With this converter you can change the field to URI as well:

@Entity
public class Article {
 // ...
  private URI uri;

  public void setUri(URI uri) {
    this.uri = uri;
  }

  public URI getUri() {
    return this.uri;
  }
}

Option 2

I tried option 1 and can confirm it is working - but Option 2 is theoretical - I think you are using JPA field access - so JPA accesses your entity using the fields - I think if you change the access to PROPERTY JPA should use your getters and setters - they have the correct type so your code could work then.

The access type is inferred using the location of your mapping annotations. If you annotate the fields you get field access - if you annotate your getters you get property access. There is also an entity level annotation to set the access level javax.persistence.Access

@Access(PROPERTY)

Try to annotate your entity with the annotation above and give it a try.

Mathias Dpunkt
  • 11,594
  • 4
  • 45
  • 70