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> {
}
Related
What would be the best practice for the following case?
You have the following entities:
#Entity public class BaseEntity { }
#Entity public class SubEntity extends BaseEntity { }
And you have a JpaRepository for BaseEntity having multiple select-methods:
public interface BaseEntityRepository extends JpaRepository<BaseEntity, Long> {
Set<BaseEntity> findAll();
Set<BaseEntity> findSome...();
}
Now you want a JpaRepository for SubEntity inheriting all the methods from BaseEntityRepository.
Now I imagine multiple scenarios, from whom I am not sure which one is the best or which ones should work:
(a) Create a new independent JpaRepository for SubEntity with exactly the same Methods, but substituing BaseEntity with SubEntity:
public interface SubEntityRepository extends JpaRepository<SubEntity, Long> {
Set<SubEntity> findAll();
Set<SubEntity> findSome...();
}
(b) Extending BaseEntityRepository, all Methods should be inherited, but it still return BaseEntity-objects instead of SubEntity-objects
public interface SubEntity extends BaseEntityRepository {
}
I don't really like any of both solutions. How would you solve this?
Somehow I think JpaRepository is not ment for this use-case?
You could parameterize your BaseEntityRepository and operate on those parameters.
public interface BaseEntityRepository<T, I> extends JpaRepository<T, I> {
Set<T> findAll();
Set<T> findSome...();
}
Then add any additional methods in the subclass by extending the BaseEntityRepository
public interface SubEntityRepository extends BaseEntityRepository<SubEntity, Long> {
...
}
I am using JDK 7.
I have an abstract class BaseEntity which is using generics:
public class BaseEntity<Id extends java.io.Serializable> {
protected Id id;
public BaseEntity() {
}
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
// also has hashCode() and equals() methods to be based on id
}
Now I want to create an interface whose type would be BaseEntity and I also want the Id of BaseEntity to be available in the interface. How can I do that?
I tried this code:
public interface BaseLookup<T extends BaseEntity> {
T findById(Id id);
}
but I got 2 messages:
1st one was on BaseEntity. The message was:
Base Entity is a raw type. References to generic type BaseEntity<Id>
should be parameterized.
2nd one was on "Id". The error message was:
Id cannot be resolved to a type.
What I am doing wrong here?
You have to specify the type of BaseEntity in BaseLookup. For better readability, I defind ID type as I.
You could use something like this:
class BaseEntity<I extends java.io.Serializable> {
protected I id;
public BaseEntity() {
}
public I getId() {
return id;
}
public void setId(I id) {
this.id = id;
}
// also has hashCode() and equals() methods to be based on id
}
interface BaseLookup<I extends java.io.Serializable, T extends BaseEntity<I>> {
T findById(I id);
}
public interface BaseLookup<Id, T extends BaseEntity<Id>> {
T findById(Id id);
}
Since you parameterized BaseEntity, you should always use a parameter whenever you reference BaseEntity. That first warning you got was telling you that you were using the BaseEntity (with no parameters) raw type, which is something that really only exists for legacy code and shouldn't be used. Since, ostensibly, BaseEntity is not just one type but an entire collection of types (one for each T), we need to tell it which BaseEntity we're extending, which amounts to adding an additional generic argument.
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.
I have a bean ArtistEntityBean extending GenericEntityBean:
public class ArtistEntityBean extends GenericEntityBean<Artist> {
public ArtistEntityBean() {
item = new Artist();
}
}
-
public abstract class GenericEntityBean<T extends IntEntity> implements Serializable {
protected T item;
public void init(Integer id){
item.setId(id);
}
}
-
public class Artist extends ArtistBaseEntity implements Comparable<Artist> {
...
}
-
public abstract class ArtistBaseEntity implements IntEntity {
...
}
-
public interface IntEntity {
Integer getId();
void setId(Integer id);
}
-
I'm trying to put as much code as possible in the GenericEntityBean class, which is why I thought of using an interface in order to be able to set the id of the item.
This does not work tough, as I get a NoSuchFieldError in the constructor of ArtistEntityBean and I don't know why?
If item is public, protected or default you have to use
super.item = new Artist();
in the constructor of ArtistEntityBean.
If it is private you have to provide a setter method in the abstract class.
Edit: If you did not specify item in the abstract class then do the following
public abstract class GenericEntityBean<T extends IntEntity> implements Serializable {
protected T item;
public void init(Integer id){
item.setId(id);
}
}
Given a generic tree based (Java) data model. Let's say
abstract class XModel {
long id;
XModel parent;
}
class ProjectModel extends XModel {
String customer;
}
class FileModel extends XModel {
String name;
}
class FolderModel extends XModel {
String name;
String attributes;
}
My challenge is to make sure that FileModels are only used in FolderModels (or PrjectModels) and FolderModels are only used in ProjectModels.
Since the model should be extensible - is there any generic way to do this constraint validation (like XML Schema does) without hardcoding the parent-child-realtions in a validation method?
I'm not sure how useful it would be - depends on how you create the tree, but this might work:
abstract class XModel<T extends XModel> {
long id;
T parent;
}
class ProjectModel extends XModel { ... }
class FolderModel extends XModel<ProjectModel> { ... }
class FileModel extends XModel<FolderModel> { ... }
UPDATE
To separate validation logic from model itself you could use some runtime validators like Hibernate or Spring validators.
I would use generics and interfaces that describe the restrictions
interface Model {
}
abstract class AbstractModel<P extends Model> implements Model {
Long id;
P parent;
}
class ProjectModel extends AbstractModel implements HasFileModel,
HasFolderModel {
}
interface HasFileModel extends Model {
}
static class FileModel extends AbstractModel<HasFileModel> {
}
interface HasFolderModel extends Model {
}
class FolderModel extends AbstractModel<HasFolderModel> implements
HasFileModel {
}
This way the child only restricts the parent to an interface not a concrete type
If you don't want to put those constraints into code, you'd have to write a validation method that is fed by some external data yourself. AFAIK there's no such built in option (in the compiler) besides using Generics, which would require you to express the constraints in code.