Java generic, returning concrete instance issue - java

Please help. I can not deal with generics. I have:
Generic interface repository:
#NoRepositoryBean
public interface PageBaseRepository<T extends PageBase> extends CrudRepository<T, Long> {
List<T> findAllByUserId(UUID userId);
}
#Repository
public interface StandardPageRepository extends PageBaseRepository<StandardPageEntity> {
}
#Repository
public interface BlockPageRepository extends PageBaseRepository<BlockPageEntity> {
}
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class PageBaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private UUID userId;
private String content;
#Entity
public class StandardPageEntity extends PageBaseEntity {
private String extraField;
}
#Entity
public class BlockPageEntity extends PageBaseEntity {
private String blockPosition;
}
#Component
#RequiredArgsConstructor
public class PageRepositoryFactory {
private final StandardPageRepository standardPageRepository;
private final BlockPageRepository blockPageRepository;
public <R extends PageBaseRepository<E>, E extends PageBaseEntity> R getRepository(final E entity) {
if (entity instanceof StandardPageEntity) {
return standardPageRepository;
}
if (entity instanceof BlockPageEntity) {
return blockPageRepository;
}
throw new IllegalArgumentException("Not recognised pension object " + pensionEntity.getClass());
}
}
I am getting hint in intelij:
Incompatible types. Required: R Found:StandardPageRepository
and error in console:
PageRepositoryFactory.java:15: error: incompatible types:
StandardPageRepository cannot be converted to R
return standardPageRepository;
^ where R,E are type-variables:
R extends PageBaseRepository declared in method getRepository(E)
E extends PageBaseEntity declared in method getRepository(E)
One thing I found out is that I can do casting, but this is ugly, compilator says it is Unchecked cast. And I do not wanna this warning.

Related

Retrive repository by entity type in Java

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).

Why I get this type error with generics in annotations?

I have this annotation:
public #interface ModelType {
Class<? extends ValueFilter<?, ?>> value();
}
and this two classes:
public class UserIntermediateModel<T> implements ValueFilter<T, User> {
// ...
}
public class UserModel extends UserIntermediateModel<User> {
// ...
}
I can use the annotation with value = UserModel.class, but not with value = UserIntermediateModel.class:
incompatible types: Class<UserIntermediateModel> cannot be converted to Class<? extends ValueFilter<?,?>>
Why so?

Spring Boot custom implementations for Spring Data repositories with MappedSuperclass and subclasses

Here is a simplified working code. There are a mapped superclass and two its subclasses (in real life superclass of course contains more fields)
Animal.java
#MappedSuperclass
#lombok.NoArgsConstructor
#lombok.RequiredArgsConstructor
public abstract class Animal {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#lombok.Getter
private Long id;
#lombok.Getter
#lombok.NonNull
private String name;
}
Cat.java
#Entity
#Table
#lombok.NoArgsConstructor
public class Cat extends Animal {
public Cat(Integer weight, String name) {
super(name);
this.weight = weight;
}
#lombok.Getter
private Integer weight;
}
Dog.java
#Entity
#Table
#lombok.NoArgsConstructor
public class Dog extends Animal {
public Dog(Integer age, String name) {
super(name);
this.age = age;
}
#lombok.Getter
private Integer age;
}
AnimalRepositoryImpl and AnimalRepository contain some shared code for Cat and Dog repositories.
AnimalRepository.java
#NoRepositoryBean
public interface AnimalRepository<T extends Animal> extends JpaRepository<T, Long> {
List<T> findAllByName(String name);
}
AnimalRepositoryImpl.java
public class AnimalRepositoryImpl<T extends Animal> {
#Autowired
AnimalRepository<T> animalRepository;
public List<T> findAllBySomeLogic() {
return animalRepository.findAll().stream().filter(animal -> !animal.getName().startsWith("Z")).collect(Collectors.toList());
}
}
Now I can add all CatRepositories and it still works (and works correctly).
CatRepository.java
#Transactional
public interface CatRepository extends AnimalRepository<Cat>, CatRepositoryCustom {
}
CatRepositoryCustom.java
public interface CatRepositoryCustom {
public List<Cat> findAllBySomeLogic();
}
CatRepositoryImpl.java
public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {
}
Here is a test class which still uses only cat repository.
AnimalRepositoryTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#ActiveProfiles(profiles = "test")
public class AnimalRepositoryTest {
#After
public void tearDown() {
catRepository.deleteAll();
}
#Autowired
private CatRepository catRepository;
#Test
public void shouldFindAllBySomeLogic() {
// given
catRepository.save(Lists.newArrayList(new Cat(2000, "Luna"), new Cat(2500, "Zoe"), new Cat(1800, "Toby")));
// when
List<Cat> cats = catRepository.findAllBySomeLogic();
// then
assertThat(cats.stream().map(c -> c.getName()).collect(Collectors.toList()), containsInAnyOrder("Luna", "Toby"));
}
#Test
public void shouldFindAllByName() {
// given
catRepository.save(Lists.newArrayList(new Cat(2000, "Luna"), new Cat(2500, "Zoe"), new Cat(1800, "Toby")));
// when
List<Cat> cats = catRepository.findAllByName("Luna");
// then
assertThat(cats.stream().map(c -> c.getName()).collect(Collectors.toList()), containsInAnyOrder("Luna"));
}
}
The way I've coded it was inspired mostly by this question (but my case is more complicated).
So... the main question. - How to add repositories for Dog (almost identical to Cat ones) and not to get something like NoUniqueBeanDefinitionException: No qualifying bean of type...? I've tried some variations with #Qualifier but seems it doesn't work in this case. Or maybe I'm doing it completely wrong.
I see at least one failure related to the generic definition of your classes. The class CatRepositoryImpl extends the classe AnimalRepositoryImpl without any generic Types. (See the following two code snippets of your post)
public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {
}
public class AnimalRepositoryImpl<T extends Animal> {
}
In my opinion it should look like.
public class CatRepositoryImpl extends AnimalRepositoryImpl<Cat> implements CatRepositoryCustom {
}
Beside that, I would avoid doing logic related things in a Repository class and move it to a Service level.

Java Generics Wildcards Type Mismatch

I have the following structure:
public abstract class BaseVersionedEntity {
private long id;
private List<BaseRevision<? extends BaseVersionedEntity>> versions;
public BaseRevision<? extends BaseVersionedEntity> getLatestRevision() {
return versions.get(versions.size() - 1);
}
public abstract BaseRevision<? extends BaseVersionedEntity> newRevision();
}
public abstract class BaseVersionedEntityData<E> {
private long id;
private BaseRevision<E> revision;
}
public abstract class BaseRevision<E> implements Comparable<BaseRevision<E>> {
private long id;
private Timestamp timestamp;
private E versionedEntity;
private BaseVersionedEntityData<E> versionedEntityData;
public BaseVersionedEntityData<E> getVersionedEntityData() {
return versionedEntityData;
}
}
That will be implemented by this:
public class PersonEntity extends BaseVersionedEntity {
#Override
public BaseRevision<? extends BaseVersionedEntity> newRevision() {
PersonRevision newRevision = new PersonRevision();
newRevision.setTimestamp(new Timestamp(System.currentTimeMillis()));
getRevisions().add(newRevision);
return newRevision;
}
}
public class PersonData extends BaseVersionedEntityData<PersonEntity> {
}
public class PersonRevision extends BaseRevision<PersonEntity> {
}
Somewhere in my code i'll do the following call:
// is not null
PersonEntity personEntity;
PersonData personData = personEntity.getLatestRevision().getVersionedEntityData();
Out of some reasons that is marked with a type mismatch...
Type mismatch: cannot convert from BaseVersionedEntityData<capture#1-of ? extends BaseVersionedEntity> to PersonData
Can anyone find a mistake?? Or have any hints??
Thank you!!
Benjamin
The method getLatestRevision does not return a PersonRevision, it returns a BaseRevision, and even then PersonRevision doesn't return PersonData - you'll need an explicit cast since this is a downcast, and not even one of the "safe" caused-by-type-erasure downcasts:
PersonData personData = (PersonData)(personEntity.getLatestRevision().getVersionedEntityData());

Typed generic DAO and Entity

currently I'm trying to implement a typed generic DAO.
I do not even get to compile anything, since NetBeans complains about UserDAOHibernate
interface expected here
type argument User is not within bounds of type-variable ENTITY
I'm afraid there is some obvious mistake in how I use inheritance/interfaces, since I'm rather new to Java.
Here's some stripped down code
public interface GenericEntity<ID extends Serializable> {
public abstract ID getId();
public abstract void setId(final ID id);
}
public abstract class LongEntity implements GenericEntity<Long> {
protected Long id;
public Long getId();
public void setId(final Long id);
}
public class User extends LongEntity implements Serializable {
private String name;
private String password;
private Customer customer;
}
public interface GenericDAO<ENTITY extends GenericEntity<ID>, ID extends Serializable> {
public abstract ENTITY findById(ID id);
public abstract List<ENTITY> findAll();
public abstract ENTITY makePersistent(ENTITY entity);
public abstract void makeTransient(ENTITY entity);
}
public abstract class GenericHibernateDAO<ENTITY extends GenericEntity<ID>, ID extends Serializable>
implements GenericDAO<ENTITY, ID> {
}
public class UserDAOHibernate implements GenericHibernateDAO<User, LongEntity> {
}
Is it that LongEntity should extend GenericEntity<Long>? If so, how would I do this with Java's single level or inheritance?
Is this layered approach a bad example to follow? All my entities need an id and this implementation could easily be reused lateron with different id types, so I thought I might use it.
The error comes from here:
public class UserDAOHibernate implements GenericHibernateDAO<User, LongEntity> {
}
You've specified that GenericHibernateDAO's ID parameterized type is bounded by <ID extends Serializable>.
LongEntity extends GenericEntity, and hence, why you have a type mismatch.
Also, GenericHibernateDAO is an abstract class (and not an interface), so you'll need to extends instead of implements.
The correct solution should be:
public class UserDAOHibernate extends GenericHibernateDAO<User, Long> {
}

Categories