Currently I'm working on a service interface which retrieves domain objects based on a primary key. However I get the feeling I'm not efficiently using generics.
Base domain objects look as follows:
public interface DomainObject<PK extends Serializable> extends Serializable {
PK getID();
}
My service interface looks as follows:
public interface LoadService<T extends DomainObject<PK>, PK extends Serializable> {
T load(PK ID);
}
This works, however I have to specify the PK type in the service generics, even though the PK type is already known inside T. Is there any way I can get around having to define my PK again in the LoadService interface? Something like:
LoadService<T extends DomainObject<? extends Serializable as PK> { ... }
Help will be greatly appreciated!
There is no way to avoid that because you use PK class at the 'LoadService'. I mean that you can define service like
interface LoadService<T extends DomainObject<?>> {
void store(T data);
}
However, that's not the option if you use PK class because compiler checks that PK type is compatible with the domain object type.
Another option is to remove type parameter from DomainObject, i.e. perform the following:
interface DomainObject extends Serializable {
Serializable getID();
}
Try using multiple bounds for the type parameter, if T both extends DomainObject and implements Serializable:
interface LoadService<T extends DomainObject<T> & Serializable> {
}
Related
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
Here's the scenario.
Our app has a very trivial interface for cache implementations, with methods similar to a Map:
public interface ICache<K, V> {
To add a concrete cache implementation we implement the interface and wrap a cache framework like EHCache, Redis, memcached etc. Example (the fact that its EHCache here is immaterial to the question):
public abstract class EHCacheWrapper<K,V> implements ICache<K, V> {
Next we have an implementation of EHCacheWrapper called AuthenticationCache:
public class AuthenticationCache
extends EHCacheWrapper<AuthenticationCacheKey, AuthenticationCacheEntry> {
So far so good.
The AuthenticationCache object has some additional methods beyond EHCacheWrapper or ICache. What we want to add are interfaces for AuthenticationCache, AuthenticationCacheKey, and AuthenticationCacheEntry:
public interface IAuthenticationCacheKey extends Serializable {
public interface IAuthenticationCacheEntry extends Serializable {
public interface IAuthenticationCache extends ICache<IAuthenticationCacheKey,IAuthenticationCacheEntry>{
So now we have:
public class AuthenticationCache
extends EHCacheWrapper<AuthenticationCacheKey, AuthenticationCacheEntry>
implements IAuthenticationCache {
Which gives the compiler error:
The interface ICache cannot be implemented more than once with different arguments: ICache<AuthenticationCacheKey,AuthenticationCacheEntry> and ICache<IAuthenticationCacheKey,IAuthenticationCacheEntry>
How do I achieve what we're after here?
As generics are no longer available at run time because of Type Erasure it can't be distinguished between two implementations of ICache<K, V>. You need to re-consider the design of you Interface and class hierarchy. Is it really necessary that IAuthenticationCache extends ICache?
public interface IAuthenticationCache
//extends ICache<IAuthenticationCacheKey,IAuthenticationCacheEntry>
{ ... }
Interface with serializable implements?
public interface SearchCriteria extends Serializable {}
class which implements a interface which doesnt have method initilization
just a extented by "Serializable" interface
public class AbstractSearchCriteria implements SearchCriteria
{
private static final long serialVersionUID = 1L;
private PageCriteria pageCriteria;
public AbstractSearchCriteria()
{
super();
}
public PageCriteria getPageCriteria()
{
return pageCriteria;
}
public void setPageCriteria(PageCriteria pageCriteria)
{
this.pageCriteria = pageCriteria;
}}
serialization is the process of translating data structures or object state into a format that can be stored.
Serializable is a marker interface
serializable is a special interface that specifies that class is serialiazable. It's special in that unlike a normal interface it does not define any methods that must be implemented: it is simply marking the class as serializable.
more here What is object serialization?
In short:
You extended Serializable interface in SearchCriteria interface. All classes that implement the SearchCriteria interface, will also be implementing Serializable interface by default.
For more detailed info check the documentation.
I've got a JPA #MappedSuperClass and an #Entity extending it:
#MappedSuperclass
public class BaseClass {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private Boolean active;
//getters & setters
}
#Entity
public class Worker extends BaseClass{
#Column
private String name;
//getters & setters
}
The active field of the base class is a flag for the children entities. Only the active ones should be loaded in the application. Then I've written a generic Spring Data Proxy interface:
public interface Dao<T extends BaseClass, E extends Serializable> extends
CrudRepository<T, E> {
Iterable<T> findByActive(Boolean active);
}
And this one is the interface that should be for Worker data access, properly extending the previous one:
#Transactional
public interface WorkerDao extends Dao<Worker, Long>{}
Well, now in my logic layer I've implemented an abstract class which will wrap the common code for CRUD operations over my entities. I'll have a service for each of them, but I want just to inherit from the abstract one. I want to wire the specific repository for each of the services and provide it to the superclass using an abstract method. That's how my superclass is implemented:
public abstract class GenericService<E extends BaseClass>{
public abstract Dao<E, Long> getDao();
//Here I've got some common operations for managing
//all my application classes, including Worker
}
The problem is that the getDao() method uses the E class parameter, which is guaranteed only to be a child of BaseClass and not a javax.persistence.Entity. When I try to access the DAO from my custom service implementation I get this error:
Caused by: java.lang.IllegalArgumentException: Could not create query metamodel for method public abstract java.lang.Iterable com.mycompany.model.daos.interfaces.Dao.findByActive(java.lang.Boolean)!
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:93)
Caused by: java.lang.IllegalArgumentException: Not an entity: class com.mycompany.model.BaseClass
at org.hibernate.jpa.internal.metamodel.MetamodelImpl.entity(MetamodelImpl.java:203)
Which makes sense, because E is defined as a child of BaseClass. The compiler allows me to write this too:
public abstract class GenericService<E extends BaseClass && Entity>
However I get an error in the child Service that says Worker class is not compatible with the signature for E. Does anybody know how to solve this?
It's just a matter of annotating the abstract Repository as #NoRepositoryBean:
#NoRepositoryBean
public interface Dao<T extends BaseClass, E extends Serializable> extends
CrudRepository<T, E> {
Iterable<T> findByActive(Boolean active);
}
This way Spring relies on the underlying repository implementation to execute the findByActive method.
Regarding to the annotation type restriction issue, it's not possible to declare an annotation restricted type. See the referenced answers below.
See also:
Generic Spring Data JPA repository implementation to load data by class type
Annotations: restrict reference to classes with a annotation
I'm wondering how an abstract class with generics would handle with JPA? I mean what kind of annotations do I need for the field?
Consider these:
#MappedSuperclass
public abstract class AbstractMyClass<T> {
// What about Strings and Integers? Do I need some kind of #LOB?
private T field;
public T getField() {
return field;
}
public void setField(T field) {
this.field = field;
}
}
And then these
#Entity
#Table(name = "String")
public class MyStringClass extends AbstractMyClass<String> {
}
#Entity
#Table(name = "Integer")
public class MyIntegerClass extends AbstractMyClass<Integer> {
}
JPA is perfectly able to handle your proposed, because the generic appears at the abstract class level and for your concrete classes it has exactly a single value per class. In fact, JPA will store your subclasses in one or more table, according to the #InheritanceStrategy you have chosen and uses different mechanism for that.
You can figure out yourself why your case is not a problem, reasoning about how an ORM could save the two classes on a DB:
You can store MyStringClass and MyIntegerClass in the same table, adding a Discriminator column so that the ORM, when it loads from the DB, know which constructor should be called.
You can store every subclass in more table.
What is not possible, on the other side, is to define a generic
#Entity
#Table(name = "MyGenericClass")
public class MyGenericClass<T> {
private T t;
public MyGenericClass(T t) {
this.t=t;
}
}
The reason for this is that, at compile time, the T is "erased" because of type erasure. It is used at compile time to verify signatures and correctness of types, but then it is turned into a java.lang.Object inside the JVM. If you follow until now, you should be able to understand the following:
In your case, every concrete subclass of AbstractMyClass has a type T which is defined for all instances of the class. While the T information is not retained into the AbstractMyClass, it is retained and unique inside the subclasses.
In the second case I posted, each possible concrete instance of MyGenericClass could have a possible different value for T, and because of type erasure this information is not retained.
*Note: the fact that the second case cannot be handled by JPA is absolutely reasonable and if you fall in that case you should ask yourself questions about your design. Generics are a great tool to design flexible classes which can handle other classes in a type-safe manner, but type-safe is a programming language concept which has nothing to do with persistance.
Extra : you could use javap to see what really is erasure. Take off annotations from MyGenericClass and compile it.
G:\>javac MyGenericClass.java
G:\>javap -p MyGenericClass
Compiled from "MyGenericClass.java"
public class MyGenericClass extends java.lang.Object{
private java.lang.Object t;
public MyGenericClass(java.lang.Object);
}
We can. if the T implements Serializable
#Entity
public class IgsSubject extends BasicObject implements Serializable{
private static final long serialVersionUID = -5387429446192609471L;
#MappedSuperclass
public class IgsBasicLog<T> extends BasicObject {
#ManyToOne
#JoinColumn(name = "ITEM_ID")
private T item;
#Entity
public class IgsLogA extends IgsBasicLog<IgsSubject> implements Serializable {
private static final long serialVersionUID = -8207430344929724212L;
}