let imagine I have per entity a repository class (spring data jpa) for database access and a service class. The dependencies are managed by spring framework. Every service method does in most cases the same, so there is mainly code duplication:
public class NewsService {
#Inject
private NewsRepository newsRepository;
public void add(News news) {
// do some validation
newsRepository.save(news);
}
}
public class UserService {
#Inject
private UserRepository userRepository;
public void add(User user) {
// do some validation
userRepository.save(user);
}
}
Now i thought about creating an abstract class like this:
public abstract class AbstractService<T> {
private UnknownRepository unknownRepository;
public void add(T entity) {
// do some validation
unknownRepository.save(entity);
}
}
public class NewsService extends AbstractService<News> {
}
public class UserService extends AbstractService<User> {
}
My problem: How can i overwrite the repository used inside the abstract class based on my entities?
You can replace the UnknownRepository field with an abstract method and a type parameter:
// R is the type of the repository
public abstract class AbstractService<T,R extends BaseRepository> {
protected abstract R getRepository();
public void add(T entity) {
getRepository().save(entity);
}
}
And inject the specific repository to the implementations of this class:
public class NewsService extends AbstractService<News, NewsRepository> {
#Inject private NewsRepository newsRepository;
#Override
public NewsRepository getRepository() {
return newsRepository;
}
// the inherited add() method works now
}
Related
I have this Interface:
public interface Test<T> {
default Class<?> getT() {
return T.getClass(); < --error
}
}
next i have a class that implements it:
static class ItemService implements Test<Item>{
}
And i want to get the 'Item' class from the 'ItemService' class
static ItemService service = new ItemService();
private static void k() {
System.out.println(service.getT());
}
Now one way to do it is this:
public interface Test<T> {
default Class<?> getT() {
return Type.type;
}
class Type {
public static Class<?> type;
}
}
Service:
static class ItemService implements Test<Item> {
public ItemService() {
Type.type = Item.class;
}
}
And it works fine but there is a problem,
When another class implement the interface:
static class OrderService implements Test<Order> {
public OrderService() {
Type.type = Order.class;
}
}
And i try:
static ItemService service = new ItemService();
static OrderService orderservice = new OrderService();
private static void k() {
System.out.println(service.getT());
}
I get the Order class and not the Item class
How can i make it work?
Classes inside interfaces are static, You can remove the default from the function and every class will need to implement this. example:
public interface Test<T> {
public Class<T> getT();
}
static class ItemService implements Test<Item> {
public Class<Item> getT() {return Item.class;}
}
static class OrderService implements Test<Order>{
public Class<Order> getT() {return Order.class;}
}
An alternative could be an abstract class.
public interface Test<T> {
public Class<T> getT();
}
abstract class AbstractTest<T> implements Test<T> {
private final Class<T> type;
AbstractItemService(Class<T> type) { this.type = type }
public Class<T> getT() {return type;}
}
class ItemService extends AbstractTest<Item> {
ItemService() { super(Item.class); }
// implement other things
}
class OrderService extends AbstractTest<Order>{
OrderService() { super(Order.class); }
// implement other things
}
Here is another option, if your implementation has an instance of T.
interface Test<T>{
T getT();
default Class<?> getClassOfT(){
return getT().getClass();
}
}
I have two JPA entites like
#Entity
#Table(name = "A")
public class A {...}
#Entity
#Table(name = "B")
public class B {...}
And services like
#Service
public class AServiceImpl implements AService {
#Autowired
private ARepository aRepository;
public void create() {...}
public void update(long id) {...}
public void doA() {...}
}
#Service
public class BServiceImpl implements BService {
#Autowired
private BRepository bRepository;
public void create() {...}
public void update(long id) {...}
public void doB() {...}
}
So, in methods create and update I have code what differs only by repository. I would like to store crud methods in some other, basic service, but do not really understand how could I manage repository inheritance.
Any help, please.
IMP: This code is compiling, I havent tested it.
I think you can use Genarics over here.
public class RepositoryService <R extends JpaRepository<T, Long>, T> {
#Autowired R repository;
public void insert(T object) {
repository.save(object);
}
}
How to effectively use these reporitories, that details you can refer to this baeldung article - https://www.baeldung.com/spring-autowire-generics
Update: This is an alternate approach you can try out
public abstract class RepositoryService <T>{
abstract Class getRepositoryClass();
Map<Class, GenericRepository> repositories;
#Autowired
List<GenericRepository> repositoriesList;
#PostConstruct
public void setupMap() {
// Convert List into Map, with the class of Objest to be saved.
}
public void insert(T object) {
repositories.get(object.getClass()).save(object);
}
}
I'm writing Java program, which interacts with Db via Hibernate.
All my persistent classes extend from common abstract class Entity which implements interface IEntity. For example:
public interface IEntity {
long getId();
void setId(long id);
}
public abstract class Entity implements IEntity {
private long id;
//get + set id
}
public class User extends Entity {
private string name;
//get + set name
}
public class Item extends Entity {
private string description;
//get + set description
}
For operations with Db I created repository classes which extend from Repository<T extends IEntity> with standard CRUD methods for all entities and this class implements interface IRepository<T extends IEntity>:
public interface IRepository<T extends IEntity> {
void create(T object) throws JDBCException;
//other CRUD operations
}
public abstract class Repository<T extends IEntity> implements IRepository<T> {
private final Class<T> entityClass;
protected final EntityManager entityManager;
public Repository(Class<T> entityClass, EntityManager entityManager) {
this.entityClass = entityClass;
this.entityManager = entityManager;
}
#Override
public void create(T object) throws JDBCException {
entityManager.getTransaction().begin();
entityManager.persist(object);
entityManager.getTransaction().commit();
}
//other CRUD operations implementation
}
public class UserRepository extends Repository<User> {
public UserRepository (EntityManager entityManager) {
super(AmountUnit.class, entityManager);
}
}
public class ItemRepository extends Repository<Item> {
public ItemRepository (EntityManager entityManager) {
super(AmountUnit.class, entityManager);
}
}
This structure worked well until I decided to create method to obtain specific repository by its entity class.
I see this method as something like this:
public <T extends IEntity, U extends IRepository<T>> U getByType(T object) {
// code here
}
Let's say, that class User extends Entity and have repository class UserRepository extends Repository<User>
I'm expecting, that this method should return RepositoryforUser object`.
From my point of view this can be achieved in two ways:
Elegant. Create method for IRepository - Class<T> getEntityClass
and then compare classes of input and result of getEntityClass
Stupid. Make many if/else statements inside this method and return repository. if(object instanceof A) return ARepository
public class Storage {
private IRepository<? extends IEntity>[] repositories;
public <T extends IEntity, U extends IRepository<T>> U getByTypeVar1(T object) {
for (IRepository<?> repo : repositories) {
if (object instanceof repo.getEntityClass ()) // cannot resolve getEntityClass
return repo;
}
}
public <T extends IEntity, U extends IRepository<T>> U getByTypeVar2(T object) {
if (object instanceof UserRepository.getEntityClass ())
return UserRepository; //incompatible type
//more if else here
}
}
But both of these implementation are failed to compile. May be you have any ideas how to write this method correctly
You can implement the getByType method like this (I changed the parameter type):
private List<IRepository<? extends IEntity>> repositories;
#SuppressWarnings("unchecked")
public <E extends IEntity> IRepository<E> getByType(Class<E> entityClass) {
for (IRepository<?> repository : repositories) {
if (repository.getEntityClass().equals(entityClass)) {
return (IRepository<E>) repository;
}
}
throw new IllegalArgumentException(
"No repository for entity class " + entityClass.getName());
}
When you post your code that failed to compile, we can figure out where the problem was.
Update (code comments)
You should add the getEntityClass() method to IRepository.
To make the code less complicated, you can replace:
<T extends IEntity, U extends IRepository<T>> U getByType()
with
<T extends IEntity> IRepository<T> getByType getByType()
Using instanceof in
object instanceof repo.getEntityClass ()
can be problematic, since you can have entity hierarchies and you can get a wrong (subclass) repository for an object. If you don't know a class of the object, you can get it by (the object can be a Hibernate proxy):
org.hibernate.Hibernate.unproxy(object).getClass()
and then compare the classes by repository.getEntityClass().equals(entityClass).
I create Beans for my Generic Service with FactoryBean.
Service:
public abstract class SimpleServiceImpl<T extends IdEntity>
implements SimpleService<T> {
#Autowired
SimpleRepository<T> entitiesRepository;
...
Factory:
#Component
public class ServiceFactoryBean<E extends IdEntity> extends ServiceAbstractFactoryBean<E> {
#Override
protected SimpleServiceImpl<E> doCreateInstance() {
return new SimpleServiceImpl<E>() { };
}
#Override
public Class<?> getObjectType() {
return SimpleService.class;
}
}
if I create bean with this factory then SimpleRepository<T> entitiesRepository; eqally SimpleRepostirory<IdEntity> not T class, what I doing wrong?
Example use Factory:
#RestController
#RequestMapping("/users")
public class UserController extends SimpleController<User> {
#Autowired
public UserController(ServiceFactoryBean<User> serviceFactoryBean) throws Exception {
super(serviceFactoryBean.getObject());
}
}
If I replace T to User in ServiceFactoryBean , it work
#Component
public class ServiceFactoryBean<> extends ServiceAbstractFactoryBean<User> {
#Override
protected SimpleServiceImpl<User> doCreateInstance() {
return new SimpleServiceImpl<User>() { };
}
#Override
public Class<?> getObjectType() {
return SimpleService.class;
}
}
I have a controller superclass and its subclass:
public class SuperController {
#Resource
private A resourceA;
}
public class SubController extends SuperController {
#Resource
private B resourceB;
}
I don't use resourceA field in Subcontroller, but Subcontroller acts as SuperController and has the same methods. So how can I forbit the inheritance of #Resource annotation, just like that:
public class SuperController {
**private** #Resource
private A resourceA;
}
Sounds like you need a common abstract class if the sub-class doesn't use some of the fields of the parent class.
public abstract class AbstractController {
// common fields.
// common methods.
}
public class SuperController extends AbstractController {
#Resource
private A resourceA;
}
public class SubController extends AbstractController {
#Resource
private B resourceB;
}