Generic test class for a generic DAO class? - java

I'm trying to test some DAO Classes that inherit from a Generic one, and was wondering if there was a way to write a generic test class that tests the functionality of my generic DAO, and create other test classes inheriting from it
My Generic DAO Class :
public abstract class GenericDAO<T> {
private final Class<T> entityClass;
public GenericDAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
//bunch of methods
//...
}
a sample DAO inheriting from my generic one
public class OptionsDAO extends GenericDAO<Options> {
public OptionsDAO() {
super(Options.class);
}
}
what i have currently in tests
public class OptionsDAOTest {
//...
#Test
public void testSomeMethod() {
OptionsDAO odao = new OptionsDAO(); //this is what blocked me from achieving what i'm trying to do (look #next sction)
odao.callSomeMethod();
//Asserts and what not...
}
//...
}
THIS is what I'm hoping to do
public class GenericDAOTest<T> {
private final Class<T> entityClass;
public GenericDAOTest(Class<T> entityClass) {
this.entityClass = entityClass;
}
#Test
public void testSomeMethod() {
GenericDAO gdao = ???//how can i get an instance based on the T passed to the class?
odao.callSomeMethod();
//Asserts and what not...
}
//...
}
public class OptionsDAOTest extends GnericDAOTest<Options> {
public OptionsDAOTest() {
super(OptionsDAO.class);
}
//how can i call inhrited methods here to test them on an Options java bean
//and do the same with oter DAO classes i have?
}

GenericDAO gdao = ???//how can i get an instance based on the T passed to the class?
You can just call entityClass.newInstance() if the generic class provides a no-arg constructor.
Note that entityClass is of type Class<T> and thus of type T.

Related

Java generics both methods have same erasure error

In my project, I have multiple services performing three basic operations - create, edit and search. For this, I am trying to create a generic service. Below is what I have come up with so far.
Search method will take a list of objects at runtime.
public interface GenericService<T> {
void update(T t);
void create(T t);
T search(List<?> t);
}
Also, I have created an abstract class where the common code for all services will be placed.
public abstract class AbstractService<T> implements GenericService<T> {
}
Here is my implementation
public class AccountService extends AbstractService<Account> implements GenericService<Account> {
#Override
public void update(Account account) { }
#Override
public void create(Account account) { }
#Override
public Account search(List<SearchCriteria> t) { return null; }
}
Here are my Account and SearchCriteria classes
public class Account {
private String accountNumber;
private Date openingDate;
// more fields
// getter setter removed for brevity
}
Search criteria class
public class SearchCriteria {
private String key;
private String value;
// getter setter removed for brevity
}
Problem: on line public Account search(List t) { return null; }, getting compilation error saying
'search(List)' in
'com.test.AccountService' clashes with
'search(List)' in 'com.test.GenericService';
both methods have same erasure, yet neither overrides the other
In order for
public Account search(List<SearchCriteria> t) { ...}
to override
T search(List<?> t);
The arguments must be the same after type parameter substitution, but ? is not SearchCriteria.
Therefore, if you want to keep these methods (the inheritance looks a bit wild to me), you'll need to parameterise the types further.
public interface GenericService<T, C> {
// ...
T search(List<C> t); // probably change that parameter name
}
public abstract class AbstractService<T, C>
implements GenericService<T, C>
{
}
public class AccountService
extends AbstractService<Account, SearchCriteria>
implements GenericService<Account, SearchCriteria> // unnecessary
{
// ...
#Override
public Account search(List<SearchCriteria> t) { /* ... */ }
}
Changing List<?> to List<SearchCriteria> in GenericService will solve the error. There is no benefit in using a wildcard if the search method will always take a list of SearchCriteria objects in every service implementation.
If, however, you want to make this generic as well, you can introduce a second type parameter.

Unchecked conversion warning with generics

I don't quite understand, why my code has to do a unchecked conversion and how I can fix that.
I am implementing immutable objects with the builder pattern for which I implemented the "Immutable" interface with the inner interface "Builder".
Each immutable class implements the Immutable interface and implements a inner static class Builder, which implements the Builder interface.
All this works fine.
Now, I am implementing a bunch of very simple classes that don't really need a builder, but I still want to implement the Immutable interface, so the objects of those classes are instances of "Immutable", but I don't want to implement empty builders without any functionality for each class. I'd rather have an abstract class in which to implement one simple builder for all the simple classes. The builder will just store the original object and return it through the build() method, so the Immutable interface is implemented completely
The build() method of the builder has to return an object of the implementing class, though. So I added generics.
public interface Immutable {
public interface Builder<T> {
public T build();
}
public <T> Builder<T> builder();
}
public interface Interface extends Immutable {
public interface BuilderInterface<T> extends Immutable.Builder<T> {
}
}
public abstract class AbstractClass implements Interface {
public static class AbstractBuilder<T> implements Interface.BuilderInterface<T> {
private final T object;
public AbstractBuilder(T object) {
this.object = object;
}
#Override
public T build() {
return this.object;
}
}
protected AbstractClass() {
super();
}
}
public class ConcreteClass extends AbstractClass {
public ConcreteClass() {
}
#Override
public AbstractBuilder<ConcreteClass> builder() {
return new AbstractClass.AbstractBuilder<ConcreteClass>(this);
}
}
I was expecting the generic type T of the Immutable interface to take the type of the implementing class, but instead it seems to be Object, which leads to the following warning:
Type safety: The return type AbstractClass.AbstractBuilder<ConcreteClass> for builder() from the type ConcreteClass needs unchecked conversion to conform to Immutable.Builder<Object> from the type Immutable
EDIT: The warning is given by the builder() method of ConcreteClass.
It's quite simple - the method signature of Immutable#builder expects the type parameter T set "on the fly" for the actual method call and not being bound to the class. To appropriately override this method, the signatur in ConcreteClass would be
public <T> Builder<T> builder() {
which obviously clashes with your builder definition
return new AbstractClass.AbstractBuilder<ConcreteClass>(this);
To make this all compilable, you have to infer T for Immutable#builder from the class and not from the method caller, i.e. that you finally have
public interface Immutable<T> {
public interface Builder<T> {
public T build();
}
public Builder<T> builder();
}
and all inheriting classes changed accordingly to pass a T to its predecessors.
public interface Interface<T> extends Immutable<T> {
public interface BuilderInterface<T> extends Immutable.Builder<T> {
}
}
public abstract class AbstractClass<T> implements Interface<T> {
public static class AbstractBuilder<T> implements Interface.BuilderInterface<T> {
private final T object;
public AbstractBuilder(T object) {
this.object = object;
}
#Override
public T build() {
return this.object;
}
}
protected AbstractClass() {
super();
}
}
public class ConcreteClass extends AbstractClass<ConcreteClass> {
public ConcreteClass() {
}
#Override
public Builder<ConcreteClass> builder() {
return new AbstractClass.AbstractBuilder<ConcreteClass>(this);
}
}

how to get the persistent class parameter of a class generic? <T>

here's my code:
AbstractClass
public abstract class AbstractClass<T>{
public abstract Class<?> getPersistentClass();
public void invokeNothing(){
Class<T> c = getPersistentClass();
// do something....
// some code...
}
}
CommonClass
public class CommonClass<T> extends AbstractClass<T>{
public Class<?> getPersistentClass(){
// how to get the persistent class of generic T
// T.class
return // T.class
}
}
Service
public class CommonService{
#Autowired
private CommonAbstractClass<Person> commonClass;
public void invoke(){
commonClass.invokeNothing();
}
}
how to get the persistent class parameter of a class generic? in my class CommonClass in method getPersistentClass();
please help me thanks...
Actually there is no way (at least no easy way I am aware of except reflection in some cases) to get the runtime type of the generic type parameter due to type erasure. You can use the following safe work-around:
public class CommonClass<T>{
private final Class<T> type;
public CommonClass(Class<T> type) {
this.type = type;
}
public Class<T> getMyType() {
return this.type;
}
}
But you may need to instantiate it like:
CommonClass<Person> cp = new CommonClass<Person>(Person.class);
If you are using Spring you can find this answer useful.

Generic JUnit tests

I've got an abstract generic collections class GCollection, and a class that extends that called GStack.
To test the implementation I have an abstract JUnit test class, which I extend for each of the GCollection implementations I do:
public abstract class GCollectionTest<T extends GCollection<E>, E> {
private GCollection<? extends Object> collection;
protected abstract GCollection<T> createInstance();
#Before
public void setup() throws Exception {
collection = createInstance();
}
// Tests down here.
This is extended like so:
public class GStackCollectionInterfaceTest<S extends GCollection<E>> {
protected GDSStack<? extends Object> createInstance() {
return new GDSStack<String>();
}
}
I test first with a GStack holding String objects, then re-run the tests with Date objects to ensure it works with different object types.
#Test
public void testIsEmpty() {
assertTrue(collection.isEmpty()); // Fresh Stack should hold no objects
collection.add(new String("Foo")); // Error here.
assertFalse(collection.isEmpty());
}
The error given is:
The method add(capture#24-of ? extends Object) in the type GCollection is not applicable for the arguments (String)
My understanding of the error is that I can't put a String object into a GCollection<T extends GCollection<E>> object, but I don't think that's what I'm trying to do.
What am I doing wrong?
How can I solve this error while maintaining tests that are as generic as possible?
The type of collection is GCollection<? extends Object>. It is not possible to add anything to this collection, see: can't add value to the java collection with wildcard generic type.
There's no need for wildcards or bounds in the subclass so you can simplify the generics. Something like:
abstract class GCollectionTest<T> {
protected Collection<T> collection;
protected abstract Collection<T> createCollection();
protected abstract T createObject();
#Before
public void setup() throws Exception {
collection = createCollection();
}
#Test
public void testIsEmpty() {
assertTrue(collection.isEmpty());
collection.add(createObject());
assertFalse(collection.isEmpty());
}
}
class GStackCollectionInterfaceTest extends GCollectionTest<String> {
protected GDSStack<String> createCollection() {
return new GDSStack<String>();
}
protected String createObject() {
return new String("123");
}
}
Using different types with the collection is allowed because of the generic type, and checked by the compiler, so it doesn't really need testing. I would just test the different container types, but you could create another subclass that uses Date instead of String.

Abstracting named queries in an abstract JPA DAO

I have an abstract DAO class which uses parameterized types E (Entity) and K (Primary Key). In every entity I have a #NamedQuery. I want to dynamically invoke this named query without knowing its exact name and parameter name.
As an example, imagine the following entity City
#Entity(name="CITY")
#NamedQuery(
name="findCityByname",
query="FROM CITY c WHERE name = :CityName"
)
public class City {
// ...
}
and this CityDao
public class CityDao extends AbstractDao<City, Long> {
public CityDao() {
super(City.class);
}
}
How should I implement the findByName() method in AbstractDao so that I don't need to know the exact name and parameter name?
public abstract class AbstractDao<E, K> implements Dao<E, K> {
#PersistenceContext
protected EntityManager entityManager;
protected Class<E> entityClass;
protected AbstractDao(Class<E> entityClass) {
this.entityClass = entityClass;
}
#Override
public E findByName(String name) {
try {
return (E) entityManager
.createNamedQuery("findCityByName")
.setParameter("CityName", name)
.getSingleResult();
} catch(Exception e) {
return null;
}
}
// ...
}
The naming convention for named queries is usually <Entity Name>.findBy<PropertyAndAnotherProperty>, "City.findByName" in your example, so I would try to change the named queries to follow this pattern. The parameter to this query should then also have the same name, or you could use positional parameters. Your find method would then turn into
#Override
public E findByName(String name) {
E entity = null;
try {
return (E)entityManager.createNamedQuery(myClass.getSimpleName() + ".findByName")
.setParameter("name", name)
.getSingleResult();
} catch (Exception ex) {
return null;
}
}
The simplest method is to pass the name of the query to the constructor of the abstract DAO:
public DaoAbstreact(Class myClass, String findByNameQueryName) {
this.myClass = myClass;
this.findByNameQueryName = findByNameQueryName;
}
Then define a public static final String in City to hold the name:
public class ConcreteCityDao<City,Long> extends DaoAbstreact {
ConcreteCityDao(){
super(City.class, City.FIND_BY_NAME_QUERY_NAME));
}
}
Alternatively you could declare DaoAbstreact as abstract and then have a method like this in it:
public abstract String getFindByNameQueryName();
And implement that in ConcreteCityDao.
Finally you could also introduce an enumeration:
public enum NamedEntityType {
CITY(City.class, "findCityByname"),
PERSON(Person.class, "findPersonByname");
private final Class<?> entityClass;
private final String findByNameQueryName;
private NamedEntityType(Class<?> entityClass, String findByNameQueryName) {
this.entityClass = entityClass;
this.findByNameQueryName = findByNameQueryName;
}
public Class<?> getEntityClass() {
return entityClass;
}
public String getFindByNameQueryName() {
return findByNameQueryName;
}
}
Then your DAO can determine the type from the class passed in. To ensure you don't forget to add an entity to the enumeration you can make each entity implement an interface with a getNamedEntityType() method. Then you can specify that your abstract generic DAO will only accept entities that implement that interface.
The obvious way would be to pass values from concrete classes to the abstract superclass using abstract method
public abstract class AbstractDao<E, K extends Serializable> implements Dao <E, K> {
...
protected abstract String getFindByNameQueryName();
#Override
public E findByName(String EntityStr) {
... entityManager.createNamedQuery(getFindByNameQueryName()) ...
}
}
#Override
public class ConcreteCityDao<City,Long> extends DaoAbstreact{
...
protected String getFindByNameQueryName() {
return "findCityByName";
}
}
or as a constructor argument:
public abstract class AbstractDao<E, K extends Serializable> implements Dao<E, K> {
public AbstractDao(Class<E> myClass, String findByNameQueryName) { ... }
...
}
#Override
public class ConcreteCityDao<City, Long> extends DaoAbstreact{
public ConcreteCityDao() {
super(City.class, "findCityByName");
}
}
Though this requires consistent naming of query parameters for different entities.
Also note the minor improvements in these snippets.
What you basically seem to want is to annotate the annotations that define the named queries, in such a way that you can programmatically discover what the "findByName" query is (and possible other queries).
Since this is not possible in Java, you could use the fact that #NamedQuery supports query hints, that are defined as being vendor specific. Unknown hints are ignored. You could add your own data here, that the generic DAO can read back from entityClass:
#NamedQuery(
name="findCityByname",
query="FROM CITY c WHERE name = :CityName",
hints=#QueryHint(name="genericDAO.type", value="findByName")
)

Categories

Resources