现在的位置: 首页 > 综合 > 正文

android in practice_Creating a data manager(MyMoviesDataBases project)

2017年12月24日 ⁄ 综合 ⁄ 共 6523字 ⁄ 字号 评论关闭

Because our DAOs are each unaware of any table other than their own, we’re also going to create a DataManager interface, an implementation class that will wrap the
multiple DAOs and take care of the remaining duties from one place. Such duties will include storing data in multiple tables and dealing with transactions. Our application

components will ultimately use this class to save and retrieve data. This technique, shown in the next listing, will keep all of the SQL and logic out of our application components and views.

The DataManager interface defines all possible operations:

//DataManager interface used to define data operations.
public interface DataManager {
	  // movie
	   public Movie getMovie(long movieId);
	   public List<Movie> getMovieHeaders();
	   public Movie findMovie(String name);
	   public long saveMovie(Movie movie);
	   public boolean deleteMovie(long movieId);
	   // optional -- used for CursorAdapter
	   public Cursor getMovieCursor();
	   // category
	   public Category getCategory(long categoryId);
	   public List<Category> getAllCategories();
	   public Category findCategory(String name);
	   public long saveCategory(Category category);
	   public void deleteCategory(Category category); 
}

Our application components will use references to this interface to perform data operations.  

implement the DataManager Interface:

/**
 *DataManagerImpl to encapsulate SQL and DB details.
 * Includes SQLiteOpenHelper, and uses Dao objects
 * to create/update/delete data.
 */
public class DataManagerImpl implements DataManager {
	   private Context context;
	   private SQLiteDatabase db;
	   private CategoryDao categoryDao;
	   private MovieDao movieDao;
	   private MovieCategoryDao movieCategoryDao;
	  
	   public DataManagerImpl(Context context) {
		      this.context = context;
		      //construct OpenHelper
		      SQLiteOpenHelper openHelper = new OpenHelper(this.context);
		      //get SQLiteDatabase reference
		      db = openHelper.getWritableDatabase();
		      Log.i(Constants.LOG_TAG, "DataManagerImpl created, db open status: " + db.isOpen());
		      
		      categoryDao = new CategoryDao(db);
		      movieDao = new MovieDao(db);
		      movieCategoryDao = new MovieCategoryDao(db);
		   }
	   public SQLiteDatabase getDb() {
		      return db;
		   }

	   private void openDb() {
		      if (!db.isOpen()) {
		         db = SQLiteDatabase.openDatabase(DataConstants.DATABASE_PATH, null, SQLiteDatabase.OPEN_READWRITE);
		         // since we pass db into DAO, have to recreate DAO if db is re-opened
		         categoryDao = new CategoryDao(db);
		         movieDao = new MovieDao(db);
		         movieCategoryDao = new MovieCategoryDao(db);
		      }
		   }

		   private void closeDb() {
		      if (db.isOpen()) {
		         db.close();
		      }
		   }

		   private void resetDb() {
		      Log.i(Constants.LOG_TAG, "Resetting database connection (close and re-open).");
		      closeDb();
		      SystemClock.sleep(500);
		      openDb();
		   }
   // we only expose methods app is actually using, and we can combine DAOs, with logic in one place	   
	@Override
	//wrap daos to get movie
	public Movie getMovie(long movieId) {
		// TODO Auto-generated method stub
		  Movie movie = movieDao.get(movieId);
	      if (movie != null) {
	    	 movie.getCategories().addAll(movieCategoryDao.getCategories(movie.getId()));
	      }
	      return movie;
	}

	@Override
	public List<Movie> getMovieHeaders() {
		// TODO Auto-generated method stub
		 return movieDao.getAll();
	}

	@Override
	public Movie findMovie(String name) {
		// TODO Auto-generated method stub
		 Movie movie = movieDao.find(name);
	      if (movie != null) {
	         movie.getCategories().addAll(movieCategoryDao.getCategories(movie.getId()));
	      }
	      return movie;
	}

	@Override
	//wrap for save movie
	public long saveMovie(Movie movie) {
		// TODO Auto-generated method stub
	    // here though, to keep it simpler, we use the DAOs directly 
	      long movieId = 0L;
	      // put it in a transaction, since we're touching multiple tables
	      /**
	       * A transaction ensures that if one part of our operation fails, 
	       * the entire thing will be rolled back. 
	       * This prevents us from ending up with an inconsistent state.
	       * it may not be appropriate for the DAO itself 
	       * (the manager has the contextual information to know a transaction is needed).
	       */
	      try {
	         db.beginTransaction();
	         // first save movie                                 
	         movieId = movieDao.save(movie);
	         // second, make sure categories exist, and save movie/category association
	         // (this makes multiple queries, but usually not many cats, could just save and catch exception too, but that's ugly)
	         if (movie.getCategories().size() > 0) {
	            for (Category c : movie.getCategories()) {
	               long catId = 0L;
	               Category dbCat = categoryDao.find(c.getName());
	               if (dbCat == null) {
	                  catId = categoryDao.save(c);
	               } else {
	                  catId = dbCat.getId();
	               }
	               MovieCategoryKey mcKey = new MovieCategoryKey(movieId, catId);
	               if (!movieCategoryDao.exists(mcKey)) {
	                  movieCategoryDao.save(mcKey);
	               }
	            }
	         }
	         db.setTransactionSuccessful();
	      } catch (SQLException e) {
	         Log.e(Constants.LOG_TAG, "Error saving movie (transaction rolled back)", e);
	         movieId = 0L;
	      } finally {
	         // an "alias" for commit
	         db.endTransaction();
	      }
	      return movieId;
	}

	@Override
	//wrap for delete movie
	public boolean deleteMovie(long movieId) {
		// TODO Auto-generated method stub
		 boolean result = false;
	      // NOTE switch this order around to see constraint error (foreign keys work)
	      try {
	         db.beginTransaction();
	         // make sure to use getMovie and not movieDao directly, categories need to be included
	         Movie movie = getMovie(movieId);
	         if (movie != null) {
	            for (Category c : movie.getCategories()) {
	               movieCategoryDao.delete(new MovieCategoryKey(movie.getId(), c.getId()));
	            }
	            movieDao.delete(movie);
	         }
	         db.setTransactionSuccessful();
	         result = true;
	      } catch (SQLException e) {
	         Log.e(Constants.LOG_TAG, "Error deleting movie (transaction rolled back)", e);
	      } finally {
	         db.endTransaction();
	      }
	      return result;
	}

	@Override
	public Cursor getMovieCursor() {
		// TODO Auto-generated method stub
		 // note that query MUST have a column named _id
	      return db.rawQuery("select " + MovieColumns._ID + ", " + MovieColumns.NAME + ", " + MovieColumns.THUMB_URL
	               + " from " + MovieTable.TABLE_NAME, null);
	}

	@Override
	public Category getCategory(long categoryId) {
		// TODO Auto-generated method stub
		return categoryDao.get(categoryId);
	}

	@Override
	public List<Category> getAllCategories() {
		// TODO Auto-generated method stub
		return categoryDao.getAll();
	}

	@Override
	public Category findCategory(String name) {
		// TODO Auto-generated method stub
		 return categoryDao.find(name);
	}

	@Override
	public long saveCategory(Category category) {
		// TODO Auto-generated method stub
		 return categoryDao.save(category);
	}

	@Override
	public void deleteCategory(Category category) {
		// TODO Auto-generated method stub
		 categoryDao.delete(category);
	}

}

Using the DataManager:

For instance the MyMovies activity is still the main ListView our application needs. But instead of using an Adapter that’s backed with List<String>, we change to one that uses List<Movie> and we get that data from our database as follows:
private MovieAdapter adapter;
private List<Movie> movies;
...
movies.addAll(app.getDataManager().getMovieHeaders());
adapter.notifyDataSetChanged();

A CursorAdapter is an adapter that can pull data from a database. Sometimes this is convenient, such as when multiple things might modify the underlying database. With a CursorAdapter, a ListView can manage the cursor and automatically update the view items
as data is added. One downside with CursorAdapter is that database
concepts such as Cursor leak into the logic of the activity (for example, when a list item is clicked). So usage of a CursorAdapter makes sense sometimes, and other
times it’s easier to do without it. For MyMoviesDatabase we’ve included both approaches in the source code for the project (see the comments in the MyMovies activity to switch between them).

抱歉!评论已关闭.