Custom repository and specification - spring-boot

in a spring boot 3, i created a custom repository
#Repository
public class BookRepositoryCustomImpl implements BookRepositoryCustom {
}
public interface BookRepositoryCustom {
List<Book> searchBook(SearchBook searchBook);
}
#Repository
#Transactional(readOnly = true)
public interface BookRepository extends JpaRepository<Book, Long> {
}
I search to use specification in searckBook method
Edit with spring 2.x it was possible
#Repository
public class AirportRepositoryImpl extends SimpleJpaRepository<Airport, Integer>
implements AirportRepositoryCustom {
#Autowired
public AirportRepositoryImpl(EntityManager em) {
super(Airport.class, em);
}
#Override
public Page<Airport> advancedSearch(AirportSearch search, Pageable page) {
Specification<Airport> specification = (Root<Airport> root, CriteriaQuery<?> cq,
CriteriaBuilder cb) -> {
Predicate p = cb.conjunction();
...
return p;
};
return this.findAll(specification, page);
}
}

You can't use the JpaSpecificationExecutor from within your custom method implementation.
But a Specification just creates a Predicate from a Root, a CriteriaQuery, and a CriteriaBuilder.
You can inject the EntityManager into BookRepositoryCustomImpl, and use the Criteria API to create a query including using the Predicate from a Specification.

Related

Spring Boot & Data Repositories - fragment implementation x does not implement x

I upgraded from Spring Boot v.1.5.8 to v.2.1.5. When I try to start the application I get the following error:
IllegalArgumentException: Fragment implementation .OtmUniParentRepository2019GeneratedImpl$$EnhancerBySpringCGLIB$$cdf9e294 does not implement x.OtmUniParentRepository2019Generated!
Why I can't start it anymore?
The files:
OtoUniChildRepository2019
#RepositoryRestResource(collectionResourceRel = "OtoUniChild2019", path = "OtoUniChild2019")
public interface OtoUniChildRepository2019 extends OtoUniChildRepository2019Generated {
}
#Transactional
class OtoUniChildRepository2019Impl extends HeartcoreRepositoryImpl<OtoUniChild> {
#PostConstruct
private void setIni() {
super.setIni(OtoUniChild.TABLENAME, OtoUniChild.getColumnName(), OtoUniChild.class, "AGRIDB2019");
}
}
OtoUniChildRepository2019Generated
public interface OtoUniChildRepository2019Generated extends HeartcoreRepository<OtoUniChild> {
OtoUniChild findByIdAndOtoUniParentIsNotNull(#Param("id") String id);
OtoUniChild findByOtoUniParentId(#Param("id") String id);
}
#Transactional
class OtoUniChildRepository2019GeneratedImpl extends HeartcoreRepositoryImpl<OtoUniChild> {
#PostConstruct
private void setIni() {
super.setIni(OtoUniChild.TABLENAME, OtoUniChild.getColumnName(), OtoUniChild.class, "AGRIDB2019");
}
}
HeartcoreRepository
#NoRepositoryBean
public interface HeartcoreRepository<T extends Heartcore> extends RevisionRepository<T, String, Integer>, PagingAndSortingRepository<T, String>, HeartcoreCustomRepository<T> {
#Override
T findOne(String id);
boolean existsById(String id);
#Override
Collection<T> findAll();
List<T> findAllByKanton(#Param("kanton") String kanton);
}
HeartcoreCustomRepository
public interface HeartcoreCustomRepository<T extends Heartcore> {
List<T> findCustom(String sqlQuery);
List<T> findCustom(String select, String where);
Class<T> getType();
T findOne(String id);
Collection<T> findAll();
String getSequence(String sequenceName);
}
HeartcoreCustomRepositoryImpl
#Transactional
public class HeartcoreRepositoryImpl<T extends Heartcore> implements HeartcoreCustomRepository<T> {
#PersistenceContext
protected EntityManager entityManager;
// irrelevant code
public void setIni(String tablename, List<String> columns, Class<T> type, String schema) {
this.tablename = tablename;
this.columns = columns;
this.type = type;
this.schema = schema;
MultitenantDataSource multitenantDataSource = (MultitenantDataSource) entityManager.getEntityManagerFactory().getProperties().get("hibernate.connection.datasource");
DataSource dataSource = (DataSource) multitenantDataSource.determineTargetDataSource();
try {
this.dbDriver = dataSource.getConnection().getMetaData().getDriverName();
}
catch (SQLException e) {
e.printStackTrace();
}
}
// irrelevant code
With 1.5.8 it works fine and I couldn't find infos about breaking changes.
EDIT: Is something wrong with this inheritance structure of the repositories? I tried some different approaches but none worked. Is there an other way to implement some basic functionality for repositories?

Creating custom functions in Spring Boot services

I am writing a simple Spring Boot Application.I'm creating a repository,then its service and then its implementation
The code works fine if I use inbuilt functions of the JPA repository.
However it throws error if I try to make a function in the service interface.
If I make that function in the repository it doesn't throw that error
Here is the code:
Repository:
#Transactional
public interface Local_Repository extends JpaRepository<LocalModel,Long> {
}
Service:
public interface Local_Service {
public List<LocalModel> findAll();
public LocalModel findById(Long id);
public LocalModel findBymo(String mo);//this is the function I added
}
Implementation:
#Service
public class Local_Impl implements Local_Service {
#Autowired
private Local_Repository repository;
#Override
public List<LocalModel> findAll() {
List<LocalModel> cities = (List<LocalModel>) repository.findAll();
return cities;
}
#Override
public LocalModel findById(Long id) {
LocalModel city = repository.findOne(id);
return city;
}
#Override //this throws error
public LocalModel findBymo(String mo) {
LocalModel city=repository.findBymo(mo);
return null;
}
}
In the service you are calling a function that doesn't exist in the repository layer. There is no method called findBymo in the repository. If LocalModel has a field called mo, you can just add a function in the repository interface like this
LocalModel findByMo(String mo);
and it will be implemented automatically.
If LocalModel doesn't have such field you should implement the query yourself in the repository like this
#Query("select ... query here")
LocalModel findByMo(String mo);

QueryDslMongoRepository for different collections

I am trying to implement a QueryDslMongoRepository for a model "Document"
#QueryEntity
#Document(collection="currentDocuments")
public class DocumentImpl extends TranslatableObjectImpl implements Document
In our current implementation a to be deleted document moves von "currentDocuments" into "deletedDocuments" collection.
I cant find a solution to create a repository like this
public interface DocumentRepository extends MongoRepository<DocumentImpl, String> ,QueryDslPredicateExecutor<DocumentImpl> {}
with a dynamic collection name.
My goal is to have the advantages of queryDsl in one Repository for different collections and to be able to move models from one collection into another like
public move(DocumentImpl entity, String sourceCollection, String targetCollection){
repository.delete(entity,sourceCollection);
repository.save(entity,targetCollection);
}
or something like
public List<Document> findAllDocumentsWithAttachments(String collectionName){
return repository.findAll(QDocumentImpl.documentImpl.attachments.isNotEmpty(), collectionName);
}
Any suggestions?
I implemented this feature by creating an own FactoryBean extending MongoRepositoryFactoryBean.
According to the Answer of this -> Question <- I implemeted following solution.
Entity
#QueryEntity
public class Document extends AbstractObject {
}
Custom QuerydslMongoRepository
public interface CustomQuerydslMongoRepository<T extends AbstractObject,ID extends Serializable> extends MongoRepository<T, ID> ,QueryDslPredicateExecutor<T>{
List<T> findAll(Predicate predicate, String collectionName);
T save(T entity, String collectionName);
...
}
Custom QuerydslMongoRepository Implementation
public class CustomQuerydslMongoRepositoryImpl<T extends AbstractObject,ID extends Serializable> extends QueryDslMongoRepository<T,ID> implements CustomQuerydslMongoRepository<T,ID> {
//All instance variables are available in super, but they are private
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final EntityPath<T> path;
private final PathBuilder<T> pathBuilder;
private final MongoOperations mongoOperations;
public CustomQuerydslMongoRepositoryImpl(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations) {
this(entityInformation, mongoOperations,DEFAULT_ENTITY_PATH_RESOLVER);
}
public CustomQuerydslMongoRepositoryImpl(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations, EntityPathResolver resolver) {
super(entityInformation, mongoOperations, resolver);
this.path=resolver.createPath(entityInformation.getJavaType());
this.pathBuilder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.mongoOperations=mongoOperations;
}
#Override
public List<T> findAll(Predicate predicate, String collectionName) {
MongodbQuery<T> query = createQueryFor(predicate,collectionName);
return query.list();
}
#Override
public T save(T entity,String collectionName){
Assert.notNull(entity, "Entity must not be null!");
mongoOperations.save(entity, collectionName);
return entity;
}
private MongodbQuery<T> createQueryFor(Predicate predicate,String collectionName) {
Class<T> domainType = getEntityInformation().getJavaType();
MongodbQuery<T> query = new SpringDataMongodbQuery<T>(getMongoOperations(), domainType,collectionName);
return query.where(predicate);
}
}
Custom Repository Factory
public class CustomQueryDslMongodbRepositoryFactoryBean<R extends QueryDslMongoRepository<T, I>, T, I extends Serializable> extends MongoRepositoryFactoryBean<R, T, I> {
#Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new CustomQueryDslMongodbRepositoryFactory<T,I>(operations);
}
public static class CustomQueryDslMongodbRepositoryFactory<T, I extends Serializable> extends MongoRepositoryFactory {
private MongoOperations operations;
public CustomQueryDslMongodbRepositoryFactory(MongoOperations mongoOperations) {
super(mongoOperations);
this.operations = mongoOperations;
}
#SuppressWarnings({ "rawtypes", "unchecked" })
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new CustomQuerydslMongoRepositoryImpl(getEntityInformation(metadata.getDomainType()), operations);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return CustomQuerydslMongoRepository.class;
}
}
}
Entity Repository
public interface DocumentRepository extends CustomQuerydslMongoRepository<Document, String>{
}
Usage in Service
#Autowired
DocumentRepository repository;
public List<Document> getAllDocuments(Predicate predicate){
return repository.findAll(predicate,"myCustomCollection");
}

How to add cache feature in Spring Data JPA CRUDRepository

I want to add "Cacheable" annotation in findOne method, and evict the cache when delete or happen methods happened.
How can I do that ?
virsir, there is one more way if you use Spring Data JPA (using just interfaces). Here what I have done, genereic dao for similar structured entities:
public interface CachingDao<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
#Cacheable(value = "myCache")
T findOne(ID id);
#Cacheable(value = "myCache")
List<T> findAll();
#Cacheable(value = "myCache")
Page<T> findAll(Pageable pageable);
....
#CacheEvict(value = "myCache", allEntries = true)
<S extends T> S save(S entity);
....
#CacheEvict(value = "myCache", allEntries = true)
void delete(ID id);
}
I think basically #seven's answer is correct, but with 2 missing points:
We cannot define a generic interface, I'm afraid we have to declare every concrete interface separately since annotation cannot be inherited and we need to have different cache names for each repository.
save and delete should be CachePut, and findAll should be both Cacheable and CacheEvict
public interface CacheRepository extends CrudRepository<T, String> {
#Cacheable("cacheName")
T findOne(String name);
#Cacheable("cacheName")
#CacheEvict(value = "cacheName", allEntries = true)
Iterable<T> findAll();
#Override
#CachePut("cacheName")
T save(T entity);
#Override
#CacheEvict("cacheName")
void delete(String name);
}
Reference
I solved the this in the following way and its working fine
public interface BookRepositoryCustom {
Book findOne(Long id);
}
public class BookRepositoryImpl extends SimpleJpaRepository<Book,Long> implements BookRepositoryCustom {
#Inject
public BookRepositoryImpl(EntityManager entityManager) {
super(Book.class, entityManager);
}
#Cacheable(value = "books", key = "#id")
public Book findOne(Long id) {
return super.findOne(id);
}
}
public interface BookRepository extends JpaRepository<Book,Long>, BookRepositoryCustom {
}
Try provide MyCRUDRepository (an interface and an implementation) as explained here: Adding custom behaviour to all repositories. Then you can override and add annotations for these methods:
findOne(ID id)
delete(T entity)
delete(Iterable<? extends T> entities)
deleteAll()
delete(ID id)

hibernate tools & hibernate template in DAO

I used Hibernate tools Eclipse plugin to generate the DAO implementation but it uses a "javax.persistence.EntityManager". I want to generate a DAO impl based on my mapped entity(using JPA annotations) that use a Hibernate Template provided by Spring Framework. How can I do this?
This is how we used to generated DAO objects based with generic implementation of DAO Impl by using Hibernate Template provided by Spring Framework.
Generating DAO java class with ftl,And sample looks like
#Repository("hrdb.UserDao")
public class UserDao extends WMGenericDaoImpl <User, Integer> {
#Autowired
#Qualifier("hrdbTemplate")
private HibernateTemplate template;
public HibernateTemplate getTemplate() {
return this.template;
}
}
And this is how our generic DAO impl looks like
public abstract class WMGenericDaoImpl<Entity extends Serializable, Identifier extends Serializable> implements WMGenericDao<Entity, Identifier> {
private Class<Entity> entityClass;
public abstract HibernateTemplate getTemplate();
#SuppressWarnings("unchecked")
#PostConstruct
public void init() {
if (getTemplate() == null)
throw new RuntimeException("hibernate template is not set.");
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class<Entity>) genericSuperclass.getActualTypeArguments()[0];
}
public Entity create(Entity entity) {
Identifier identifier = (Identifier) getTemplate().save(entity);
return findById(identifier);
}
public void update(Entity entity) {
getTemplate().update(entity);
getTemplate().flush();
}
public void delete(Entity entity) {
getTemplate().delete(entity);
}
public Entity findById(Identifier entityId) {
return getTemplate().get(entityClass, entityId);
}
}
Ofcourse here is WMGenericDao Interface,
public interface WMGenericDao<Entity, Identifier> {
Entity create(Entity entity);
void update(Entity entity);
void delete(Entity entity);
Entity findById(Identifier entityId);
}
And HibernateTemplate is declared in spring.xml.
With this kind of implementation we are able to generate All DAO class in the reverse engineering process by using hibernate template provided by spring.

Resources