Search code examples
androidretrofit2android-room

Data fetched from server is copying multiple times in room database


In my app I am using room as a database cache.I am fetching data from server using retrofit library and saving the data inside room database and showing the same data from room in recycler view.

Problem: Whenever activity starts it fetches data from server and saving it in room database due to which same data showing multiple times in recycler view.

What I want: I want to know how can I check if data already present in room should not save multiple time in room and if some new data updated on server it should fetch new data and save it to the room database.

This is what I have done so far:

UserDao.java

@Dao
public interface UserDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
void Insert(User... users);

@Query("SELECT * FROM Users")
LiveData<List<User>> getRoomUsers();

}

User.java

@Entity(tableName = "Users")
public class User {

@PrimaryKey
private String id;

@ColumnInfo(name = "name")
@SerializedName("name")
@Expose
private String name;

@ColumnInfo(name = "age")
@SerializedName("age")
@Expose
private String age;

public User(String id,String name, String age) {
    this.id = id; 
    this.name = name;
    this.age = age;
}

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getAge() {
    return age;
}

public void setAge(String age) {
    this.age = age;
}
}

UserRepository.java

public class UserRepository {

private Context context;
private UserDb userDb;
private LiveData<List<User>> listLiveData;

public UserRepository(Context context) {
    this.context = context;
    userDb = UserDb.getInstance(context);
    listLiveData = userDb.userDao().getRoomUsers();
}

public void getUserList(){

          Retrofit retrofit = RetrofitClient.getInstance();
          ApiService apiService = retrofit.create(ApiService.class);

          Call<List<User>> userList = apiService.getUser();

          userList.enqueue(new Callback<List<User>>() {
              @Override
              public void onResponse(Call<List<User>> call, final Response<List<User>> response) {

                  Completable.fromAction(new Action() {
                          @Override
                          public void run() throws Exception {

                              if(response.body() != null) {

                                  List<User> list = response.body();

                                  for (int i = 0; i < list.size(); i++) {

                                      String names = list.get(i).getName();
                                      String age = list.get(i).getAge();
                                      String id = UUID.randomUUID().toString();


                                      User user = new User(id,names,age);

                                      userDb.userDao().Insert(user);
                                  }

                              }

                          }
                      }).subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new CompletableObserver() {
                            @Override
                            public void onSubscribe(Disposable d) {

                            }

                            @Override
                            public void onComplete() {

                                Toast.makeText(context,"Data inserted",Toast.LENGTH_SHORT).show();
                            }

                            @Override
                            public void onError(Throwable e) {

                                Toast.makeText(context,e.getMessage(),Toast.LENGTH_SHORT).show();
                            }
                        });


              }

              @Override
              public void onFailure(Call<List<User>> call, Throwable t) {
                  Toast.makeText(context,t.getMessage(),Toast.LENGTH_LONG).show();
              }
          });

}

public LiveData<List<User>> getRoomUsers(){

    return listLiveData;
}
}

UserViewModel.java

public class UserViewModel extends AndroidViewModel {

private UserRepository repo;
private LiveData<List<User>> listLiveData;

public UserViewModel(@NonNull Application application) {
    super(application);

    repo = new UserRepository(application);
    listLiveData = repo.getRoomUsers();

}

public LiveData<List<User>> getListLiveData() {
    return listLiveData;
}
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

UserRepository userRepository;
RecyclerView recyclerView;
UserViewModel userModel;
List<User> userList;
UserAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    userRepository = new UserRepository(this);
    userModel = ViewModelProviders.of(this).get(UserViewModel.class);

    recyclerView = findViewById(R.id.recyclerView);
    recyclerView.setHasFixedSize(true);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

    userList = new ArrayList<>();

    adapter = new UserAdapter(userList,this);
    recyclerView.setAdapter(adapter);

    userModel.getListLiveData().observe(this, new Observer<List<User>>() {

        @Override
        public void onChanged(List<User> users) {
            adapter.setUserList(users);
        }
    });

    FloatingActionButton fab = findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent i = new Intent(MainActivity.this,AddUser.class);
            startActivity(i);
        }
    });

    userRepository.getUserList();
}

In MainActivity.java userRepository.getUserList() fetches data from server and save it in room database.

Some one please let me know how can I get desired result. Any help would be appreciated.

THANKS


Solution

  • As @a_local_nobody says add this to your Dao.

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    

    But this will not fully solve your problem. If you see SQLite doc it says

    The ON CONFLICT clause applies to UNIQUE, NOT NULL, CHECK, and PRIMARY KEY constraints. The ON CONFLICT algorithm does not apply to FOREIGN KEY constraints.
    

    Your entity defines a Primary Key but it is tagged with autoGenerate = true. Due to this whenever you create a new User instance and calling insert on your Dao Room is generating a new primary key. This id does not conflict with the "duplicate" User in your db, and hence Room is happy to insert this one without touching the previous "duplicate" version.

    The solution is to have the Primary Key from the backend or have a UNIQUE constraint on one field of User entity.