Manu
Manu

Reputation: 4137

Spring + Mongo + Generics + Flexibility

The following code doesn't work (of course), because the marked line does not compile:

MyClass {
    //singleton stuff
    private static MyClass instance;
    private MyClass () {}
    public static MyClass getInstance() {
        if(instance==null) {
            instance = new MyClass ();
        }
        return instance;
    }

    // method creating problems
    public NonGenericSuperClassOfGenericClass create(Class<?>... classes) {
        if(someCondition)
             return new GenericClass<classes[0],classes[1]>; // DOES NOT COMPILE
        else
             return new OtherGenericClass<classes[0]>;
    }
}

Therefore, I actually don't know whether "create" will return

GenericClass<classes[0],classes[1]>

or

OtherGenericClass<classes[0]>

which have different numbers of parameters.

This happens because I'm using Spring and I plan to use MongoDB, but in the future I may need to switch to something different (e.g. Hibernate).

The class GenericClass is something like:

 GenericClass<PersistetType1, Long> 

or

 GenericClass<PersistentType2, Long>

where PersistentType1/2 are classes that I need to finally store in the DB, while, GenericClass is a sort of Proxy to access Mongo APIs. In fact, it looks like:

  public MongoTemplate getTemplate();
  public void save(T toInsert);
  public List<T> select(Query selectionQuery);
  public T selectById(ID id);
  public WriteResult update(Query selectionQuery, Update updatedAttributes);
  public void delete(T toRemove);
  public void delete(Query selectionQuery);

Now, what? From Controllers (or Entity, if you are picky) I need to instantiate the repository and invoke any methods. This causes the Controllers to be coupled with MongoDB, i.e. they explicitly have to instantiate such GenericClass, which is actually called MongoRepository and is strictly dependent on Mongo (in fact it is a generic with exactly two "degrees of freedom").

So, I decided to create MyClass, that is a further proxy that isolates Controllers. In this way, Controller can get the single instance of MyClass and let it create a new instance of the appropriate repository. In particular, when "somecondition" is true, it means that we want to use MongoRepository (when it is false, maybe, a need to instantiate a Hibernate proxy, i.e. HibernateRepository). However, MongoRepository is generic, therefore it requires some form of instantiation, that I hoped to pass as a parameter.

Unfortunately, generics are resolved at compile time, thus they don't work for me, I guess.

How can I fix that?

Upvotes: 1

Views: 1423

Answers (1)

Alex Barnes
Alex Barnes

Reputation: 7218

In order to decouple the underlying persistence store from your application logic I would use the DAO approach.

Define the interface of your DAO with the required methods e.g. save, update etc. And then provide an implementation for each persistence provider you might need e.g.UserAccess might be the interface which you could implement as HibernateUserAccess and MongoUserAccess. In each implementation you inject the appropriate Template e.g. Mongo or Hibernate and use that to complete the persistence operation.

The issue you might have is that your load operation would return an instance of User, this would need to vary across persistence providers i.e. JPA annotations would be different to the Spring Data annotations needed for MongoDB (leaky abstraction).

I would probably solve that by creating a User interface to represent the result of the persistence operation and having an implementation for each persistence provider. Either that or return a common model which you build from the results of a JPA or Mongo load.

Upvotes: 2

Related Questions