Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data cannot fetch a record using UUID in postgresql

I am using Spring data to manage a REST API. All tables in my postgresql database has a primary key of type UUID. I am able to persist this data but unable to fetch it. Below is the code I am using and the problem I am facing.

MyTableParent.java

@MappedSuperclass
public class MyTableParent {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(
            name = "uuid",
            strategy = "com.mypackage.UUIDGenerator",
            parameters = {
                    @Parameter(
                            name = UUID_NAMESPACE,
                            value = "something"
                    )
            }
    )
    private UUID id;
    // Constructor getter setter
}

MyTable.java

@Entity
@Table(name = "my_table")
public class MyTable extends MyTableParent {
    private String name;
    // constructor getter setter
}

This is my service class for the table

TableService.java

@Service("tableservice")
public class TableService {
    private TableRepository tableRepository;

    public TableService(TableRepository tableRepository) {
        this.tableRepository = tableRepository;
    }

    public List<MyTable> read(MultiValueMap<String, String> queryParams) {
        return this.tableRepository.findAll(TableSpecification.searchByParams(queryParams));
    }

    public MyTable read(UUID id) {
        return this.tableRepository.findById(id).orElse(null);
    }
}

As you can see I am using a repository object as well as a specification. I have defined the repository as such

@Repository
interface TableRepository extends JpaRepository<MyTable, UUID>, JpaSpecificationExecutor<MyTable> {
}

And the specification is created as

public class TableSpecification {
    public static <T> Specification<T> searchByParams(MultiValueMap<String, String> queryMap) {
        return (Specification<T>) (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) -> {
            Set<Map.Entry<String, List<String>>> fields = queryMap.entrySet();
            if (queryMap.size() != 0) {
                List<Predicate> predicates = new ArrayList<>();
                for (MultiValueMap.Entry<String, List<String>> field : fields) {
                    List<String> params = new ArrayList<>(field.getValue());
                    String property = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, field.getKey());
                    Expression<String> expression = root.get(property);
                    predicates.add(expression.in(params));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[]{}));
            }
            return null;
        };
    }
}

The first issue arises when I call read(MultiValueMap). Say I have my request params as /my_table?id=12345678-1234-1234-1234-123456789012which triggers the code this.tableRepository.findAll(TableSpecification.searchByParams(queryParams)) where queryParams the MultiValueMap. This is throwing an error

java.lang.IllegalArgumentException: Parameter value [12345678-1234-1234-1234-123456789012] did not match expected type [java.util.UUID (n/a)]
    at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:54) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:27) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.query.internal.QueryParameterBindingImpl.validate(QueryParameterBindingImpl.java:90) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue(QueryParameterBindingImpl.java:55) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.setParameter(AbstractProducedQuery.java:486) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.setParameter(AbstractProducedQuery.java:104) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.query.criteria.internal.compile.CriteriaCompiler$1$1.bind(CriteriaCompiler.java:119) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.query.criteria.internal.CriteriaQueryImpl$1.buildCompiledQuery(CriteriaQueryImpl.java:368) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.query.criteria.internal.compile.CriteriaCompiler.compile(CriteriaCompiler.java:149) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:3616) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:203) ~[hibernate-core-5.2.14.Final.jar:5.2.14.Final]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350) ~[spring-orm-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at com.sun.proxy.$Proxy125.createQuery(Unknown Source) ~[na:na]...

So I tried to check the findById method. This is causing this.tableRepository.findById(id).orElse(null) to return null which means that the find failed too.

I am at a loss how to proceed with this. Please advise.

N.B. I can fetch records by the name field correctly. I have a hunch that spring data is messing up the types somewhere.

EDIT Not working even for String type field.

like image 273
Sayak Mukhopadhyay Avatar asked Apr 06 '18 08:04

Sayak Mukhopadhyay


2 Answers

I'm using Hibernate 5.3.11 and had the same problem. Thanks to the answer Luca Basso Ricci in 'FindByUUID() using Spring Data's JPA Repository' it was fixed.

The solution (for me) was adding the following annotation to the UUID field:

import org.hibernate.annotations.Type

... 

@Type(type="org.hibernate.type.UUIDCharType")
private UUID uuid;
like image 151
Martin van Wingerden Avatar answered Sep 27 '22 16:09

Martin van Wingerden


The Parameter value [12345678-1234-1234-1234-123456789012] did not match expected type [java.util.UUID (n/a)] error means that value read from the database failed to map to the corresponding field type.

See HHH-9562 Dialect specific UUID handling which improved UUID handling in Hibernate 5. Your problem might be solvable with @Column(columnDefinition = "uuid", updatable = false) annotation.

@Id
@Column(columnDefinition = "uuid", updatable = false)
@GeneratedValue(generator = "uuid")
@GenericGenerator(
        name = "uuid",
        strategy = "com.mypackage.UUIDGenerator",
        parameters = {
                @Parameter(
                        name = UUID_NAMESPACE,
                        value = "something"
                )
        }
)
private UUID id;

If you need to debug it further check PostgreSQL Dialect class that you configured in your code and ensure that field definition in your entity correctly declares PostgreSQL UUID column type. There have been a number of issues with UUID e.g. HHH-9577.

like image 30
Karol Dowbecki Avatar answered Sep 27 '22 15:09

Karol Dowbecki