There must be a better way of doing this. I want to create a database table with all my clothing and have subcategories of clothing, like outerwear, dresses, shoes, etc. They all will have the same attributes (Id, name, image, about, price). Couldn't I create one table? I believe this is a One-to-Many relationship.
@Serializable
@Entity(tableName = CLOTHING_DATABASE_TABLE)
data class Clothing(
@PrimaryKey(autoGenerate = false)
val id: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = POPULAR_DATABASE_TABLE)
data class Popular(
@PrimaryKey(autoGenerate = false)
val popularId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = OUTERWEAR_DATABASE_TABLE)
data class Outerwear(
@PrimaryKey(autoGenerate = false)
val outerwearId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = TOPS_DATABASE_TABLE)
data class Tops(
@PrimaryKey(autoGenerate = false)
val topsId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = SWIMWEAR_DATABASE_TABLE)
data class Swimwear(
@PrimaryKey(autoGenerate = false)
val swimwearId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = SHOES_DATABASE_TABLE)
data class Shoes(
@PrimaryKey(autoGenerate = false)
val shoesId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = BUNDLES_DATABASE_TABLE)
data class Bundles(
@PrimaryKey(autoGenerate = false)
val bundlesId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = DRESSES_DATABASE_TABLE)
data class Dresses(
@PrimaryKey(autoGenerate = false)
val dressesId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = PAJAMAS_DATABASE_TABLE)
data class Pajamas(
@PrimaryKey(autoGenerate = false)
val pajamasId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
@Serializable
@Entity(tableName = ACCESSORIES_DATABASE_TABLE)
data class Accessories(
@PrimaryKey(autoGenerate = false)
val accessoriesId: Int,
val name: String,
val image: String,
val about: String,
val price: String
)
CodePudding user response:
You would typically have either 2 or 3 tables (3 for a many-many i.e. an item of clothing could have multiple sub-categories).
For one-many have a clothing table which has a column for the sub-category that references(relates) to the single sub-category and a sub-category table that is referenced according to a unique column (the primary key).
For the many-many you have the clothing table (without the column to reference the single sub-category), the sub-category table and then a third table that has two columns, one for the reference to the clothing and the other for the reference to the sub-category with the primary key being a composite of both.
So you could have:-
@Entity(tableName = CLOTHING_DATABASE_TABLE)
data class Clothing(
@PrimaryKey(autoGenerate = false)
val id: Long, /* should really be Long as */
val subCategoryReference: Long, /*<<<<< ADDED for the 1 subcategory */
val name: String,
val image: String,
val about: String,
val price: String
)
and :-
@Entity(tableName = SUBCATEGORY_DATABASE_TABLE)
data class SubCategory(
@PrimaryKey
val id: Long?,
val subCategoryName: String
)
- to enforce referential integrity you could add a foreign key constraint to the subCategoryReference column of the clothing table.
If you wanted a many-many, allowing a clothing to have multiple sub-categories then you could have the third table as :-
@Entity(
tableName = CLOTHING_SUBCATEGORY_MAP_DATABASE_TABLE,
primaryKeys = ["clothingMap","subcategoryMap"],
)
data class ClothingSubCategoryMap(
val clothingMap: Long,
@ColumnInfo(index = true)
val subcategoryMap: Long
)
Of course you could have a single clothing table and just have a column for the sub-category. However this would be considered as not being normalised as you would be duplicating the sub-category throughout.
Example 1-many (i.e. using the 2 tables Clothing and SubCategory)
As you would very likely want to retrieve clothing along with it's sub-category then you would have a POJO that uses the @Embedded and @Relation annotations e.g.
data class ClothingWithSingleSubCategory (
@Embedded
val clothing: Clothing,
@Relation(
entity = SubCategory::class,
parentColumn = "subCategoryReference",
entityColumn = "id"
)
val subCategory: SubCategory
)
You could then have the following as an @Dao annotated class :-
@Dao
interface AllDao {
@Insert(onConflict = IGNORE)
fun insert(clothing: Clothing): Long
@Insert(onConflict = IGNORE)
fun insert(subCategory: SubCategory): Long
@Transaction
@Query("SELECT * FROM clothing")
fun getAllClothingWithSubCategory(): List<ClothingWithSingleSubCategory>
}
With a suitable @Database annotated class you could then have something like the following in an activity:-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
val sc_popular = dao.insert(SubCategory(null,"Popular"))
val sc_outerwear = dao.insert(SubCategory(null,"OuterWear"))
val sc_tops = dao.insert(SubCategory(null,"Tops"))
val sc_swimwear = dao.insert(SubCategory(100,"Swimwear"))
val sc_shoes = dao.insert(SubCategory(null,"Shoes"))
val sc_dresses = dao.insert(SubCategory(null,"Dresses"))
val sc_pyjamas = dao.insert(SubCategory(null,"Pyjamas"))
dao.insert(Clothing(100200300400,sc_popular,"Jeans","jeans_image","blah","100.55"))
dao.insert(Clothing(100200300500,sc_outerwear,"Anorak","anorak_image","blah","214.55"))
for (cwsc: ClothingWithSingleSubCategory in dao.getAllClothingWithSubCategory()) {
Log.d("DBINFO","Name = ${cwsc.clothing.name} Price is ${cwsc.clothing.price} Sub-Category is ${cwsc.subCategory.subCategoryName}")
}
}
}
When run the log would then include:-
D/DBINFO: Name = Jeans Price is 100.55 Sub-Category is Popular
D/DBINFO: Name = Anorak Price is 214.55 Sub-Category is OuterWear
Example many-many
Like the 1-many you will want a POJO BUT one that has a List of sub-categories obtained via the mapping table. This uses the @Embeded annotation and the @Relation annotation but extended to include the associateBy to inform Room about the intermediate table. So you could have:-
data class ClothingWithListOfSubCategories(
@Embedded
val clothing: Clothing,
@Relation(
entity = SubCategory::class,
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = ClothingSubCategoryMap::class,
parentColumn = "clothingMap",
entityColumn = "subcategoryMap"
)
)
val subCategories: List<SubCategory>
)
The you could have the following in an @Dao annotated class:-
/* ADDED for many-many */
@Insert(onConflict = IGNORE)
fun insert(clothingSubCategoryMap: ClothingSubCategoryMap): Long
@Transaction
@Query("SELECT * FROM clothing")
fun getAllClothingWithSubCategories(): List<ClothingWithListOfSubCategories>
and if the activity were extended to include :-
/* Added for many-many */
/* Note utilises clothing and sub-categories above */
dao.insert(ClothingSubCategoryMap(jeans,sc_popular))
dao.insert(ClothingSubCategoryMap(jeans,sc_swimwear))
dao.insert(ClothingSubCategoryMap(jeans,sc_shoes))
dao.insert(ClothingSubCategoryMap(anorak,sc_popular))
dao.insert(ClothingSubCategoryMap(anorak,sc_outerwear))
for(cwlsc: ClothingWithListOfSubCategories in dao.getAllClothingWithSubCategories()) {
Log.d("DBINFO","Name = ${cwlsc.clothing.name} Price is ${cwlsc.clothing.price} it is in ${cwlsc.subCategories.size} sub-categories. They are:-")
for(sc: SubCategory in cwlsc.subCategories) {
Log.d("DBINFO","\t${sc.subCategoryName}")
}
}
The the log would also include :-
D/DBINFO: Name = Jeans Price is 100.55 it is in 3 sub-categories. They are:-
D/DBINFO: Popular
D/DBINFO: Swimwear
D/DBINFO: Shoes
D/DBINFO: Name = Anorak Price is 214.55 it is in 2 sub-categories. They are:-
D/DBINFO: Popular
D/DBINFO: OuterWear