Jaythaking
Jaythaking

Reputation: 2102

Better pattern to handle DAO creation for POJO using SQLite

I'm working on an Android App that use SQLCipher, ORMLite for Android to handle to POJO storing with SQLite and Jackson for parsing.

I'm wondering if there would be a better pattern that the one i'm using (Recommended by stayforit) to get the DAO corresponding to the Entity class given. I have over 30 Entity class and I keep adding some over the time and each time, I have to create a DAO class that looks exactly the same as the previous one. How could I generalize using a generic class?

Here is my DbManager class:

public class DbManager {
    private static DbManager instance;
    private CipherDbHelper dbHelper;
    private SecureSharedPreferences settings;

    private DbManager() {

    }

    private DbManager(Context context, String password) {
        SQLiteDatabase.loadLibs(context);
        dbHelper = new CipherDbHelper(context, password);
    }

    public static void init(Context context, String password) {
        instance = new DbManager(context, password);
    }

    public static DbManager getInstance() {
        if (instance == null) {
            Log.e("DbManager", "DbManager is null");
        }
        return instance;
    }

    public <D extends Dao<T, String>, T> D getDAO(Class<T> clz) throws SQLException {
        return dbHelper.getDao(clz);
    }
}

Here is an example of a recurrent DAO class I need to generate each time I add a POJO entity to my project:

public class CategoriesDAO extends BaseDAO<EntityCategories> {
    private static CategoriesDAO instance;

    private CategoriesDAO() {
    }

    public synchronized static CategoriesDAO getInstance() {
        if (instance == null) {
            instance = new CategoriesDAO();
        }
        return instance;
    }

    @Override
    public Dao<EntityCategories, String> getDAO() throws SQLException, java.sql.SQLException {
        return DbManager.getInstance().getDAO(EntityCategories.class);
    }
}

Here is how I use it in an Activity:

CategoriesDAO.getInstance().addOrUpdate(categories);

Upvotes: 1

Views: 958

Answers (2)

jns
jns

Reputation: 6952

That's the way I like to use Ormlite DAO's:

CRUDOperator:

public interface CRUDOperator<T> {

    void create(T obj);

    void update(T obj);

    void delete(T obj);
}

Repo:

public interface Repo<T> extends CRUDOperator<T>{

    Optional<T> queryForId(Integer id);
    ObservableList<T> queryForAll();
    ...
}

OrmliteRepo:

public class OrmliteRepo<T> implements Repo<T> {

    protected Dao<T, Integer>          dao;

    protected OrmliteRepo(Dao<T, Integer> dao) {
        this.dao = dao;
    }

    public ObservableList<T> queryForAll() throws SQLException {
        List<T> results =  dao.queryForAll();
        return Validators.isNullOrEmpty(results) ? FXCollections.observableArrayList() : FXCollections.observableArrayList(results);
    }

    public Optional<T> queryForId(Integer id) throws SQLException {
        T result = dao.queryForId(id);
        return Optional.ofNullable(result);
    }

    @Override
    public void create(T obj) throws SQLException {
            dao.create(obj);
    }

    @Override
    public void update(T obj) throws SQLException {
            dao.update(obj);
    }

    @Override
    public void delete(T obj) throws SQLException {
            dao.delete(obj);
    }
}

YourRepo:

public class YourRepo extends OrmliteRepo<YourModel> {

    public YourRepo(Dao<YourModel, Integer> dao) {
        super(dao);
    }
}

RepoService:

public interface RepoService {
    <T> Repo<T> get(Class<T> dataClass);
}

BaseRepoService:

public class BaseRepoService implements RepoService {

    private RepoFactory            repoFactory;
    private Map<Class<?>, Repo<?>> repoCache;

    public BaseRepoService(RepoFactory repoFactory) {
        this.repoFactory = repoFactory;
        repoCache = new HashMap<>();
    }

    @Override
    public <T> Repo<T> get(Class<T> dataClass) {
        @SuppressWarnings("unchecked")
        Repo<T> repo =  (Repo<T>) repoCache.get(dataClass);

        if (repo == null) {
            repo = createRepo(dataClass);
            repoCache.put(dataClass, repo);
        }
        return repo;
    }

    private <T> Repo<T> createRepo(Class<T> dataClass) {
        return repoFactory.createRepo(dataClass);
    }
}

RepoFactory:

public interface RepoFactory {
    public <T> Repo<T> createRepo(Class<T> dataClass);
}

OrmliteRepoFactory:

public class OrmliteRepoFactory implements RepoFactory {

    private DbAccess                                      dbAccess;
    private final Map<Class<?>, Supplier<OrmliteRepo<?>>> suppliers;

    public OrmliteRepoFactory(DbAccess dbAccess) {
        this.dbAccess = dbAccess;

        suppliers = new HashMap<>();
        suppliers.put(YourModel.class, () -> new YourRepo(getDao(YourModel.class)));
    }

    private <T> Dao<T, Integer> getDao(Class<T> modelClass) {
        return dbAccess.getDaoImplementation(modelClass);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> OrmliteRepo<T> createRepo(Class<T> dataClass) {
        return (OrmliteRepo<T>) suppliers.get(dataClass).get();
    }
}

DbAccess:

public interface DbAccess {
     <T, R> R getDaoImplemantation(Class<T> dataClass);
}

OrmliteDbAccess:

public class OrmliteDbAccess implements DbAccess{

@Override
public <T, R> R getDaoImplementation(Class<T> objectClass) {
    R dao = null;

    try {
        dao = DaoManager.createDao(connectionSource, objectClass);

    } catch (SQLException e) {
        LOGGER.error("Error getting dao for class {}; {}", objectClass, e);
    }
    return dao;
}

}

Now all you need to do is add the suppliers for your repos to the repoFactory and make YourRepo.class extend OrmliteRepo.class. If I need some additional behaviour for a specific repo, I put it in that repo implementation.

When you have an instance of RepoService:

RepoService repoService = new BaseRepoService(ormliteRepoFactory);

you can access your repo like this:

Repo<YourModel> repo = repoService.get(YourModel.class);

Upvotes: 3

Kal
Kal

Reputation: 24910

You could store the instances of your POJO daos in a map either inside your BaseDao itself or in a subclass and then use an unchecked cast to extract it out.

public class GenericDao<T> extends BaseDao<T>  {

  private static class InstanceHolder {
      static final Map<Class<?>, GenericDao<?>> INSTANCES = new HashMap<>();
  }

  public static synchronized <T> GenericDao<T> getInstance(Class<T> clazz) {
      GenericDao<T> dao = (GenericDao<T>)InstanceHolder.INSTANCES.get(clazz);
      if (dao == null) {
        dao = new GenericDao<T>();
        InstanceHolder.INSTANCES.put(clazz, dao);
      }
      return dao;
  }

  private GenericDao() {
  }
}

and then

    GenericDao<EntityCategories> foo = GenericDao.getInstance(EntityCategories.class);
    foo.addOrUpdate(....);

Upvotes: 2

Related Questions