Hi I can't store values in the database DAO android. I have User.class:
public class User {
#PrimaryKey #NonNull
public String uid;
#ColumnInfo(name="name")
public String name;
public User(String uid, String name){
this.uid=uid;
this.name=name;
}
}
DAO database:
#Dao
public interface UserDAO {
#Query("select*from User")
List<User> getAll();
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(User users);
}
My Dtababase class is:
#Database(entities = {User.class},version=1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDAO userDAO();
}
My code
new Thread(new Runnable() {
#Override
public void run() {
try {
AppDatabase db = Room.databaseBuilder(activity.getApplicationContext(),
AppDatabase.class, "user_db").build();
UserDAO userDao = db.userDAO();
userDao.insertAll(new User("11","Melany"));
Log.d("main_activity_uid",
"ok you are pretty");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
Now my code don't store new User(i can't print "ok you are pretty").Why?
I have another question: i know that when you store data in the database you use threads but what do threads allow you to do?
A problem is that you always create in a new database instance instead of having a singleton database.
Single instance (source):
private static volatile WordRoomDatabase INSTANCE;
static WordRoomDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (WordRoomDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
WordRoomDatabase.class, "word_database")
.build();
}
}
}
return INSTANCE;
}
You need a thread because the Room database does not allow operations on the main thread to prevent blocking the UI (source)
Related
hello can I add data in the databse DAO from a class, or can I do it only from the mainthread?For example, if i have Database DAO:
public interface UserDAO {
#Query("select*from User")
List<User> getAll();
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(User...users);
}
and i have Appdatabase.class:
#Database(entities = {User.class},version=1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDAO userDAO();
}
i also have a class that initializes the DAO database:
Model_user {
public static synchronized Model_user getInstance() {
if (theinstance == null) {
theinstance = new Model_user();
}
return theinstance;
}
public static synchronized Model_user getInstance(Context c) {
if (theinstance == null) {
userDAOs = Room.databaseBuilder(c, AppDatabase.class, "userdb").build();
theinstance = new Model_user();
}
return theinstance;
}
private Model_user() {
user_list = new ArrayList<User>();
}
public void addUsers(User[] s) {
Log.d("main_activity_uid", "ok it start");
userDAOs.userDAO().insertAll(s);
user_list.addAll(Arrays.asList(s));
Log.d("main_activity_uid", "ok");
return;
}
Now in Utils.class , i can't add data;in Utils.class i have:
Model_user.getInstance(activity.getApplicationContext()).addUsers(u.toArray(new User[0]));
for store data in DAO database,but but it does not start.Why?How can i do? can i add data to the DAO database only from the main activity?
I've tried using the below code to load some data from my database and display them in an activity:
PhoneNumberViewModel phoneNumberViewModel =
new ViewModelProvider(WorksideContactCard.this).get(PhoneNumberViewModel.class);
phoneNumberViewModel
.getPhoneNumbersById(contactID)
.observe(this,
numbers -> {
phoneNumberList = numbers;
});
To be precise, I needn't observe the data changing, as the data will be refreshed if the activity is resumed. But the above code freezes my application, although I am accessing the DB using an AsyncTask in my other class.
Why could this be so?
EDIT:
PhoneNumberViewModel.class:
public class PhoneNumberViewModel extends AndroidViewModel {
private PhoneNumberRepository phoneNumberRepository;
private LiveData<List<PhoneNumber>> allPhoneNumbers;
public PhoneNumberViewModel(#NonNull Application application) {
super(application);
phoneNumberRepository = new PhoneNumberRepository(application);
allPhoneNumbers = phoneNumberRepository.getAllPhoneNumbersLive();
}
public void insert(PhoneNumber phoneNumber) {
System.out.println("[PhoneNumberRepository] Adding new phoneNumber");
phoneNumberRepository.insert(phoneNumber);
}
public void update(PhoneNumber phoneNumber) {
phoneNumberRepository.update(phoneNumber);
}
public void delete(PhoneNumber phoneNumber) {
phoneNumberRepository.delete(phoneNumber);
}
public LiveData<List<PhoneNumber>> getAllPhoneNumbers() {
return allPhoneNumbers;
}
public LiveData<List<PhoneNumber>> getPhoneNumbersById(long contactId)
throws ExecutionException, InterruptedException {
return phoneNumberRepository.getPhoneNumbersByContactId(contactId);
}
PhoneNumberRepository.class
public class PhoneNumberRepository {
private PhoneNumberDao phoneNumberDao;
private LiveData<List<PhoneNumber>> allPhoneNumbers;
private LiveData<List<PhoneNumber>> phoneNumbersByIdList;
public PhoneNumberRepository(Application application) {
WorksideDatabase database = WorksideDatabase.getInstance(application);
phoneNumberDao = database.phoneNumberDao();
allPhoneNumbers = phoneNumberDao.getAllPhoneNumbers();
}
...
public LiveData<List<PhoneNumber>> getAllPhoneNumbersLive() {
return allPhoneNumbers;
}
public void deleteAllPhoneNumbers() {
new DeleteAllPhoneNumbersAsyncTask(phoneNumberDao).execute();
}
public LiveData<List<PhoneNumber>> getPhoneNumbersByContactId(long id)
throws ExecutionException, InterruptedException {
return new SelectPhoneNumberByIdAsyncTask(phoneNumberDao, id).get();
}
private static class SelectPhoneNumberByIdAsyncTask
extends AsyncTask<Long, Void, LiveData<List<PhoneNumber>>> {
private PhoneNumberDao phoneNumberDao;
private Long ID;
private SelectPhoneNumberByIdAsyncTask(PhoneNumberDao phoneNumberDao, Long contactId) {
this.phoneNumberDao = phoneNumberDao;
ID = contactId;
}
#Override
protected LiveData<List<PhoneNumber>> doInBackground(Long... contactId) {
ID = contactId[0];
return phoneNumberDao.getPhoneNumbersById(ID);
}
}
...
}
PhoneNumberDao.class:
#Dao
public interface PhoneNumberDao {
#Insert
void insert(PhoneNumber phoneNumber);
#Update
void update(PhoneNumber phoneNumber);
#Delete
void delete(PhoneNumber phoneNumber);
// Probably uneeded method
#Query("DELETE FROM phone_numbers_table")
void deleteAllPhoneNumbers();
// Delete entry/entries by ID
#Query("DELETE FROM phone_numbers_table WHERE id = :phoneNumberId")
void deleteByPhoneNumberId(long phoneNumberId);
// Retrieve entry/entries by contact ID
// #Query("SELECT * FROM phone_numbers_table WHERE contact_id = :contactId")
// List<PhoneNumber> getPhoneNumbersById(long contactId);
// Retrieve all saved phone numbers in LiveData format
#Query("SELECT * FROM phone_numbers_table")
LiveData<List<PhoneNumber>> getAllPhoneNumbers();
#Query("SELECT * FROM phone_numbers_table WHERE contact_id = :contactId")
LiveData<List<PhoneNumber>> getPhoneNumbersById(long contactId);
#Query("SELECT * FROM phone_numbers_table")
List<PhoneNumber> getAll();
}
return new SelectPhoneNumberByIdAsyncTask(phoneNumberDao, id).get();
this here is your problem ,.get() call blocks the main thread
I would suggest switching to Kotlin and using Coroutines or here you can handle this using callbacks and not using AsyncTask.get() which blocks the main thread.Also BTW Async Task is going to be deprecated last I heard ,So keep that in mind too.
This is one solution the problem
public class Repository implements
SelectPhoneNumberByIdAsyncTask.OnPhoneNumberFound {
#Override
public void onPhoneNumberFound(LiveData<List<Object>> list) {
//you can get the data you want here
}
}
class SelectPhoneNumberByIdAsyncTask extends AsyncTask<Long, Void,
LiveData<List<PhoneNumber//Correct this>>> {
interface OnPhoneNumberFound{
void onPhoneNumberFound(LiveData<List<PhoneNumberDao>> list);
}
OnPhoneNumberFound mListener;
private PhoneNumberDao phoneNumberDao;
private Long ID;
private SelectPhoneNumberByIdAsyncTask(Object phoneNumberDao, Long contactId) {
this.phoneNumberDao = phoneNumberDao;
ID = contactId;
}
#Override
protected LiveData<List<Object>> doInBackground(Long... contactId) {
ID = contactId[0];
mListener.onPhoneNumberFound(phoneNumberDao.getPhoneNumbersById(ID));
return null;
}
}
use .execute() call now instead of .get()
I am at my last year at the university and working on my final project with a group of friends.
I am responsible on implementing the database (using google firestore in java) and i am trying to implement it using a design pattern.
I found the adapter quiet useful, as I can create an interface called:
GenericDB, which contains all the methods the database needs to use.
A concrete class, let's call her FirestoreDB which implements it,
and an Adapter, which also implements the GenericDB, and holds an Instance Of GenericDB as a variable, so I can choose at run time which db I will want to use (maybe in the future the db would change)
Here is some basic code:
public interface GenericDB {
boolean add(String... args);
boolean delete(String... args);
boolean get(String... args);
boolean changePassword(String... args);
}
public class FirestoreDB implements GenericDB {
private final Firestore db;
public FirestoreDB() {
FirestoreOptions firestoreOptions =
FirestoreOptions.getDefaultInstance().toBuilder()
.setProjectId(Constants.PROJECT_ID)
.build();
this.db = firestoreOptions.getService();
}
public boolean add(String... args) {
return true;
}
public boolean delete(String... args) {
return false;
}
public boolean get(String... args) {
return false;
}
public boolean changePassword(String... args) {
return false;
}
}
public class Adapter implements GenericDB {
private GenericDB db;
public Adapter(GenericDB db){
this.db = db;
}
public boolean add(String... args) {
return this.db.add(args);
}
public boolean delete(String... args) {
return db.delete(args);
}
public boolean get(String... args) {
return db.get(args);
}
public boolean changePassword(String... args) {
return db.changePassword(args);
}
}
public class DatabaseCreator {
public GenericDB getDB(DATABASE database) {
switch (database) {
case FIRESTORE:
return new FirestoreDB();
default:
return null;
}
}
DatabaseCreator database = new DatabaseCreator();
GenericDB db = database.getDB(EXTRA.DATABASE.FIRESTORE);
Adapter ad = new Adapter(db);
System.out.println(ad.add("1"));
Is this a good use of the adapter pattern?
Is this a good use of the adapter pattern?
What you call Adapter, is not an Adapter. Purpose of Adapter is to convert interface of the class to another interface expected by the client. But your 'adapter' implements the same GenericDB interface as the adaptee it wraps - no conversion happens here.
The closest one to Adapter is your FirestoreDB class. It is intended to convert the interface of Firestore to the interface GenericDB required by your code. Of course there is still small chance that Google will change Firestore to implement your GenericDB interface. Until then you should use an adapter.
To make FirestoreDB a proper adapter, you should pass adaptee (Firestore object) to the constructor of adapter, and later call it in the GenericDB methods implementation:
public class FirestoreAdapter implements GenericDB {
private final Firestore db;
public FirestoreAdapter(Firestore db) { // pass adaptee to adapter
this.db = db;
}
public boolean add(...) {
// DocumentReference docRef = db.collection(colName).document(docId);
// etc
}
// etc
}
This adapter could be passed to the code which expects GenericDB interface and knows nothing about Firestore interface:
FirestoreOptions firestoreOptions =
FirestoreOptions.getDefaultInstance().toBuilder()
.setProjectId(Constants.PROJECT_ID)
.build();
Firestore firestore = firestoreOptions.getService(); // but your code requires GenericDB
GenericDB db = new FirestoreAdapter(firestore); // adapt Firestore interface
// use db here as if Firestore was implementing your GenericDB interface
You can write adapters for another type of database in the same way. But... usually, you don't use some abstract database interface, because databases are very different with different sets of features. Trying to find some intersection of features supported by all databases might be not a great idea (unless you writing CosmosDB). Usually, you will work with higher-level abstractions, like Repositories.
You should use Command Design Pattern it more flexible than Adapter in your case
Example:
import java.util.HashMap;
import java.util.Map;
// demo for firestore
class Firestore {
private Map<Object, Object> map = new HashMap<>();
public void add(Object id, Object object) {
map.put(id, object);
}
public Object get(Object id) {
return map.get(id);
}
}
interface FirestoreAware {
void setFirestore(Firestore firestore);
}
enum CommandType {
ADD,
DELETE,
GET,
CHANGE_PASSWORD,
GET_USER
}
interface Command {
CommandType getType();
}
class GetCommand implements Command {
private int id;
public GetCommand id(int id) {
this.id = id;
return this;
}
public int getId() {
return id;
}
#Override
public CommandType getType() {
return CommandType.GET;
}
}
class AddCommand implements Command {
private int id;
private String jsonData;
public AddCommand id(int id) {
this.id = id;
return this;
}
public AddCommand jsonData(String jsonData) {
this.jsonData = jsonData;
return this;
}
public int getId() {
return id;
}
public String getJsonData() {
return jsonData;
}
#Override
public CommandType getType() {
return CommandType.ADD;
}
}
interface CommandHandler<C> {
Object handle(C cmd);
}
abstract class CommandFirestoreHandler<C>
implements CommandHandler<C>, FirestoreAware {
protected Firestore firestore;
#Override
public void setFirestore(Firestore firestore) {
this.firestore = firestore;
}
}
class AddCommandHandler extends CommandFirestoreHandler<AddCommand> {
#Override
public Object handle(AddCommand cmd) {
firestore.add(cmd.getId(), cmd.getJsonData());
return Boolean.TRUE;
}
}
class GetCommandHandler extends CommandFirestoreHandler<GetCommand> {
#Override
public Object handle(GetCommand cmd) {
return firestore.get(cmd.getId());
}
}
interface GenericDB {
<T> T execute(Command cmd);
}
class FirestoreDB implements GenericDB {
private final Firestore firestore;
private final Map<CommandType, CommandHandler> handlers;
public FirestoreDB() {
this(new Firestore());
}
public FirestoreDB(Firestore firestore) {
this.firestore = firestore;
this.handlers = new HashMap<>();
// demo add default command handlers
this.addHandler(CommandType.ADD, new AddCommandHandler());
this.addHandler(CommandType.GET, new GetCommandHandler());
}
public void addHandler(CommandType commandType, CommandHandler handler) {
if(handler instanceof FirestoreAware)
((FirestoreAware)handler).setFirestore(firestore);
this.handlers.put(commandType, handler);
}
#Override
public <T> T execute(Command cmd) {
CommandHandler handler = handlers.get(cmd.getType());
return (T)handler.handle(cmd);
}
}
class DatabaseCreator {
public GenericDB getDB(String database) {
switch (database) {
case "FIRESTORE":
return new FirestoreDB();
default:
return null;
}
}
}
public class GenericDBDemo {
public static void main(String[] args) {
DatabaseCreator database = new DatabaseCreator();
GenericDB db = database.getDB("FIRESTORE");
db.execute(new AddCommand().id(1).jsonData("{'_id': 1, 'name' : 'hello world'}"));
System.out.println(db.execute(new GetCommand().id(1)).toString());
}
}
How can DAO be used with realm? Because when in my activity I try to set members of my model class I get an exception :
java.lang.IllegalStateException: Changing Realm data can only be done from inside a transaction.
I know that using realm.executeTransaction fixes the issue, but the code in my activity is no more database-agnostic because it will countain code that is specific to low level database communication. So later if I want to change database, the refactoring will cost a lot of time and work... Besides, I will have to handle in all my activities a reference to Realm.getDefaultInstance();
Here is sample of code of my activity
protected void onCreate(Bundle savedInstanceState)
{
mBook = mBookDaoImpl.getBookById(bookId);
}
// Later in the code
private void saveBook(String name)
{
mBook.setName(name);
}
Here is my model class
public class Book extends RealmObject
{
#Required
#PrimaryKey
private String id;
private String name;
public Book() {
}
public Book(String id, String name) {
this.id = id;
this.name = name;
}
// getter setter methods
}
Here is my DAO interface :
public interface BookDao
{
List<Book> getAllBooks();
Book getBookByIsbn(int isbn);
void saveBook(Book book);
void deleteBook(Book book);
}
And finally is my implementation :
public class BookDaoImpl implements BookDao
{
private static BookDaoImpl INSTANCE = null;
private Realm mRealm;
private BookDaoImpl()
{
mRealm = Realm.getDefaultInstance();
}
public static BookDaoImpl getInstance()
{
if (INSTANCE == null)
INSTANCE = new BookDaoImpl();
return INSTANCE;
}
#Override
public List<Book> getAllBooks()
{
return mRealm.where(Book.class).findAll();
}
#Override
public Book getBookById(String id)
{
return mRealm.where(Book.class).equalTo("id", id).findFirst();
}
#Override
public void saveBook(final Book book)
{
mRealm.executeTransaction(new Realm.Transaction()
{
#Override
public void execute(Realm realm)
{
if (book.getId() == null)
book.setId(UUID.randomUUID().toString());
realm.copyToRealmOrUpdate(book);
}
});
}
#Override
public void deleteBook(final Book book)
{
mRealm.executeTransaction(new Realm.Transaction()
{
#Override
public void execute(Realm realm)
{
mRealm.where(Counter.class).equalTo("id", book.getId())
.findFirst()
.deleteFromRealm();
}
});
}
}
Realm's getInstance() method returns a thread-local, reference counted instance which must be paired with a close() call, so your DAO implementation probably won't suit what you expect.
If you use my library Realm-Monarchy which I created specifically for making it easier to "abstract Realm away", then you can implement your DAO like this:
public class BookDaoImpl implements BookDao
{
private static BookDaoImpl INSTANCE = null;
private Monarchy monarchy;
private BookDaoImpl(Monarchy monarchy)
{
this.monarchy = monarchy;
}
public static BookDaoImpl getInstance(Monarchy monarchy)
{
if (INSTANCE == null) {
synchronized(BookDaoImpl.class) {
if(INSTANCE == null) {
INSTANCE = new BookDaoImpl(monarchy);
}
}
}
return INSTANCE;
}
#Override
public List<Book> getAllBooks()
{
return monarchy.fetchAllCopiedSync((realm) -> realm.where(Book.class));
}
#Override
public Book getBookById(final String id)
{
List<Book> books = monarchy.fetchAllCopiedSync((realm) -> realm.where(Book.class).equalTo("id", id));
if(books.isEmpty()) {
return null;
} else {
return books.get(0);
}
}
#Override
public void saveBook(final Book book)
{
monarchy.runTransactionSync((realm) -> {
if (book.getId() == null)
book.setId(UUID.randomUUID().toString());
realm.insertOrUpdate(book);
});
}
#Override
public void deleteBook(final Book book)
{
monarchy.runTransactionSync((realm) -> {
realm.where(Counter.class).equalTo("id", book.getId())
.findFirst()
.deleteFromRealm();
});
}
}
P.S.: you're throwing away a lot of power/functionality if you return List<T> synchronously, instead of an observable like LiveData<List<T>> (or originally, RealmResults<T>).
I made a DAO class with factory method and the specific DAO returns singleton, a single instance of the DAO. But I been tracing it and its being created but I try to call on it and it always null.
Just to explain the storage factory
I call on DAOFactory to get RAMDAOFactory to get to RAMUserDAO
If there is better way to handle RAM, Serialization and SQL type DAOs or CRUD please let me know.
class that I'm calling the storage from.
public class Registration
{
private UserDAO userStorage;
private static Logger log = LogClass.getLog();
Registration(DAOFactoryType typeDataStorage)
{
userStorage = DAOFactory.getDAOFactory(typeDataStorage).getUserDAO();
log.trace("insdie Reg");
}
void addUser(String userName, String password, UserType... args)
throws Exception
{
List<UserType> userTypes = new ArrayList<UserType>(args.length);
for (UserType userType : args)
{
log.trace("userType " + userType);
userTypes.add(userType);
}
User newUser = new DefaultUser(userName, password, userTypes);
log.trace("newUser " + newUser);
if (userStorage != null)
{
userStorage.insert(newUser);
}
else
{
log.trace("userStorage null");
}
}
}
This is my DAOFactory
public abstract class DAOFactory
{
private static Logger log = LogClass.getLog();
public abstract TradeDAO getTradeDAO();
public abstract UserDAO getUserDAO();
public abstract LogDAO getLogDAO();
public static DAOFactory getDAOFactory(DAOFactoryType factoryType)
{
switch (factoryType)
{
case SQL:
return new SQLDAOFactory();
case RAM:
log.trace("insdie RAM");
return new RAMDAOFactory();
case SERIAL:
return new SerialDAOFactory();
default:
return null;
}
}
}
RAMDAOFactory
public class RAMDAOFactory extends DAOFactory
{
private static Logger log = LogClass.getLog();
private TradeDAO ramTradeDAO;
private UserDAO ramUserDAO;
private LogDAO ramLogDAO;
public RAMDAOFactory()
{
log.trace("insdie RAMDAOFactory");
RAMUserDAO.getRAMUserDAO();
RAMTradeDAO.getRAMTradeDAO();
RAMLogDAO.getRAMLogDAO();
}
#Override
public TradeDAO getTradeDAO()
{
return ramTradeDAO;
}
#Override
public UserDAO getUserDAO()
{
return ramUserDAO;
}
#Override
public LogDAO getLogDAO()
{
return ramLogDAO;
}
}
This is my UserDAO
public class RAMUserDAO implements UserDAO
{
/*
* Map<Integer, List<byte[]>> userHash; List<byte[]> arrayHashSalt;
*/
private static RAMUserDAO userDAO = null;
private Map<String, User> userList;
private static Logger log = LogClass.getLog();
private RAMUserDAO()
{
userList = new HashMap<String, User>();
log.trace("insdie RAMUserDAO constructor");
}
public static RAMUserDAO getRAMUserDAO()
{
log.trace("insdie getRAMUserDAO");
if(userDAO == null) {
log.trace("insdie new RAMUserDAO()");
userDAO = new RAMUserDAO();
}
/*if (userDAO == null)
{
synchronized (RAMUserDAO.class)
{
if (userDAO == null)
{
userDAO = new RAMUserDAO();
}
}
}*/
return userDAO;
}
#Override
public void insert(User user) throws Exception
{
log.trace("insdie insert");
userList.put(user.getUserName(), user);
}
}
The oversight was in RAMDAOFactory and fix was:
public class RAMDAOFactory extends DAOFactory
{
private static Logger log = LogClass.getLog();
#Override
public TradeDAO getTradeDAO()
{
return RAMTradeDAO.getRAMTradeDAO();
}
#Override
public UserDAO getUserDAO()
{
return RAMUserDAO.getRAMUserDAO();
}
#Override
public LogDAO getLogDAO()
{
return RAMLogDAO.getRAMLogDAO();
}
}
You've called the methods
public RAMDAOFactory()
{
log.trace("insdie RAMDAOFactory");
RAMUserDAO.getRAMUserDAO();
RAMTradeDAO.getRAMTradeDAO();
RAMLogDAO.getRAMLogDAO();
}
but you haven't assigned their value to anything
#Override
public UserDAO getUserDAO()
{
return ramUserDAO;
}
Either always call
RAMUserDao.getRAMUserDAO();
when you want to return the UserDAO or assign it to ramUserDAO and return that.