【问题标题】:Room - Embedded object and the where clause with LiveDataRoom - 嵌入式对象和带有 LiveData 的 where 子句
【发布时间】:2023-02-04 05:09:29
【问题描述】:

这是我上一个的延续,但我终于想通了(摆脱了重复问题)。

Android Room Relationship duplicating information

客户表

    @Entity(tableName = "customer_table")
    public class Customer {
      @ColumnInfo(name = "Customer_Serial", index = true)
      @PrimaryKey
      private int customerSerial;
    
      @ColumnInfo(name = "Customer_Sort", index = true)
      private String customerSort;

      @ColumnInfo(name = "Customer_Name")
      private String customerName;
    
      public Customer(int customerSerial, String customerName) {
        this.customerSerial = customerSerial;
        this.customerName = customerName;
        this.customerSort = String.format(Locale.ENGLISH, "%d-%d", new Date().getTime(), customerSerial);
      }
    }

发票表

    @Entity(tableName = "invoice_table")
    public class Invoice {
      @ColumnInfo(name = "Invoice_Number", index = true)
      @PrimaryKey
      private int invoiceNumber;
    
      @ColumnInfo(name = "Customer_Serial")
      private int customerSerial;
    
      @ColumnInfo(name = "Invoice_Sort", index = true)
      private String invoiceSort;

      @ColumnInfo(name = "Delivery_Status")
      private int deliveryStatus;

      public Invoice(int invoiceNumber, int customerSerial) {
        this.invoiceNumber = invoiceNumber;
        this.customerSerial = customerSerial;
        this.invoiceSort = String.format(Locale.ENGLISH, "%d-%d", new Date().getTime(), invoiceNumber)
      }

      public void setDeliveryStatus(int deliveryStatus) {
        this.deliveryStatus = deliveryStatus;
      }

      public int getDeliveryStatus() { return deliveryStatus; }
    }

客户发票关系

    public class CustomerInvoice {
      @Embedded public Customer customer;
      @Relation(
        parentColumn = "Customer_Serial",
        entityColumn = "Customer_Serial"
        entity = Invoice.class
      )
      public List<Invoice> invoices;
    }

   public abstract class InvoiceDao {
     @Transaction
     @Query("SELECT * FROM invoice_table " +
            "JOIN customer_table " +
            "ON invoice_table.Debtor_Ser_No = customer_table.Customer_Serial " +
            "WHERE invoice_table.Delivery_Status = :deliveryStatus " +
            "GROUP BY customer_table.Customer_Serial " +
            "ORDER BY customer_table.Customer_Sort, invoice_table.Invoice_Sort")
    abstract public LiveData<List<CustomerInvoices>> getCustomerInvoices(int deliveryStatus);

    abstract public void insert(Invoice... invoice);

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract public void insertCustomer(Customer... customer);
   }

视图模型public LiveData<List> getCustomerInvoices(int deliveryStatus) { return dao.getCustomerInvoices(); }

测试

    Invoice invoice1 = new Invoice(1234, 1);
    Invoice invoice2 = new Invoice(1235, 1);
    Invoice invoice3 = new Invoice(2468, 2);
    Invoice invoice4 = new Invoice(2469, 2);

    Customer customer1 = new Customer(1, "Customer 1");
    Customer customer2 = new Customer(2, "Customer 2");

    dao.insertCustomer(customer1);
    dao.insertCustomer(customer2);
    dao.insert(invoice1);
    dao.insert(invoice2);
    dao.insert(invoice3);
    dao.insert(invoice4);

    invoice1.setDeliveryStatus(0);
    invoice2.setDeliveryStatus(0);
    invoice3.setDeliveryStatus(0);
    invoice4.setDeliveryStatus(0);
    viewModel.getCustomerInvoices2(0).observe(getViewLifeCycleOwner(), list -> { ... });

如果我调试观察者的输出,它会正确返回 2 个客户,每个客户有 2 张发票。

但是,如果我这样做

测试2

   invoice1.setDeliveryStatus(1);
   viewModel.getCustomerInvoices2(1).observe(getViewLifeCycleOwner(), list -> { ... });

它返回 1 个客户和 2 张发票,而不是 1 个客户和 1 张发票,因为该客户的第二张发票的交付状态仍然为 0。

我意识到问题出在 CustomerInvoice 关系中,它忽略了 invoice_table 本身的 where 子句(它仍然完美地执行 customer where 子句)。

但是,我似乎无法全神贯注地解决它。

我已经在 Google 上搜索了很长一段时间,我知道这是因为它基本上只是在做“让客户至少有一张交货状态正确的发票”,然后它在做“为这个客户获取所有发票” ,几乎所有我能找到的东西都提供了根本不涉及 LiveData 的基本示例,我需要它才能使用 LiveData。

我试图让它工作的众多尝试之一是在视图模型本身中做大量的跑腿工作。

    @Query("SELECT * FROM customer_table " +
            "JOIN invoice_table " +
            "ON customer_table.Customer_Serial = invoice_table.Debtor_Ser_No " +
            "WHERE invoice_table.Delivery_Status = :deliveryStatus " +
            "GROUP BY customer_table.Customer_Serial ORDER BY customer_table.Customer_Sort")
    abstract public Maybe<List<Customer>> getCustomersByDeliveryStatus(int deliveryStatus);

    @Query("SELECT * FROM invoice_table " +
            "WHERE invoice_table.Debtor_Ser_No = :debtorSerial " +
            "AND invoice_table.Delivery_Status = :deliveryStatus " +
            "ORDER BY invoice_table.Invoice_Sort")
    abstract public Single<List<Invoice>> getCustomerInvoicesByDeliveryStatus(int debtorSerial, int deliveryStatus);

视图模型

public LiveData<List<Map<Customer, List<Invoice>>>> getCustomerInvoices2(int deliveryStatus) {
        MutableLiveData<List<Map<Customer, List<Invoice>>>> liveCustomerInvoices = new MutableLiveData<>();


        List<Map<Customer, List<Invoice>>> listCustomerInvoices = new ArrayList<>();

        mInvoiceDao
                .getCustomersByDeliveryStatus(deliveryStatus)
                .subscribeOn(Schedulers.io())
                .subscribe(
                        (customers) -> {
                            for (Customer customer : customers) {
                                mInvoiceDao.getCustomerInvoicesByDeliveryStatus(
                                        customer.getCustomerSerial(),
                                        deliveryStatus
                                ).subscribeOn(Schedulers.io())
                                        .subscribe(
                                                (invoices) -> {
                                                    listCustomerInvoices.add(Collections.singletonMap(customer, invoices));
                                                }
                                        );
                            }
                            liveCustomerInvoices.postValue(listCustomerInvoices);
                        }, throwable -> Log.e("Error", "Error")
                );

        return liveCustomerInvoices;
    }

虽然它确实有效(在不同程度上,LiveData 不会立即更新,所以有时它什么都不显示,有时它只显示一件事,直到我刷新显示),而我的 recyclerview 显示了我需要它显示的内容,它不维护基于必须维护的“Customer_Sort”和“Invoice_Sort”的订单。

我也明白为什么,这是因为“地图”不能保证顺序。

【问题讨论】:

    标签: android android-room android-livedata rx-java3


    【解决方案1】:

    我认为第一个问题是,当您有 @Embedded@Relation 时,@Embedded 被视为父母(客户)。那就是 Room 基本上(起初)忽略了孩子(发票)。

    正如@Embedded/@Relation所指示的,从客户的角度来看,当 Room 考虑它时,您似乎是从发票的角度考虑这个问题。

    一旦 Room(理论上)获得了父项(客户),它就会从对象的角度考虑这一点并获得所有子项(发票),而不管影响检索和构建的子项的 SQL(例如 WHERE ORDER)完整的对象s(父级的所有子级)。

    WHERE 和 ORDER 仅在更改父母的数量时才会产生影响。

    这基本上是一种方便的方法。

    要影响子项(发票),修剪它们,如果使用 Customer(@Embedded)Invoice(@Realtion) POJO 需要一种覆盖房间处理的方法,则对它们进行排序。

    另一个问题是您的测试代码更改了 Invoice 对象(例如 invoice1.setDeliveryStatus(0);),但没有将该更改应用于数据库。因此,如果您从数据库中提取,则不会应用这些更改。

    不改变客户发票班级。考虑以下:-

    getters 和 setters 添加到顾客发票类。

    发票道转变为:-

    @Dao
    public abstract class InvoiceDao {
    
       /*
       @Transaction
       @Query("SELECT * FROM invoice_table " +
               "JOIN customer_table " +
               "ON invoice_table.Debtor_Ser_No = customer_table.Customer_Serial " +
               "WHERE invoice_table.Delivery_Status = :deliveryStatus " +
               "GROUP BY customer_table.Customer_Serial " +
               "ORDER BY customer_table.Customer_Sort, invoice_table.Invoice_Sort")
       abstract public LiveData<List<CustomerInvoice>> getCustomerInvoices(int deliveryStatus);
    
        */
    
       @Insert(onConflict = OnConflictStrategy.IGNORE)
       abstract public void insert(Invoice... invoice);
       @Insert(onConflict = OnConflictStrategy.IGNORE)
       abstract public void insertCustomer(Customer... customer);
       @Update(onConflict = OnConflictStrategy.IGNORE)
       abstract public void updateInvoice(Invoice... invoices);
    
       @Query("SELECT customer_table.* " + /* Room does not use the invoice table columns, they are not needed */
               "FROM customer_table " +
               "JOIN invoice_table ON invoice_table.customer_serial = customer_table.Customer_Serial " +
               "WHERE invoice_table.Delivery_Status = :deliveryStatus " +
               "GROUP BY customer_table.Customer_Serial ORDER BY customer_table.Customer_Sort, invoice_table.Invoice_Sort")
       abstract public List<Customer> getApplicableCustomers(int deliveryStatus);
       @Query("SELECT * FROM invoice_table WHERE delivery_status=:deliveryStatus AND customer_serial=:customerSerial ORDER BY invoice_sort" )
       abstract List<Invoice> getApplicableInvoicesForCustomer(int deliveryStatus, int customerSerial);
    
    
       @Transaction /* do in a single transaction */
       @Query("") /* trick room so it applies transaction processing logic*/
       public List<CustomerInvoice> getCustomerInvoices(int deliveryStatus) {
          ArrayList<CustomerInvoice> rv = new ArrayList<>();
          for(Customer c: getApplicableCustomers(deliveryStatus)) {
             CustomerInvoice ci = new CustomerInvoice();
             ci.customer = c;
             ci.invoices = getApplicableInvoicesForCustomer(deliveryStatus,c.getCustomerSerial());
             rv.add(ci);
          }
          return rv;
       }
    }
    
    • 获取客户发票方法注释掉
    • 更新发票添加的方法
    • 获取适用客户添加的方法
      • 类似于获取客户发票但只获取 Customer 字段/列,因为 Room 不使用 Invoice(SQL 可以相应地进行修剪)。
      • 而不是 Debtor_Ser_No customer_serial 硬编码。
    • getApplicableInvoicesForCustomer添加的方法(用于获取适当的发票)
    • 获取客户发票方法替换为使用带有获取 Customers 的 body 的方法,就像 Room 一样,但随后获取所需的子项(发票)。这个结合了getApplicable??方法并返回 CustomerInvoice 对象列表。

    为了演示修改后的测试,a) 更新数据库中的发票 delivery_status 值和 b) 使用一种方法记录返回的 CustomerInvoices,允许传递所需的交付状态:-

    • 笔记为简洁起见,使用了 mainThread。

      公共类 MainActivity 扩展 AppCompatActivity {

        TheDatabase db;
        InvoiceDao dao;
        private static final String TAG = "DBINFO";
      
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
      
            db = TheDatabase.getInstance(this);
            dao = db.getInvoiceDao();
      
            Invoice invoice1 = new Invoice(1234, 1);
            Invoice invoice2 = new Invoice(1235, 1);
            Invoice invoice3 = new Invoice(2468, 2);
            Invoice invoice4 = new Invoice(2469, 2);
      
            Customer customer1 = new Customer(1, "Customer 1");
            Customer customer2 = new Customer(2, "Customer 2");
      
            dao.insertCustomer(customer1);
            dao.insertCustomer(customer2);
            dao.insert(invoice1);
            dao.insert(invoice2);
            dao.insert(invoice3);
            dao.insert(invoice4);
      
            logCustomerInvoices(0,"_R1");
            logCustomerInvoices(1,"_R2");
      
      
            invoice1.setDeliveryStatus(0);
            invoice2.setDeliveryStatus(0);
            invoice3.setDeliveryStatus(0);
            invoice4.setDeliveryStatus(0);
            dao.updateInvoice(invoice1);
            dao.updateInvoice(invoice2);
            dao.updateInvoice(invoice3);
            dao.updateInvoice(invoice4);
            logCustomerInvoices(0,"_R3");
            logCustomerInvoices(1,"_R4");
      
            invoice1.setDeliveryStatus(1);
            invoice2.setDeliveryStatus(0);
            invoice3.setDeliveryStatus(0);
            invoice4.setDeliveryStatus(0);
            dao.updateInvoice(invoice1);
            dao.updateInvoice(invoice2);
            dao.updateInvoice(invoice3);
            dao.updateInvoice(invoice4);
            logCustomerInvoices(0,"_R5");
            logCustomerInvoices(1,"_R6");
      
      
        }
      
        void logCustomerInvoices(int deliveryStatus, String tagSuffix) {
            for(CustomerInvoice ci: dao.getCustomerInvoices(deliveryStatus)) {
                Log.d(TAG+tagSuffix,"Customer is " + ci.customer.getCustomerName() +
                        " Serial is " + ci.customer.getCustomerSerial() +
                        " Sort is " + ci.customer.getCustomerSort() + " There are " + ci.invoices.size() + " Invoices. They are ");
                for (Invoice i: ci.invoices) {
                    Log.d(TAG+tagSuffix,"
      	Invoice # is " + i.getInvoiceNumber() + " CustSerial is " + i.getCustomerSerial() + " DlvrStatus is " + i.getDeliveryStatus() + " Sort is " +  i.getInvoiceSort());
                }
            }
        }
      

      }

    运行时可能会输出 6 组(3 对)结果到日志中。输出是:-

    2023-02-04 06:53:16.867 D/DBINFO_R1: Customer is Customer 1 Serial is 1 Sort is 1675453996772-1 There are 2 Invoices. They are 
    2023-02-04 06:53:16.868 D/DBINFO_R1:    Invoice # is 1234 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996766-1234
    2023-02-04 06:53:16.868 D/DBINFO_R1:    Invoice # is 1235 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996771-1235
    2023-02-04 06:53:16.869 D/DBINFO_R1: Customer is Customer 2 Serial is 2 Sort is 1675453996772-2 There are 2 Invoices. They are 
    2023-02-04 06:53:16.869 D/DBINFO_R1:    Invoice # is 2468 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2468
    2023-02-04 06:53:16.869 D/DBINFO_R1:    Invoice # is 2469 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2469
    
    
    2023-02-04 06:53:16.887 D/DBINFO_R3: Customer is Customer 1 Serial is 1 Sort is 1675453996772-1 There are 2 Invoices. They are 
    2023-02-04 06:53:16.887 D/DBINFO_R3:    Invoice # is 1234 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996766-1234
    2023-02-04 06:53:16.887 D/DBINFO_R3:    Invoice # is 1235 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996771-1235
    2023-02-04 06:53:16.887 D/DBINFO_R3: Customer is Customer 2 Serial is 2 Sort is 1675453996772-2 There are 2 Invoices. They are 
    2023-02-04 06:53:16.887 D/DBINFO_R3:    Invoice # is 2468 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2468
    2023-02-04 06:53:16.887 D/DBINFO_R3:    Invoice # is 2469 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2469
    
    
    2023-02-04 06:53:16.906 D/DBINFO_R5: Customer is Customer 1 Serial is 1 Sort is 1675453996772-1 There are 1 Invoices. They are 
    2023-02-04 06:53:16.906 D/DBINFO_R5:    Invoice # is 1235 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996771-1235
    2023-02-04 06:53:16.906 D/DBINFO_R5: Customer is Customer 2 Serial is 2 Sort is 1675453996772-2 There are 2 Invoices. They are 
    2023-02-04 06:53:16.906 D/DBINFO_R5:    Invoice # is 2468 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2468
    2023-02-04 06:53:16.906 D/DBINFO_R5:    Invoice # is 2469 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2469
    2023-02-04 06:53:16.911 D/DBINFO_R6: Customer is Customer 1 Serial is 1 Sort is 1675453996772-1 There are 1 Invoices. They are 
    2023-02-04 06:53:16.911 D/DBINFO_R6:    Invoice # is 1234 CustSerial is 1 DlvrStatus is 1 Sort is 1675453996766-1234
    
    • R1(因为交货状态都是 0 退货 2 个客户,每个客户有 2 张发票)
    • R2 不返回任何内容,因为没有客户的发票交付状态为 1
    • 发票更新没有任何作用,因为状态已经是 0,所以:-
    • R3 返回所有
    • R4 没有
    • 由于 1 发票的交货状态更改为 1,则
    • R5 返回 2 个客户,但第一个有 1 张发票(如预期的那样),另一个如预期有 2 张发票
    • R6 返回状态为 1 的 1 位客户和 1 份发票
    • 客户和发票已相应排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多