Reputation: 8724
I've looked at How to store data with dynamic number of attributes in a database, but it didn't quite answer my question. The system I am implementing is rather large, so I'll stick with the objects I am concerned about: Item and Category. An Item has a variable number of attributes depending on the Category that it belongs to. For instance, Items belonging to the "T-shirt" Category would have an attribute for size, while Items belonging to the "Car" Category would have an attribute for model. Someone logged into the system as an Admin can create new Categories with completely new attributes. What's the best way to structure this both in my Java code and in my database to make this work?
If the categories weren't dynamically created, I'd use inheritance so that I would have a TShirtItem object with its specific attributes filled in. But since it is dynamic, I'm puzzled. I saw a similar question that suggested using the Map data structure, but I am not sure how that would work...
The question I referred to at the top would suggest on the database end that I would have an Category table, and Attributes table, and relationship table linking what attributes go with what Category. This makes sense. But I'm tripped up again when thinking about the Item object. I could store the unique Category_ID in the Item table, but where would I store the attributes for each Item?
Upvotes: 3
Views: 9719
Reputation: 2662
I think some attributes are major(size, color...) and some are minor(material, weight....). I will describe simplified JDL schema for this(of course exist another Stock entity which stores exact item with color, size - identified with barcode - but omitted for simpilicity). This design will help, not hard coding some part of attributes which is important for classifaing items.
For creating data in this design => You are creating some category with it's attribute, size type(for example Shoes => 39,40,41...(options); Tshirt => S,M,L..., Iphone 7,8,X...) => inside this category particular Article - Iphone (with this way on your E-Commerce system exist Iphone)
You adding specific Article Iphone X (size) - Color Black to your warehouse or stock with additional Category related Attributes => Material, Weight .... and system generates for you barcode. With this way you can easily specify Iphone with sizes and colors(without additional sql - with pure ORM) (even with additional attributes)
enum CategoryTarget{
F, M, K
}
entity Category{
name String,
imageUrl String,
showHomepage Boolean,
target CategoryTarget,
accOrder Integer,
isLastNode Boolean
}
entity Article{
status Integer,
name String,
code String,
rating Double,
imageUrl String,
isPopular Boolean
}
relationship ManyToOne{
Category{parent} to Category,
Article{category} to Category{article},
Category{sizeType} to SizeType
}
relationship ManyToMany{
Category{attribute} to Attribute{category}
}
entity Attribute{
name String
}
entity AttributeOption{
name String
}
relationship OneToMany{
Attribute{attributeOption} to AttributeOption{attribute}
}
entity SizeType{
name String
}
entity ArticleSize{
name String,
accOrder Integer
}
relationship OneToMany{
SizeType{articleSize} to ArticleSize{sizeType}
}
relationship ManyToMany{
Article{attributeOption} to AttributeOption{article}
}
Upvotes: 0
Reputation: 1
I guess you can structure ur data in such a way that u define all object types in a table and later use the below approach.
U can define tables like:
OBJECT_TYPE
OBJECTS
OBJ_PROPERTY_DEF
OBJ_PROP_VALUES
eg.
In OBJECT_TYPE
Define all object types here
object_type_code(pk) obj_name
4 car
5 t-shirt
In OBJECTS
obj_code(pk) object_type_code(fk) obj_name
1 4 BMW
2 4 Maruti
3 4 Honda
4 5 levis
5 5 polo
6 5 reebock
In OBJ_PROPERTY_DEF
Define all properties corresponding to a object in this table.
Note: Using Constants file to deifine property types would avoid another table. Hopefully when you know the data types earlier.
obj_prop_code(pk) obj_code(fk) property_type property_name
------------- -------- ------------- -------------
12 6 8 (Integer) size
13 6 9 (String) color
14 6 10 (float) weight
15 6 11 (Boolean) is_coloured
16 6 9 (String) comments
17 3 9 (String) model
18 3 8 (Integer) version
19 3 9 (String) color
20 3 9 (String) comments
In OBJ_PROP_VALUES U can inser the values the the above specified properties
obj_prop_val_code(pk) obj_prop_code(fk) obj_prop_value(VARCHAR) prop_value_sufix
----------------- ------------- -------------- -----------------
101 12 30 -
102 13 green -
103 14 126 gms
104 15 0 -
105 16 looks cool -
106 17 c532 -
107 18 3.22 -
108 19 black -
109 20 awesome car -
Reg Java classed:
Define all classes with corresponding properties. eg:
ObjectTypeData
ObjectData
ObjectPropertyDefData
ObjectPropertyValuesData
In ObjectData.java
private ObjectTypeData objType;//specifies the type
private List<ObjectPropertyValueData> listProps; //will have all property values
//setter & getters and other req props
Upvotes: 0
Reputation: 3279
Instead of Java it is more of design level issue. You have to figure out the way to define DB tables and that will assist you in finding the Java objects...
Lets start from category... A category may contain lot of Items, and an Item will belong only one category(Though I practical scenarios it is not a correct assumption).
So in DATABASE you have table called Category. If you wanna define attribute based on category, then have another table called Category_attribute which will hold default value for the attribute.
Now lets move to an Item. An item belongs to a Category so Item table will have category_key to have the mapping for Item n category... Item will have its attribute which are stored in ITEM_Attribute table...
Simple form of DB objects must be somewhat as mentioned below
Category
C_Id
Name
Category_Attribute
CA_ID
Name
Default_value
Category_Id(FK)
Item
I_ID
Name
C_ID(FK)
Item_attribute
IA_ID
Ca_ID(FK from category_attribute table)
I_ID(FK from item table)
Value
So whenever you create a Category, you will ask user to define associated attribute to the category.
At the time of creating the Item, you map it to category... And attributes associated to category should replicate with default value and map to item as well...
So you will be able to create Java objects easily...
Upvotes: 0
Reputation: 46960
If you are constrained to use an SQL database and you need to do efficient type-aware (not merely string) queries on the attributes (like list all items with category shirt having size between 4 and 8), then the structure of the database is the hard part. The Java will follow from that. If I understand correctly, you will need something like this:
categories:
id : integer (pk)
name : varchar
attributes:
id : integer (pk)
of_category : integer (fk -> categories.id)
name : varchar
type : char(1) // 'N' for number, 'S' for string, etc.
items:
id : integer (pk)
of_category : integer (fk -> categories.id)
number_values:
value : number
of_item : integer (pk, fk -> items.id)
of_attribute : integer (pk, fk -> attributes.id)
string_values:
value : varchar
of_item : integer (pk, fk -> items.id)
of_attribute : integer (pk, fk -> attributes.id)
... additional table for each attribute type
Now for the example query:
select * from categories c, items i, attributes a, number_values v
where c.name = 'shirt' and
a.of_category = c.id and
a.name = 'size' and
a.id = v.of_attribute and
i.id = v.of_item and
v.value between 4 and 8
Hairy multiple joins are the price to be paid for runtime-defined attrbutes.
Once you have the tables right, modeling them as Java maps is straightforward. There is redundancy in the structure above: for example the character "type" field in the attribute rows. Consider triggers to make consistency checks.
Upvotes: 3
Reputation: 8096
Here is a simple java approach to do it. While designing large systems I always suggest to look at the bigger picture. The issue here is dynamically changing attributes. It would not be easy but yeah it is interesting.
The structure of your item class has to be like the following:
class Item{
private String itemName; // I assume all items will have a name.
private Map<ItemAttibuteName , Object> attributeMap ; // this will be a dynamic map.
public Map<ItemAttibuteName, Object> getAttributeMap(){//getter for attribute map
if( null == attributeMap)
return new HashMap<String, Object>();
return attributeMap ;
}
// you can synchronize this if needed
public void setAttribute(ItemAttibuteName name, Object value){
attributeMap.put(name, value);
}
public Object getAttribute(ItemAttibuteName name){
return attributeMap.get(name);
}
}
public enum ItemAttibuteName{
SIZE,
COLOUR
}
This approach suites your needs, further you can use a factory pattern to instantiate the Items depending on the category and make it an elegant code.
Please get back if you still have some doubts.
EDIT: Now there will be a complicated way to get all the attributes of an element in the code while programming, because you don't have getters or setters for the attributes. Well, the approach will help if you can maintain a Set of attributes added to the Item, in the Item class or you can query for the keySet of the item class's attributeMap property.
private Set<ItemAttibuteName> attributes;
or
attributeMap.keySet();
Happy to Help
Dharam
Upvotes: 2