从服务器获取的数据在房间数据库中复制了多次
Data fetched from server is copying multiple times in room database
在我的应用程序中,我将房间用作数据库cache.I我正在使用改造库从服务器获取数据并将数据保存在房间数据库中,并在回收器视图中显示来自房间的相同数据。
问题: 每当 activity 启动时,它都会从服务器获取数据并将其保存在房间数据库中,因为相同的数据在回收器视图中多次显示。
我想要什么:我想知道如何检查房间中已经存在的数据是否不应在房间中保存多次,以及是否应该在服务器上更新一些新数据获取新数据并将其保存到房间数据库中。
这是我目前所做的:
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();
}
在MainActivity.java userRepository.getUserList()
从服务器获取数据并将其保存在房间数据库中。
有人请让我知道我怎样才能得到想要的结果。任何帮助将不胜感激。
谢谢
考虑查看此房间注释:
@Insert(onConflict = OnConflictStrategy.REPLACE)
有了这个,如果你插入任何已经存在的东西,它会简单地替换它。
你打电话的地方:
@Override
public void onChanged(List<User> users) {
adapter.setUserList(users);
}
首先尝试清除适配器列表
如@a_local_nobody所说,将其添加到您的 Dao 中。
@Insert(onConflict = OnConflictStrategy.REPLACE)
但这并不能完全解决您的问题。如果你看到 SQLite doc 它说
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.
您的实体定义了一个主键,但它被标记为 autoGenerate = true
。因此,每当您创建一个新的 User
实例并在您的 Dao Room 上调用 insert
时,都会生成一个新的主键。此 id
与您数据库中的 "duplicate" User
不冲突,因此 Room 很乐意在不触及之前的 "duplicate" 版本的情况下插入此版本。
解决方案是从后端获取主键或对 User
实体的一个字段进行 UNIQUE
约束。
任何展望未来的人。最好的方法是在插入新数据之前删除 table。例如
在你的@Dao class 中定义@delete 查询
@Query("DELETE FROM Users")
void deleteAllUsersData();
并在每次将新数据插入数据库之前调用此查询。如果您想要相同的主键,请将@Primary key autogenerate 设置为 false。
在我的应用程序中,我将房间用作数据库cache.I我正在使用改造库从服务器获取数据并将数据保存在房间数据库中,并在回收器视图中显示来自房间的相同数据。
问题: 每当 activity 启动时,它都会从服务器获取数据并将其保存在房间数据库中,因为相同的数据在回收器视图中多次显示。
我想要什么:我想知道如何检查房间中已经存在的数据是否不应在房间中保存多次,以及是否应该在服务器上更新一些新数据获取新数据并将其保存到房间数据库中。
这是我目前所做的:
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();
}
在MainActivity.java userRepository.getUserList()
从服务器获取数据并将其保存在房间数据库中。
有人请让我知道我怎样才能得到想要的结果。任何帮助将不胜感激。
谢谢
考虑查看此房间注释:
@Insert(onConflict = OnConflictStrategy.REPLACE)
有了这个,如果你插入任何已经存在的东西,它会简单地替换它。
你打电话的地方:
@Override
public void onChanged(List<User> users) {
adapter.setUserList(users);
}
首先尝试清除适配器列表
如@a_local_nobody所说,将其添加到您的 Dao 中。
@Insert(onConflict = OnConflictStrategy.REPLACE)
但这并不能完全解决您的问题。如果你看到 SQLite doc 它说
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.
您的实体定义了一个主键,但它被标记为 autoGenerate = true
。因此,每当您创建一个新的 User
实例并在您的 Dao Room 上调用 insert
时,都会生成一个新的主键。此 id
与您数据库中的 "duplicate" User
不冲突,因此 Room 很乐意在不触及之前的 "duplicate" 版本的情况下插入此版本。
解决方案是从后端获取主键或对 User
实体的一个字段进行 UNIQUE
约束。
任何展望未来的人。最好的方法是在插入新数据之前删除 table。例如
在你的@Dao class 中定义@delete 查询
@Query("DELETE FROM Users")
void deleteAllUsersData();
并在每次将新数据插入数据库之前调用此查询。如果您想要相同的主键,请将@Primary key autogenerate 设置为 false。