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-123456789012
which 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.
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;
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With