How to override Spring Data JPA repository base methods? - java

I have some entity type that needs additional logic on saving (to be precise, I want to save position at the moment of saving). I don't want to do it with any DB-specific features, like triggers, because I'm not sure what will be the data storage used in future.
So I would like to override save() method.
In Spring Data JPA documentation I can see two ways of providing own implementation for repository classes:
Extend base repository class and tell Spring Data to use it.
Defining an interface (in my case I assume PositionedRepository) with an implementation class (PositionedRepositoryImpl).
Problem with first way - I don't want to implement it for all repositories, only two entity types are positioned.
Problem with second way - I don't have access to base repository methods, so apart from position calculation I would need to somehow build all of the queries, normally provided by base repository.
Any way to extend base repository class just for specific repository types?

Don't do that logic in the repository itself. Think about repositories as a dumb layer between java and the database. It just passes data from end to the other.
Instead you should handle that case in a different layer. A more intelligent one. The business logic layer.
See this example:
#Service
public class MyEntityService{
private final MyEntityRepository myEntityRepository;
private final OtherEntityRepository otherEntityRepository;
#Autowired
public MyEntityService(MyEntityRepository myEntityRepository,
OtherEntityRepository otherEntityRepository){
this.myEntityRepository = myEntityRepository;
this.otherEntityRepository = otherEntityRepository;
}
public void save(MyEntity myEntity){
// do stuff with otherEntityRepository
myEntitiyRepository.save(myEntity);
}
}

you can :
public class CustomJpaRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {
private final JpaEntityInformation<T, ?> entityInformationWrap;
private final EntityManager emWrap;
public CustomJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
entityInformationWrap=entityInformation;
emWrap=entityManager;
}
#Override
public <S extends T> S save(S entity) {
//doing
}
}
then main class add:
#EnableJpaRepositories(repositoryBaseClass = CustomJpaRepository.class)

As third option you can extend SimpleJpaRepository that implements JpaRepository and JpaSpecificationExecutor.
In this way, you could benefit from the default implementation of JpaRepository while being the ability to override these methods.
For example :
#Repository
public class PositionedRepository extends SimpleJpaRepository<Positioned, Long> {
#Override
public Positioned save(Positioned positioned) {
...
}
}
As fourth option you can also define your own savePositioned() method that uses under the hood the JpaRepository.save().

Related

Generic Repository within DDD: How can I make this interface generic?

I'm developing a multi-module CMS application following Domain-Driven Design principles. I'm trying to figure out how to implement Generic Repository, thus avoiding a lot of boiler-plate code.
The idea is to have a "two-way" mapping strategy (model to entity and vice versa) and Generic Repository implemented in the Persistence module. Further, an interface in the Domain module would act as a contract between Domain and Persistence, so I can use it for later injection in the other layers.
How can I make this interface generic?
To be specific, the problem here is the mapping. Since I'm using a "two-way" mapping strategy, the Domain module has no idea about DB specific entities.
Is there a way to map generic type models between layers? Or use some other mapping strategy while keeping the layers loosely coupled?
Here is a code example to clarify what I'm trying to achieve.
This would be the code example for Generic Repository:
#MappedSuperclass
public abstract class AbstractJpaMappedType {
…
String attribute
}
#Entity
public class ConcreteJpaType extends AbstractJpaMappedType { … }
#NoRepositoryBean
public interface JpaMappedTypeRepository<T extends AbstractJpaMappedType>
extends Repository<T, Long> {
#Query("select t from #{#entityName} t where t.attribute = ?1")
List<T> findAllByAttribute(String attribute);
}
public interface ConcreteRepository
extends JpaMappedTypeRepository<ConcreteType> { … }
Further, I want to make my own Custom Repository to be able to do some mapping of model to entity and vice versa, so I wouldn't have JPA specific annotation in my domain classes, thus making it loosely coupled. I want this Custom Repository to implement an interface from Domain module, allowing me to inject it later in the Services layer.
public class CustomRepositoryImpl implements CustomRepository {
public final JpaMappedTypeRepository<T> repository;
...
}
How can I make this class and this interface generic so that I would be able to do mapping between model and entity, since Domain layer has no information about entity classes?
I figured it out eventually.
The problem, as it was stated in the question, was the mapping between layers. I created a mapping interface to declare mapping methods. I used #ObjectFactory annotation from MapStruct to deal with generic mapping (look here):
public interface EntityMapper<M, E> {
M toModel(E entity);
List<M> toModelList(List<E> entities);
E toEntity(M model);
List<E> toEntityList(List<M> models);
// default object factory methods
}
Then I proceeded with creating a mapper for each of the child classes and extending it with the EntityMapper interface with concrete types that I want to map.
#Mapper(componentModel="spring")
public interface ConcreteEntityMapper extends EntityMapper<ConcreteModelType, ConcreteJpaType> {
}
I created an abstract class where I injected the JPA repository and the mapper, and also implemented common methods.
abstract class CustomRepositoryImpl<T extends AbstractModelMappedType, E extends AbstractJpaMappedType> {
private final JpaMappedTypeRepository<E> repository;
private final EntityMapper<M, E> mapper;
//... common methods for mapping and querying repositories.
}
Then I extended a ConcreteTypeRepositoryImpl with this abstract class, and implemented a generic interface, which I can later use as a reference in other layers.
public interface CustomRepository<M> {
M saveOrUpdate(M model);
Optional<M> getById(Long id);
List<M> getByName(String name);
List<M> getAll();
void delete(Long id);
}
#Component
public class ConcreteTypeRepositoryImpl extends CustomRepositoryImpl<ConcreteModelType,ConcreteJpaType> implements CustomRepository<ConcreteModelType> {
public ConcreteTypeRepositoryImpl(JpaMappedTypeRepository<ConcreteJpaType> repository,
EntityMapper<ConcreteModelType, ConcreteJpaType> mapper) {
super(repository, mapper);
}
}
And that would be it. Now I can inject CustomRepository into other layers and hit desired repository.

How can I use generics in Java to work with JPA repositories much easier?

I have a web component called Select. It's a drop down box in Vaadin framework.
I want to construct Select drop down boxes at start up and they are going to contain data from repositories from JPA in Spring Boot.
Here is my example code:
private <T extends UserLoggRepository> void addSelect(Select<Long> select, String placeHolder, T repository) {
List<UserLogg> userLoggers = repository.findAll();
List<Long> items = new ArrayList<Long>();
for(int i = 0; i < userLoggers.size(); i++) {
long value = userLoggers.get(i).getLoggerId();
if(items.contains(value) == false)
items.add(value);
}
if(items.contains((long) 0) == false)
items.add((long) 0);
select.setItems(items);
select.setPlaceholder(placeHolder);
}
This code works! But I have different entities and different repositories.
I have the entities:
UserLogg
DataLogg
CalibrationLogg
AlarmLogg
And I have the repositories:
UserLoggRepository
DataLoggRepository
CalibrationLoggRepository
AlarmLoggRepository
Question:
How can I change the method addSelect so they will work with any respository that are connected to an entity? All repositories have the standard function findAll() and all entities have the field loggerId.
Focus:
What I have problem with is that this code only works for the entity UserLogg and repository UserLoggRepository.
List<UserLogg> userLoggers = repository.findAll();
What need to be done:
This need to change, but I don't know what to write here so it become more general for them all.
private <T extends UserLoggRepository> void addSelect
Update 1:
A repository header in JPA looks e.g like this:
public interface AlarmLoggRepository extends JpaRepository<AlarmLogg, Long>
I could use this, but still, got the UserLogg class is fixed.
private <T extends JpaRepository<UserLogg, Long>> void addSelect(Select<Long> select, String placeHolder, T repository)
If I understood your problem correctly, you can do inheritance and make a parent class for all your existing entities and change the definition of your addSelect method to return that parent class which could eventually return a type of any of it's subclasses.
For example, looks like your existing entities are all logs, so you can have an Abstract class named LoggRepository and have it extended by all your existing entities like:
public class UserLoggRepository extends LoggRepository
And then, update the method addSelect like below so it can return and of the subclasses of your parent abstract class LoggRepository
private <? extends LoggRepository> void addSelect(Select<Long> select, String placeHolder, T repository)
Hope this helps :)
You need a common interface or superclass for all your entities, that ensures they have the getLoggedId() method.
After that, something like this should work:
private void addSelect(Select<Long> select, String placeHolder, JpaRepository<? extends AbstractLogg, Long> repository) {
List<AbstractLogg> loggers = repository.findAll();
...
}

Is it better to use aggregation over inheritance when creating a generic CRUD component?

I have a question. I have these classes:
public interface CRUDService<MODEL extends BaseModel<ID>,ID extends Serializable>
{
List<MODEL> findAll();
MODEL findById(ID id);
// + delete, save & update methods
}
public abstract class AbstractCRUDService<MODEL extends BaseModel<ID>,ID extends Serializable> implements CRUDService<MODEL,ID>
{
//overriding the CRUDService interface methods here.
}
Is it better to extend each service from AbstractCRUDService like this:
public class DefaultProductService extends AbstractCRUDService<ProductModel,Long> implements ProductService
{ //some methods here}
or should I remove abstract from AbstractCRUDService and inject this service in the DefaultProductService ?
public class DefaultProductService implements ProductService {
#Autowired
private CRUDService<ProductModel,Long> crudService;
// override "ProductService" methods here.
}
It depends on your requirement.
If all Model Types, need the same CRUD implementation, you can go with your 2nd approach: composition.
However, if different Model objects require different CRUD implementations, the inheritance would fit better. For example, for all ProductModels Del(obj) will remove the object from the DB table, however, for all OrderModels Del(obj) doesn't remove the data, instead, it does something else, throw an exception, for example.
Yes, it is. You need to prefer composition over inheritance
here a really good post to read about it
composition over inheritance

How to force Spring Data to create query methods with entity runtime type?

I've got around 5 objects that I want to do similar things with.
I figured out that not to polute the code I will put a logic for those objects in one place.
public class MetaObjectController<T extends MetaObject> {
#Autowired
private final MetaObjectRepository<T> repository;
// generic logic
Here's how repository looks:
public interface MetaObjectRepository<T extends MetaObject> extends GraphRepository<T> {
T findByName(String name);
}
Now, I create concrete class which uses delegation:
public class ExperimentalController {
#Autowired
private final MetaObjectController<MetaCategory> metaController;
#RequestMapping(method = RequestMethod.POST)
public void add(#RequestBody MetaCategory toAdd) {
metaController.add(toAdd);
}
Now, when I look at the generated queries I see, that although instantiated correctly, repository puts MetaObject as an entity name instead of runtime type.
Is there a way to force the repository to use runtime type?
Please don't advise to put a #Query annnotation. That's not what I am looking for.
This is most probably due to type erasure: at runtime there is only the type constraint available which is MetaObject. If you want to use (via spring-data) the actually relevant subclass you will have to create explicit interfaces of the MetaObjectRepository like this:
public class Transmogrifier extends MetaObject
public interface MetaTransmogrifierRepository
extends MetaObjectRepository<Transmogrifier> {}

Dao Registry refactoring

Using the generic dao pattern, I define the generic interface:
public interface GenericDao<T extends DataObject, ID extends Serializable> {
T save(T t);
void delete(ID id);
T findById(ID id);
Class<T> getPersistentClass();
}
I then implemented an default GenericDaoImpl implementation to perform these functions with the following constructor:
public GenericDaoImpl(Class<T> clazz) {
this.persistentClass = clazz;
DaoRegistry.getInstance().register(clazz, this);
}
The point of the DaoRegistry is to look up a Dao by the class associating to it. This allows me to extend GenericDaoImpl and overwrite methods for objects that requires special handling:
DaoRegistry.getInstance().getDao(someClass.getClass()).save(someClass);
While it works, there are a few things that I don't like about it:
DaoRegistry is an singleton
The logic of calling save is complicated
Is there a better way to do this?
Edit
I am not looking to debate whether Singleton is an anti-pattern or not.
First of all, what is your problem with DaoRegistry being singleton?
Anyway, you could have an abstract base class for your entities that'd implement save like this
public T save(){
DaoRegistry.getInstance().getDao(this.getClass()).save(this);
}
then you could simply call someEntity.save()
Or it may be more straightforward if the entity classes itself implemented the whole GenericDao interface (save, delete and find methods), so the contents of your GenericDaoImpl would be in the base class of your entities.
It could be better to use instance of DaoRegistry instead of static methods. It would make it more manageable for test configurations. You could implement it as
#Component("daoRegistry")
public class DaoRegistry {
#Autowired
private List<GenericDao> customDaos;
private GenericDao defaultDao = new GenericDaoImpl();
public <T> T getDao(Class<T> clazz) {
// search customDaos for matching clazz, return default dao otherwise
}
}
Also you could add save method to it and rename accordingly. All customised daos should be available as beans.

Categories

Resources