web-dev-qa-db-fra.com

Type de retour pour les jointures de pièce Android

Disons que je veux faire un INNER JOIN entre deux entités Foo et Bar:

@Query("SELECT * FROM Foo INNER JOIN Bar ON Foo.bar = Bar.id")
List<FooAndBar> findAllFooAndBar();

Est-il possible de forcer un type de retour comme celui-ci?

public class FooAndBar {
    Foo foo;
    Bar bar;
}

Lorsque j'essaie de faire cela, j'obtiens cette erreur:

error: Cannot figure out how to read this field from a cursor.

J'ai également essayé d'aliaser les noms de table pour qu'ils correspondent aux noms de champs, mais cela n'a pas fonctionné non plus.

Si ce n'est pas possible, comment dois-je construire proprement un type de retour compatible incluant tous les champs des deux entités?

36
pqvst

Dao

@Query("SELECT * FROM Foo")
List<FooAndBar> findAllFooAndBar();

Classe FooAndBar

public class FooAndBar {
    @Embedded
    Foo foo;

    @Relation(parentColumn =  "Foo.bar_id", entityColumn = "Bar.id")
    //Relation returns a list
    //Even if we only want a single Bar object .. 
    List<Bar> bar;

    //Getter and setter...
}

Cette solution semble fonctionner, mais je n'en suis pas très fier ... Que pensez-vous de cela?

Edit: Une autre solution  

Dao, je préfère choisir explicitement mais "*" fera l'affaire :)

@Query("SELECT Foo.*, Bar.* FROM Foo INNER JOIN Bar ON Foo.bar = Bar.id")
List<FooAndBar> findAllFooAndBar();

Classe FooAndBar

public class FooAndBar {
    @Embedded
    Foo foo;

    @Embedded
    Bar bar;

    //Getter and setter...
}
37
Gaëtan S

Essayez de cette façon ... Par exemple, j'ai des relations M2M (plusieurs à plusieurs) entre Product et Attribute. Plusieurs Produits ont plusieurs Attributs et je dois obtenir tous les Attributs par Product.id avec des enregistrements triés par PRODUCTS_ATTRIBUTES.DISPLAY_ORDERING.

|--------------|  |--------------|  |-----------------------|
| PRODUCT      |  | ATTRIBUTE    |  | PRODUCTS_ATTRIBUTES   |
|--------------|  |--------------|  |-----------------------|
| _ID:  Long   |  | _ID: Long    |  | _ID: Long             |
| NAME: Text   |  | NAME: Text   |  | _PRODUCT_ID: Long     |
|______________|  |______________|  | _ATTRIBUTE_ID: Long   |
                                    | DISPLAY_ORDERING: Int |
                                    |_______________________|

Donc, les modèles seront comme ci-dessous:

@Entity(
    tableName = "PRODUCTS",
    indices = [
        Index(value = arrayOf("NAME"), unique = true)
    ]
)
class Product {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_ID")
    var _id: Long = 0

    @ColumnInfo(name = "NAME")
    @SerializedName(value = "NAME")
    var name: String = String()

}


@Entity(
    tableName = "ATTRIBUTES",
    indices = [
        Index(value = arrayOf("NAME"), unique = true)
    ]
)
class Attribute {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_ID")
    var _id: Long = 0

    @ColumnInfo(name = "NAME")
    @SerializedName(value = "NAME")
    var name: String = String()

}

Et la table "rejoindre" sera:

@Entity(
    tableName = "PRODUCTS_ATTRIBUTES",
    indices = [
        Index(value = ["_PRODUCT_ID", "_ATTRIBUTE_ID"])
    ],
    foreignKeys = [
        ForeignKey(entity = Product::class, parentColumns = ["_ID"], childColumns = ["_PRODUCT_ID"]),
        ForeignKey(entity = Attribute::class, parentColumns = ["_ID"], childColumns = ["_ATTRIBUTE_ID"])
    ]
)
class ProductAttribute {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_ID")
    var _id: Long = 0

    @ColumnInfo(name = "_PRODUCT_ID")
    var _productId: Long = 0

    @ColumnInfo(name = "_ATTRIBUTE_ID")
    var _attributeId: Long = 0

    @ColumnInfo(name = "DISPLAY_ORDERING")
    var displayOrdering: Int = 0

}

Dans, AttributeDAO, pour obtenir tous les attributs basés sur Product._ID, vous pouvez procéder comme ci-dessous:

@Dao
interface AttributeDAO {

    @Query("SELECT ATTRIBUTES.* FROM ATTRIBUTES INNER JOIN PRODUCTS_ATTRIBUTES ON PRODUCTS_ATTRIBUTES._ATTRIBUTE_ID = ATTRIBUTES._ID INNER JOIN PRODUCTS ON PRODUCTS._ID = PRODUCTS_ATTRIBUTES._PRODUCT_ID WHERE PRODUCTS._ID = :productId ORDER BY PRODUCTS_ATTRIBUTES.DISPLAY_ORDERING ASC")
    fun getAttributesByProductId(productId: Long): LiveData<List<Attribute>>

}

Si vous avez des questions, s'il vous plaît dites-le moi.

6
dphans

Une autre option consiste simplement à écrire un nouveau POJO représentant la structure résultante de votre requête JOIN (qui prend même en charge le changement de nom de colonne pour éviter les conflits):

@Dao
public interface FooBarDao {
   @Query("SELECT foo.field1 AS unique1, bar.field1 AS unique2 "
          + "FROM Foo INNER JOIN Bar ON Foo.bar = Bar.id")
   public List<FooBar> getFooBars();

   static class FooBar {
       public String unique1;
       public String unique2;
   }
}    

Voir: room/accéder-data.html # query-multiple-tables

4
AjahnCharles

Est-il possible de forcer un type de retour comme celui-ci?

Vous pouvez essayer les annotations @Embedded sur foo et bar. Cela indiquera à Room d'essayer de prendre les colonnes de votre requête et de les verser dans les instances foo et bar. Je n'ai essayé cela qu'avec des entités, mais la documentation indique que cela devrait également fonctionner avec des POJO.

Toutefois, cela peut ne pas fonctionner correctement si vos deux tables ont des colonnes portant le même nom.

2
CommonsWare

Ceci est ma table de nourriture

@Entity(tableName = "_food_table")
data class Food(@PrimaryKey(autoGenerate = false)
            @ColumnInfo(name = "_food_id")
            var id: Int = 0,
            @ColumnInfo(name = "_name")
            var name: String? = "")

Ceci est la table et la classe de modèles de mon panier (panier de nourriture)

@Entity(tableName = "_client_cart_table")
data class CartItem(
                @PrimaryKey(autoGenerate = false)
                @ColumnInfo(name = "_food_id")
                var foodId: Int? = 0,
                @Embedded(prefix = "_food")
                var food: Food? = null,
                @ColumnInfo(name = "_branch_id")
                var branchId: Int = 0)

Remarque: nous voyons ici la colonne _food_id dans deux tables. Cela provoquera une erreur de compilation. À partir de @Embedded doc, vous devez utiliser un préfixe pour les différencier. 

À l'intérieur dao

@Query("select * from _client_cart_table inner join _food_table on _client_cart_table._food_id = _food_table._food_id where _client_cart_table._branch_id = :branchId")
fun getCarts(branchId: Int) : LiveData<List<CartItem>>

Cette requête retournera des données comme celle-ci

CartItem(foodId=5, food=Food(id=5, name=Black Coffee), branchId=1)

Je l'ai fait dans mon projet. Alors essayez-le. Bonne codage

0
Shaon