The method findAll() in the type CrudRepository<Build,ObjectId> is not applicable for the arguments (Predicate) - spring-boot

I want to upgrade my spring boot project to 2.6.6 from 1.5.22.RELEASE while upgrading I'm getting the following errors suggest me how to fix it
The method findAll() in the type CrudRepository<Build,ObjectId> is not applicable for the arguments (Predicate)
The method findAll() in the type CrudRepository<Build,ObjectId> is not applicable for the arguments (Predicate, PageRequest)
Repository:
package com.capitalone.dashboard.repository;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;
public interface BuildRepository extends CrudRepository<Build, ObjectId>, QueryDslPredicateExecutor<Build>
{
Build findByCollectorItemIdAndNumber(ObjectId collectorItemId, String number);
Build findByCollectorItemIdAndBuildUrl(ObjectId collectorItemId, String buildUrl);
...
}
Client code:
Iterable<Build> result;
if (request.getMax() == null) {
result = buildRepository.findAll(builder.getValue());
} else {
PageRequest pageRequest =PageRequest.of(0, request.getMax(), Sort.Direction.DESC, "timestamp");
result = buildRepository.findAll(builder.getValue(), pageRequest).getContent();
}
build.class
#Document(collection="builds")
public class Build extends BaseModel {
private ObjectId collectorItemId;
private long timestamp;
private String number;
I also tried changing to findAllById then got the below error :
The method findAllById() in the type CrudRepository<Build,ObjectId> is not applicable for the arguments (Predicate)
though the interface is extending QueryDslPredicateExecutor why I'm not able to use findAll(predicate)?

Looking at the API doc for CrudRepository and JpaRepository, I see that findaAll(Example<s> example) is only available in the JpaRepository interface. (I assume builder.getValue() in your code is a single value.)
For CrudRepository you would need to use findAllById(Iterable<ID> ids).
Thus, I suggest switching to JpaRepository or using findAllById.

Related

#RepositoryRestResource changes url every time the application is restarted

I have a repository interface that extends JpaRepository and a NameRepositoryCustom.
My repository is annotated with #RepositoryRestRessource(collectionResourceRel="pathname", path="pathname").
The problem I have is that every second restart of my application the URL of the repository gets changed so I can't find the exposed data of the repository under the URL I defined and some features like the search of the repository aren't exposed in the API anymore either.
The "NameRepositroyCustom" is used for a search function which uses another Repository to implement Specification with JPA Criteria Api for a searchbar in my frontend.
Does anybody have a solution for this? The only repository annotated as #RepositoryRestRessource is the main repository that implements all the others. The NameRepositorySpec is annotated with #Repository, could this maybe be the cause?
Edit: I implemented the code as an example to clarify the relations between the mentioned classes and interfaces.
This is the basic repository related to the entity persisted in the database:
#RepositoryRestResource(collectionRessourceRel = "enitynames", path = "entitynames")
public interface EntitynameRepository extends JpaRepository<Entityname, Long>, EntitynameRepositoryCustom{
//custom methods in here
}
This is the custom repository:
public interface EntitynameRepositoryCustom {
Page<Entityname> search(String exampleParam1, String exampleParam2, Pageable pageable);
}
This is the implementation of the custom repository:
public class EntitynameRepositoryCustomImpl implements EntitynameRepositoryCustom{
#Autowired
EntityManager em;
#Autowired
EntitynameRepositorySpec entitynameRepositorySpec;
Specification<Entityname> querySpecification = null;
#Override
public Page<Entityname> search(String exampleParam1, String exampleParam2, Pageable pageable) {
//Code here uses the criteria builder and Specification to generate a custom query with optional parameters
CriteriaBuilder cb= em.getCriteriaBuilder();
CriteriaQuery<Entityname> cq = cb.createQuery(Entityname.class);
//Code below is done for every passed in parameter
if(exampleParam1 != null){
Specification<Entityname> param1Specification = EntitynameSpecification.likeParam1(exampleParam1);
querySpecification = Specification.where(param1Specification);
} else {
return null;
}
return entitynameRepositorySpec.findAll(specification, pageable);
}
}
This is the specification repository:
public interface EntitynameRepositorySpec extends JpaRepository<Entityname, Long>, JpaSpecificationExecutor<Entityname>{
}
And this is the implementation of the specification:
public class EntitynameSpecification {
public static Specification<Entityname> likeExampleParam1(String exampleParam1){
if(exampleParam1 == null){
return null;
}
return(root, query, cb) -> {
reutrn cb.like(root.get("fieldname"), "%"+ exampleParam1 + "%");
};
}
}
The URL of the repository gets changed to a part of the entity name compared to my example it would be something like: entityname has URL: /entityname
if the bug occurs the URL changes to /name.

how to use EnumCodec in r2dbc-postgresql

I am trying to use EnumCodec from the latest version of r2dbc-postgresql (0.8.4) unsuccessfully, and I wondered if you could help me.
I use also spring-data-r2dbc version 1.1.1.
I took the exact example from the GitHub and created an enum type “my_enum” in my Postgres,
and a table “sample_table” which contains ‘name’ (text) and ‘value’ (my_enum).
Then I did as in the example:
SQL:
CREATE TYPE my_enum AS ENUM ('FIRST', 'SECOND');
Java Model:
enum MyEnumType {
FIRST, SECOND;
}
Codec Registration:
PostgresqlConnectionConfiguration.builder()
.codecRegistrar(EnumCodec.builder().withEnum("my_enum", MyEnumType.class).build());
I use DatabaseClient in order to communicate with the DB.
I tried to insert using 2 methods:
databaseClient.insert().into(SampleTable.class)
.using(sampleTable).fetch().rowsUpdated();
or:
databaseClient.insert().into("sample_table")
.value("name", sampleTable.getName())
.value("value", sampleTable.getValue())
.then();
where SampleTable is:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Table("sample_table")
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class SampleTable implements Serializable {
private String name;
#Column("value")
#JsonProperty("value")
private MyEnumType value;
}
But I get the same error using both:
column "value" is of type my_enum but expression is of type character varying
Can you please help me understand what I did wrong, or refer me to some working example?
I appreciate your help!
Spring Data considers enum values as values to be converted to String by default. You need to register a Converter that retains the type by writing the enum-type as-is.
#WritingConverter
class MyEnumTypeConverter implements Converter<MyEnumType, MyEnumType> {
#Override
public MyEnumType convert(MyEnumType source) {
return source;
}
}
Next, you need to register the converter. If you're using Spring Data R2DBC's AbstractR2dbcConfiguration, then override getCustomConverters():
class MyConfiguration extends AbstractR2dbcConfiguration {
#Override
protected List<Object> getCustomConverters() {
return Collections.singletonList(new MyEnumTypeConverter());
}
// …
}
Alternatively, if you configure DatabaseClient standalone, then you need a bit more of code:
PostgresqlConnectionConfiguration configuration = PostgresqlConnectionConfiguration.builder()
.codecRegistrar(EnumCodec.builder().withEnum("my_enum", MyEnumType.class).build())
.host(…)
.username(…)
.password(…)
.database(…).build();
R2dbcDialect dialect = PostgresDialect.INSTANCE;
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(dialect, Collections.singletonList(new MyEnumTypeConverter()));
DatabaseClient databaseClient = DatabaseClient.builder()
.connectionFactory(new PostgresqlConnectionFactory(configuration))
.dataAccessStrategy(strategy)
.build();
However, there are two bugs in the R2DBC driver that prevent Spring Data from working as expected:
Row.decode(…) fails for enum type with IllegalArgumentException: 72093 is not a valid object id #301
EnumCodec decoding fails if the requested value type is Object #302
As temporary workaround, you can duplicate EnumCodec in your codebase and apply the fix from #302 until a new release of R2DBC Postgres is available.
I have tried to use pg enum type and Java Enum class in my sample projects.
If you are using DatabaseClient API(in Spring 5.3 core, not use Spring Data R2dbc), register an EnumCodec in PostgresConnectionFactory is enough.
Check my exmaple.
If creating a pg type enum as a column type in the table schema, and Register an EnumCodec in the PostgresConnectionFactory.builder. You need to write custom #ReadingConverter to read the custom enum.
Check my example here.
If you use text-based type(varchar) in the table schema with Java Enum. no need for the extra effort on conversion, check my example here.
The Spring Data R2dbc said if using the driver built-in mechanism to handle enum, you have to register an EnumWriteSupport. But according to my experience, when using Spring Data R2dbc, the write can be handled automatically, but reading converter is required to read enum from Postgres.

Spring Data - Build where clause at runtime

In Spring Data, how can I append more conditions to an existing query?
For example, I have the CrudRepository below:
#RepositoryRestResource
public interface MyRep extends CrudRepository<MyObject, Long> {
#Query("from MyObject mo where mo.attrib1 = :attrib1")
List<MyObj> findMyObjects(String attrib1, String conditions);
}
At runtime, I will need to call "findMyObjects" with two params. The first param is obviously the value of attrib1. the second param will be a where clause that would be determined at runtime, for example "attrib2 like '%xx%' and attrib3 between 'that' and 'this' and ...". I know this extra where condition will be valid, but I don't know what attributes and conditions will be in it. Is there anyway to append this where clause to the query defined in the #Query annotation?
Unfortunately, no. There is no straightforward way to achieve that.
You'll want to use custom reporistory methods where you'll be able to inject an EntityManager and interact with EntityManager.createQuery(...) directly.
Alternatively, you can build dynamic queries using Specifications or QueryDsl.
I ended up injecting an EntityManager that I obtained in the rest controller. Posting what I did here for criticism:
The repository code:
#RepositoryRestResource
public interface MyRepo extends CrudRepository<MyObject, Long> {
default List<MyObject> findByRuntimeConditions(EntityManager em, String runtimeConditions) {
String mySql = "<built my sql here. Watch for sql injection.>";
List<MyObject> list = em.createQuery(mySql).getResultList();
return list
}
}
The Rest controller code:
#RestController
public class DataController {
#Autowired
EntityManager em;
// of course watch for sql injection
#RequestMapping("myobjects/{runtimeConditions}")
public List<MyObject> getMyObjects(#PathVariable String runtimeConditions) {
List<MyObject> list = MyRepo.findByRuntimeConditions(em, runtimeConditions);
return list;
}
}

Spring Boot WebFlux Converter

I am trying to migrate my project from the Spring MVC to the Spring WebFlux.
The repository I am currently using is ReactiveCrudRepository.
In order to achieve the post-redirect-get pattern, which I have used within Spring MVC, I need to rewrite the current converter to work with ReactiveCrudRepository.
I was trying to do that with this aproach:
#Component
public class ObjByIdConverter implements Converter<String, Obj> {
#Autowired
private IObjRepository objRepository;
#Override
public Obj convert(String id) {
return objRepository.findById(id).block();
}
}
When I implement converter in this way, I am getting the following error:
block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-xxx.
When I was using CrudRepository instead of ReactiveCrudRepository everything was worked fine.
Is there a way to implement converter to work with ReactiveCrudRepository?
~~~ Edit 1 ~~~
The controller class:
#PostMapping
public Mono<String> processOrder(#ModelAttribute("newCar") Car car) {
webDataBinder.validate();
BindingResult bindingResult = webDataBinder.getBindingResult();
if (bindingResult.hasErrors()) {
return Mono.just("orderForm");
}
return this.carRepository.save(car).thenReturn("redirect:/");
}
The model class:
#Document(collection = "cars")
#ToString
#EqualsAndHashCode
public class Car {
#Id
private String id;
private List<Obj> objs = new ArrayList<>();
// constructor, getters, setters, ...
}
I am using the Thymeleaf view technology.
I have to provide the implementation for ObjByIdConverter because I am getting the following error message: [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'objs'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.app.model.Obj' for property 'objs[0]': no matching editors or conversion strategy found]
You should not use block in any case in reactive development. If you have ReactiveRepository and Spring Webflux, use them together with Mono/Flux from repository to controller to leverage the reactive way of doing.
But I think the main reason why you try to convert the result to a standard type is for the post-redirect-get pattern, could you detail this in the spring controller context ?

Why is this method in a Spring Data repository considered a query method?

We have implemented an application that should be able to use either JPA, Couchbase or MongoDB. (for now, may increase in the future). We successfully implemented JPA and Couchbase by separating repositories for each e.g. JPA will come from org.company.repository.jpa while couchbase will come from org.company.repository.cb. All repository interfaces extends a common repository found in org.company.repository. We are now targeting MongoDB by creating a new package org.company.repository.mongo. However we are encountering this error:
No property updateLastUsedDate found for type TokenHistory!
Here are our codes:
#Document
public class TokenHistory extends BaseEntity {
private String subject;
private Date lastUpdate;
// Getters and setters here...
}
Under org.company.repository.TokenHistoryRepository.java
#NoRepositoryBean
public interface TokenHistoryRepository<ID extends Serializable> extends TokenHistoryRepositoryCustom, BaseEntityRepository<TokenHistory, ID> {
// No problem here. Handled by Spring Data
TokenHistory findBySubject(#Param("subject") String subject);
}
// The custom method
interface TokenHistoryRepositoryCustom {
void updateLastUsedDate(#Param("subject") String subject);
}
Under org.company.repository.mongo.TokenHistoryMongoRepository.java
#RepositoryRestResource(path = "/token-history")
public interface TokenHistoryMongoRepository extends TokenHistoryRepository<String> {
TokenHistory findBySubject(#Param("subject") String subject);
}
class TokenHistoryMongoRepositoryCustomImpl {
public void updateLastUsedDate(String subject) {
//TODO implement this
}
}
And for Mongo Configuration
#Configuration
#Profile("mongo")
#EnableMongoRepositories(basePackages = {
"org.company.repository.mongo"
}, repositoryImplementationPostfix = "CustomImpl",
repositoryBaseClass = BaseEntityRepositoryMongoImpl.class
)
public class MongoConfig {
}
Setup is the same for both JPA and Couchbase but we didn't encountered that error. It was able to use the inner class with "CustomImpl" prefix, which should be the case base on the documentations.
Is there a problem in my setup or configuration for MongoDB?
Your TokenHistoryMongoRepositoryCustomImpl doesn't actually implement the TokenHistoryRepositoryCustom interface, which means that there's no way for us to find out that updateLastUsedDate(…) in the class found is considered to be an implementation of the interface method. Hence, it's considered a query method and then triggers the query derivation.
I highly doubt that this works for the other stores as claimed as the code inspecting query methods is shared in DefaultRepositoryInformation.

Resources