I encountered a problem while learning the room database, I need help I have created the room database well and write the employees name in recyclerView There is a query that I want to implement, and it collects all employees' salaries and displays them in recyclerView
I will shorten the codes
The Problem
Attempt to invoke virtual method 'void com.app.roomdatabase.database.MyViewModel.getSalariesSum(long, com.app.roomdatabase.database.GetSalarySumListener)' on a null object reference
at com.app.roomdatabase.Adapters.EmployeeSalaryAdapter$viewHolder.bind(EmployeeSalaryAdapter.java:68)
at com.app.roomdatabase.Adapters.EmployeeSalaryAdapter.onBindViewHolder(EmployeeSalaryAdapter.java:49)
at com.app.roomdatabase.Adapters.EmployeeSalaryAdapter.onBindViewHolder(EmployeeSalaryAdapter.java:19)
The query that i used for retrieve salaries
@Query("select sum(amount) from salary where empId=:emp_id")
double getSalariesSum(long emp_id);
Repository
public void getSalariesSum(long emp_id, GetSalarySumListener listener ) {
MyRoomDatabase.databaseWriteExecuter.execute(new Runnable() {
@Override
public void run() {
double value = salaryDAO.getSalariesSum(emp_id);
listener.onValueSubmit(value);
}
});
**GetSalarySumListener **
public interface GetSalarySumListener {
void onValueSubmit(double value);}
ViewModel
public void getSalariesSum(long emp_id, GetSalarySumListener listener ){
repository.getSalariesSum(emp_id,listener);
}
Adapter
public class EmployeeSalaryAdapter extends RecyclerView.Adapter<EmployeeSalaryAdapter.viewHolder> {
List <Employee> employees;
private MyViewModel mvm;
public EmployeeSalaryAdapter(List <Employee> employees, MyViewModel mvm) {
this.employees = employees;
this.mvm = mvm;
}
public List <Employee> getEmployees() {
return employees;
}
public void setEmployees(List <Employee> employees) {
this.employees = employees;
notifyDataSetChanged();
}
@NonNull
@Override
public viewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new viewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.emp_sal_item,parent,false));
}
@Override
public void onBindViewHolder(@NonNull EmployeeSalaryAdapter.viewHolder holder, int position) {
Employee employee = employees.get(position);
holder.bind(employee,mvm);
}
@Override
public int getItemCount() {
return employees.size();
}
public class viewHolder extends RecyclerView.ViewHolder {
EmpSalItemBinding binding;
Employee employee;
public viewHolder(@NonNull View itemView) {
super(itemView);
binding= EmpSalItemBinding.bind(itemView);
}
public void bind(Employee employee, MyViewModel mvm) {
this.employee = employee;
binding.empNameTv.setText(employee.getName());
mvm.getSalariesSum(employee.getId(), new GetSalarySumListener() {
@Override
public void onValueSubmit(double value) {
binding.empSalaryTv.setText(String.valueOf(value));
}
});
}
}
I'd suggest that rather than getting the Employees (Employee list) and then try to get the Salary for each employee, that you instead have a POJO, say EmployeeWithSalary that embeds (using the @Embedded
annotation) the Employee class with the salary. Along the lines of:-
class EmployeeWithSalary {
@Embedded
Employee employee;
double salary;
}
You could then have a query in the respective @Dao
annotated interface/abstract class, such as :-
@Query("SELECT *, (SELECT sum(amount) FROM salary WHERE empId=employee.id) AS salary FROM employee")
abstract List<EmployeeWithSalary> getEmployeesWithSalary();
You could then have your adapter use the List and the salary would then be available without the need to access the database a second time to extract the salary.
As an example/demo consider the following based upon your code :-
@Entity
class Employee {
@PrimaryKey
Long id = null;
@ColumnInfo(index = true)
String employeeName;
// etc...
Employee(){}
@Ignore
Employee(String employeeName) {
this.id = null;
this.employeeName = employeeName;
}
@Ignore
Employee(Long employeeId, String employeeName) {
this.id = employeeId;
this.employeeName = employeeName;
}
}
@Entity
class Salary {
@PrimaryKey
Long salaryId = null;
long empId;
double amount;
// etc ....
Salary(){}
@Ignore
Salary(long empId, double amount) {
this.salaryId = null;
this.empId = empId;
this.amount = amount;
}
}
The new POJO
class EmployeeWithSalary {
@Embedded
Employee employee;
double salary;
}
A basic @Dao
annotated abstract class (could be an interface, abstract classes can include methods with bodies)
@Dao
abstract class AllDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract long insert(Employee employee);
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract long insert(Salary salary);
@Query("SELECT *, (SELECT sum(amount) FROM salary WHERE empId=employee.id) AS salary FROM employee")
abstract List<EmployeeWithSalary> getEmployeesWithSalary();
}
And to demonstrate (note that .allowMainThreadQueries
has been used in the databaseBuilder for brevity of the demo) the following in an activity:-
db = TheDatabase.getInstance(this);
dao = db.getAllDao();
long fredId = dao.insert(new Employee("Fred"));
long maryId = dao.insert(new Employee("Mary"));
dao.insert(new Salary(fredId,111.33));
dao.insert(new Salary(fredId,222.66));
dao.insert(new Salary(fredId,333.999));
dao.insert(new Salary(maryId,115.55));
dao.insert(new Salary(maryId,230.10));
for (EmployeeWithSalary ews: dao.getEmployeesWithSalary()) {
Log.d("EMPINFO","Employee is " + ews.employee.employeeName + " Salary is $" + ews.salary);
}
and the log includes:-
D/EMPINFO: Employee is Fred Salary is $667.989
D/EMPINFO: Employee is Mary Salary is $345.65