Additional methods to the generated Panache REST Data resource using repository pattern - quarkus

I'm using Quarkus with Hibernate Reactive and Panache REST Data. I'm using the Repository pattern. I would like to use the PanacheRepositoryResource and add a custom endpoint that calls a method in the EntityRepository, but I don't know how to inject the entityRepository since it is an interface.
#ResourceProperties
public interface EntityResource extends PanacheRepositoryResource<EntityRepository, Entity, Long> {
#GET
#Path("/customMethod")
default Uni<List<Entity>> repositoryMethod() {
return entityRepository.customMethod(); // <-- How can I inject my repository?
}
}
Any ideas?

You can do something like this:
#ResourceProperties
public interface EntityResource extends PanacheRepositoryResource<EntityRepository, Entity, Long> {
#GET
#Path("/customMethod")
default Uni<List<Entity>> repositoryMethod() {
return CDI.current().select(EntityRepository.class).get().customMethod();
}
}

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.

Spring's couchbase JPA repository with abstract class fails to find entity

We are developing a project in Springboot that uses a Couchbase, I have following classes:
public abstract class Content {
...
}
public class Film extends Content {
...
}
public class Serie extends Content {
...
}
Then I have following JPA repository:
public interface ContentJpaRepository extends ReactiveCouchbaseSortingRepository<Content> {
}
Then, when I save a content (film or serie) the content is successfully saved, however, the _class field gets the simple class name (instead of the full package name).
Then, when doing:
repository.findById(id);
The repository fails as it can't deserialize the json document to the expected entity. How could I achieve that?
Thank you very much
Using a generic repository is currently not supported for Couchbase Spring Data, as the _class attribute will refer to the abstract class instead of its implementations.

How to separate PATCH and PUT swagger documentation for Spring Data Rest?

I use Spring Data Rest + Springfox (swagger). I want to provide documentation separately for PATCH and PUT but they are both mapped to one function call:
#RepositoryRestResource
public interface OrderRepository extends JpaRepository<Order, Long> {
#ApiOperation(value = "Create new order")
Order save(Order order);
}
How do I separate it into two function calls? I don't have a controller because SDR generates it.
If you need two different API documentations for each request method then you also need two separate endpoints. Use the default method feature for interfaces to enable desired functionality.
#RepositoryRestResource
public interface OrderRepository extends JpaRepository<Order, Long> {
// this method is hidden for Swagger
Order save(Order order);
#PatchMapping
#ApiOperation(value = "Partialy modify order")
default Order modify(Order order) {
return save(order);
}
#PutMapping
#ApiOperation(value = "Create new or replace existing order")
default Order update(Order order) {
return save(order);
}
}

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.

Using QueryDslRepositorySupport in combination with interface repositories

since I didn't get a reply on the spring forum I'll give it a try here.
Is there a way to have a common interface repository which is extended by interfaces the following way:
#NoRepositoryBean
public interface CommonRepository<T> extends JpaRepository<T, Long>, QueryDslPredicateExecutor<T> {
T getById(final long id);
}
#Repository
public interface ConcreteRepository extends CommonRepository<ConcreteEntity> {
List<ConcreteEntity> getByNameAndAddress(final String name, final String address);
}
public class ConcreteRepositoryImpl extends QueryDslRepositorySupport implements ConcreteRepository {
private BooleanExpression nameEquals(final QConcreteEntity entity, final String name) {
return entity.eq(name);
}
public List<ConcreteEntity> getByNameAndAddress(final String name, final String address) {
QConcreteEntity entity = QConcreteEntity.concreteEntity;
return from(entity).where(entity.name.eq(name).and(entity.address.eq(address))).list(entity);
}
}
The problem with the implementation is that I have to implement getById(final long id)
in each concrete class. I don't want to do that. Normally, spring data automatically knows about each entity. Also I want to have the functionality of QueryDslRepositorySupport.
In my example it normally generates something like:
select .. from concreteentity en where en.id = ...
Is there a way to solve it? I already stumbled upon
Spring Jpa adding custom functionality to all repositories and at the same time other custom funcs to a single repository
and
http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.custom-implementations
but I don't think these solutions are helpful and I don't entirely understand how I can use them to solve the problem.
Thanks,
Christian
One way to create a generic getById under QuerydslRepositorySupport is like this
T getById(long id) {
return getEntityManager().find(getBuilder().getType(), id)
}

Resources