I have few entities in my app and I want to have a functionality where I can generically set few properties on them when user tries to save them.
For this, I have attempted writing a utility class as follows:
public class IrisUserUtil<T> {
IrisUserRepository irisUserRepository;
public IrisUserUtil(IrisUserRepository irisUserRepository){
this.irisUserRepository = irisUserRepository;
}
public <S extends T> S populate(S entity) {
//IrisUser irisuser = irisUserRepository.findOne(entity.getIrisUserId());
//entity.setFirstName(irisuser.getFirstName());
//entity.setLastName(irisuser.getLastName());
return entity;
}
}
This I will call just before my repository.save() ; Here I am looking for correct way to call functions like entity.getIrisUserId() etc on the passed parameter that is "entity". Of course, this is not working.
please suggest why?
maybe with <S extends Entity> and not T what is T here ?
How the compilater know that S is something extending the Entity class (or an other base class) ?
public <S extends T> S populate(S entity)
In your code S and T are somethings extending Object, the compilater can't known your methods getIrisUserId(), getFirstName() etc...
I think you have to remove your generic T in the class and just do
public <S extends Entity> S populate(S entity)
or (but useless and more complicated)
public class IrisUserUtil<T extends Entity> {
and
public <S extends T> S populate(S entity)
or in a simple way no need to use generic types just do (but you have to cast the returned entity with the concrete type)
public Entity populate(Entity entity) {
...
return entity;
}
in my example Entity is your entities abstract base class
Related
Let's say we have three JPA objects with name and id. I made an interface with getters+setters for name and id.
class Car implements MetadataObject
class Bus implements MetadataObject
class Train implements MetadataObject
We also have three repositories for these JPA objects:
interface CarRepository extends CrudRepository<Car, Long>
interface BusRepository extends CrudRepository<Bus, Long>
interface TrainRepository extends CrudRepository<Train, Long>
For each of these objects we want to run the same method in an spring service. (highly simplified)
private void importMetadata(CrudRepository<? extends MetadataObject, String> mRepository) {
Optional<? extends MetadataObject> currentOptional = mRepository.findById(1);
if (currentOptional.isPresent()) {
MetadataObject current = (MetadataObject) currentOptional.get();
current.setName("a1");
mRepository.save(current);
}
}
Which is callable by the same spring service by
#Autowired
private CarRepository carRepository;
...
importMetadata(carRepository);
This results in the error:
The method save(S) in the type CrudRepository<capture#4-of ? extends MetadataObject, Long> is not applicable for the arguments (MetadataObject)
Which is odd if I look at Springs CRUD Repository: CrudRepository<T, ID> and its save method: <S extends T> S save(S entity);.
In our example we have T = ? extends MetadataObject and S = ? extends ? extends MetadataObjects.
If we change my function to private void importMetadata(CrudRepository<MetadataObject, String> bdbRepository) the save method is correct but I can't call the method with my carRepository anymore
The method importMetadata(CrudRepository<MetadataObject,String>) in the type <...> is not applicable for the arguments (CarRepository)
Be aware: I highly simplified the example. I know that in this example Interfaces for these JPA classes makes no sense. I also know that my method makes no sense but it highlights the problem perfectly.
My question would be: What to pass to save or how to rewrite this function that it works? What exactly is the issue here?
You can use this method definition:
private void <T extends MetadataObject>importMetadata(CrudRepository<T, String> mRepository) {
Optional<T> currentOptional = mRepository.findById(1);
if (currentOptional.isPresent()) {
T current = currentOptional.get();
current.setName("a1");
mRepository.save(current);
}
}
I will try the explain the reason the method save of CrudRepository<? extends MetadataObject, String> mRepository fails.
Suppose we have a generic class:
class C<T> {
public void save (T t) {
// .. whatever
}
}
When we write something like:
void f (C<? extends Object> c) {
c.save(new Object());
}
the compiler complains about the c.save line.
That's because, when applying the type restriction, the compile doesn't know whether the c reference points, in fact, to C<Object> or C<Number> or whatever, since both C<Object> and C<Number> are accepted as arguments for f.
Because of that, the compiler doesn't know whether the save method's argument is allowed or not, hence the error.
Tables:
StudentHistory 1--->n Student
TeacherHistory 1--->n Teacher
I try to regroup the JPA behaviour of History because they do the same thing (retrieve the students/teacher from a given history for example).
Entities with generic types:
// Entities
public abstract class AbstractHistory <T> {}
public class StudentHistory extends AbstractHistory<Student> {}
public class TeacherHistory extends AbstractHistory<Teacher> {}
Repositories with genric types:
// repositories
public interface IHistoryRepository<T> extends CrudRepository<AbstractHistory<T>, Long> {
public AbstractHistory<T> findFirst();
}
public interface StudentHistoryRepository extends IHistoryRepository<Student> {}
public interface TeacherHistoryRepository extends IHistoryRepository<Teacher> {}
I though I could do:
StudentHistory stuHisto = new StudentHistoryRepository().findFirst();
But I get this error:
// err -> Type mismatch: cannot convert from AbstractHistory<Student> to StudentHistory
1/ Why can't I retrieve a 'StudentHistory' from my 'StudentHistoryRepository' ?
2/ How should I deal whith that?
You have this problem because your method explicitly returns an AbstractHistory and not the subtype.
You would need to cast...
... if only your repository implementation understood that each T you get a specific history.
You may try adding another type but I fear that it'll fail:
public interface IHistoryRepository<
T,
H extends AbstractHistory<T>
> extends CrudRepository<H, Long> {
public H findFirst();
}
public interface StudentHistoryRepository extends IHistoryRepository<Student, StudentHistory> {}
public interface TeacherHistoryRepository extends IHistoryRepository<Teacher, TeacherHistory> {}
I don't know what framework you are using, probably Spring Data from the names; while I had used it in the past, I don't know if it is able to do that.
After all, it needs to get the concrete class and since it is generics, type erasure may interfere (if the information about the concrete type representing H is lost in reflection then Spring Data won't probably be able to do much here, unless you help it with an annotation or something else).
Another solution that should work is to do that per each child interface instead:
public interface StudentHistoryRepository extends CrudRepository<StudentHistory, Long> {
StudentHistory findFirst();
}
Or with another interface:
public interface FindFirst<T> {
T findFirst();
}
public interface StudentHistoryRepository extends CrudRepository<StudentHistory, Long>, FindFirst<StudentHistory> {}
I have a root class called Entity. I also have service classes that do CRUD operations on a subtypes of Entity. So, my service class definitions follow this pattern
class Entity {
//the root class
}
class Service<T extends Entity> {
//root service class containing common CRUD operations
}
class SomeEntity extends Entity {
}
class SomeService extends Service<SomeEntity> {
}
I maintain a Map of Entity class --> to their corresponding Service like
Map<Class<? extends Entity>, Service<? extends Entity>> = serviceMap
new HashMap<Class<? extends Entity>, Service<? extends Entity>>();
However, the above doesn't enforce that the class and its corresponding Service belong to the same hierarchy. Hence, even after casting, my client code have to cast it again. How to avoid the following ? And enforce a strict relation among the key and value ?
public <T extends Entity> Service<T> get(Class<T> clazz) {
return (Service<T>) serviceMap.get(clazz);
}
//example of client code
SomeService service = (SomeService) serviceRegistry.get(SomeEntity.class);
Unfortunately, you cannot do what you'd like to do. It just isn't possible!
However, you shouldn't really need to know what the Service implementation is though. Service should define provide all the necessary methods for the client code to invoke. Hello polymorphism! Personally, I'd just change your get method slightly to:
Service<? extends Entity> getService(Class<?> entityClass) {
return serviceMap.get(entityClass);
}
Client code shouldn't care too much about the actual implementation:
Service<? extends Entity> service = getService(SomeEntity.class);
service.save(myEntity);
Another option is if you know what the entity type is when you want the service then you can pass in the Service class and use that for the cast, eg:
<T extends Service<? extends Entity>> T getService(Class<?> entityClass, Class<T> serviceClass) {
return serviceClass.cast(serviceMap.get(entityClass));
}
Admittedly, neither of the above methods guarantee that SomeEntity.class is paired with a SomeService instance in the Map.
I'm looking for assistance on how to implement this Repository. Here's what I have so far:
public interface IEntity {
int getId(); //would rather not depend on int. fix later.
}
public interface IRepository<T extends IEntity> {
Collection<T> findAll();
T find(T t);
T findById(int id); //would rather not depend on int. fix later.
void add(T t);
void remove(T t);
}
public interface ISurveyRepository extends IRepository<Survey> {
}
The problem I'm running into is that I need for T in the IRepository signature to extend IEntity, but I don't need IRepository in the ISurveyRepository signature to have a bounded type parameter. I would like for the signature to just be
public interface ISurveyRepository extends IRepository { }
so that I could create a concrete class that just implements ISurveyRepository
public class MySurveyRepository extends ISurveyRepository { }
How can I go about doing that?
You can do better than this.
Fix that int problem now. Use a generic DAO interface, like this:
public interface Repository<T, K extends Serializable> {
List<T> find();
T find(K id);
K save(T value);
void update(T value);
void delete(T value);
}
Lose that Hungarian notation in disguise: no "I" for "interface".
You can write a generic implementation, too.
If you want to create a class like:
public class MySurveyRepository extends ISurveyRepository {}
Then your existing interface (with the use of generics) will do just fine. Your implementing class will 'inherit' the definition by design, and will be (effecticely) completely unaware that it's descended from a previously-generic interface.
If you're using one of the modern editors, like Eclipse, to write your code, when you ask it to fill in the missing inherited methods it won't give you T - it'll give you Survey.
I'm trying to extend a library that uses generics in entities and entities manager, so I have the following:
public class MyEntity extends ParentEntity extends BaseEntity
and
public class MyEntityManager extends ParentEntityManager extends BaseEntityManager<ParentEntity, ParentEntityDao>
Now my problem is, I'm using another class of the library to update the entity, and the method I need to call expects:
public <ENT extends BaseEntity> void update (Class<ENT> entityClass, BaseEntityManager<ENT> entityManager)
So when I try to call the method with MyEntity and MyEntityManager, it fails because my EntityManager extends from the BaseEntityManager with ParentEntity as parameter, not MyEntity, so they don't match.
I would say that the cleanest way to solve this would be to copy the utility class that has the update method and extend it in my project, but I would like to make it generic so I can pass it EntityManagers that use any children class of ParentEntity, but I've been trying for a while and I cannot find the solution.
I tried changing the method signature to this:
public <ENT extends BaseEntity> void update (Class<ENT> entityClass, BaseEntityManager<? extends ENT> entityManager)
but I'm still getting a compiler exception...
EDIT: Modifying ParentEntityManager, BaseEntityManager or ParentEntity is not possible, since there are too many dependencies to those classes
Since it doesn't appear you can easily fix the problem I would use a work around. You can use type erasure.
update((Class) MyEntity.class, myEntityManager);
This will compile with a warning. The warning is valid IMHO as your class structure isn't entirely logical ;) You can suppress this warning if you want.
What I do is; the container class returns the type of object it contains/manages.
class BaseEntityManager<E> {
private final Class<E> typeManaged;
public Class<E> typeManaged() { return typeManaged; }
}
// remove duplicate class parameter
public <ENT extends BaseEntity> void update (BaseEntityManager<ENT> entityManager)
The manager know what type it manages and the update() method can ask it by calling typeManaged(). This avoid having to give a matching type or for this type to be checked.
The problem you have is that
MyEntityManager extends BaseEntityManager<ParentEntity>
Which mean the class you have to provide is ParentEntity.class not MyEntity.class.
It appears the real solution is that you need to change
MyEntityManager extends BaseEntityManager<MyEntity>
This would be more logical IMHO and fix your problem.
You need to call the update method with ParentEntity.class and a MyEntityManager, e.g.
update(ParentEntity.class, new MyEntityManager());
because your MyEntityManager is a BaseEntityManager<ParentEntity>.
If this is not what you want (because for example update creates new objects of entityClass and you want them to be MyEntity instances), you need to change the signature of the update method and introduce wildcards:
public <ENT extends BaseEntity> void update (Class<? extends ENT> entityClass, BaseEntityManager<ENT> entityManager)
Now you can call it with MyEntity.class and a MyEntityManager.
I think if you look replace the Manager with Container then the problem becomes apparent: MyEntityManager manages all sorts of super ParentEntity classes, while the update method wants one catering specifically to the subclass.
Does the following work for you? It compiles but lint complains
class BaseEntity {}
class BaseEntityManager<ENT extends BaseEntity> {}
class ParentEntity extends BaseEntity {}
class ParentEntityManager extends BaseEntityManager<ParentEntity> {}
class MyEntityManager extends ParentEntityManager { public void update(ParentEntity e) {}}
class MyEntity extends ParentEntity {}
class XEntityManager<ENT extends ParentEntity> extends BaseEntityManager<ENT>
{
MyEntityManager _delegate;
XEntityManager(MyEntityManager m)
{
_delegate = m;
}
public void update(ENT e)
{
_delegate.update(e);
}
}
class Foo {
public <ENT extends BaseEntity> void update (Class<ENT> entityClass, BaseEntityManager<ENT> entityManager) {}
void bar(MyEntity e, MyEntityManager m)
{
update(MyEntity.class, new XEntityManager(m));
}
}