Entity–attribute–value model (EAV) is a data model to describe entities where the number of attributes (properties, parameters) that can be used to describe them is potentially vast, but the number that will actually apply to a given entity is relatively modest. In mathematics, this model is known as a sparse matrix. EAV is also known as object–attribute–value model, vertical database model and open schema.
##EAV database schema Data is recorded in the following tables
- eav_attribute
- eav_category
- eav_dictionary
- eav_dictionary_entry
- eav_my_object
- eav_object_value
- eav_rel_configuration
- eav_relation
###eav_category Stores all defined categories referenced by
- eav_attribute
- eav_my_object
- eav_rel_configuration
It plays a role of glue between objects, attributes and relations. Attributes and relations related to the one object have the same category.
Properties:
id
- uniqueidentifier
- uniquename
Example of categories
- person
- computer
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| identifier | varchar(255) | NO | UNI | NULL | |
| name | varchar(255) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
###eav_attribute
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| data_type | varchar(255) | NO | | NULL | |
| identifier | varchar(255) | NO | UNI | NULL | |
| name | varchar(255) | NO | | NULL | |
| category_id | bigint(20) | NO | MUL | NULL | |
| dictionary_id | bigint(20) | YES | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
###eav_rel_configuration
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| identifier | varchar(255) | NO | UNI | NULL | |
| name | varchar(255) | NO | | NULL | |
| owner_cat_id | bigint(20) | NO | MUL | NULL | |
| target_cat_id | bigint(20) | NO | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
###eav_my_object
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| category_id | bigint(20) | NO | MUL | NULL | |
+-------------+--------------+------+-----+---------+----------------+
###eav_object_value
+---------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| boolean_value | bit(1) | YES | | NULL | |
| date_value | date | YES | | NULL | |
| double_value | double | YES | | NULL | |
| integer_value | int(11) | YES | | NULL | |
| string_value | varchar(255) | YES | | NULL | |
| attribute_id | bigint(20) | NO | MUL | NULL | |
| dictionary_entry_id | bigint(20) | YES | MUL | NULL | |
| object_id | bigint(20) | NO | MUL | NULL | |
+---------------------+--------------+------+-----+---------+----------------+
###eav_relation
+------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| rel_cnf_id | bigint(20) | NO | MUL | NULL | |
| owner_id | bigint(20) | NO | MUL | NULL | |
| target_id | bigint(20) | YES | MUL | NULL | |
+------------+------------+------+-----+---------+----------------+
###eav_dictionary
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| identifier | varchar(255) | NO | UNI | NULL | |
| name | varchar(255) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
###eav_dictionary_entry
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| disabled | bit(1) | NO | | NULL | |
| identifier | varchar(255) | NO | UNI | NULL | |
| name | varchar(255) | NO | | NULL | |
| dictionary_id | bigint(20) | NO | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
##EAV Api
Logically api is organized into three main modules
- frame api
- pl.softech.eav.domain.frame
- dsl api
- pl.softech.eav.domain.dsl
- model manipulation/query api being stored in the following packages
- pl.softech.eav.domain.attribute
- pl.softech.eav.domain.category
- pl.softech.eav.domain.dictionary
- pl.softech.eav.domain.object
- pl.softech.eav.domain.relation
- pl.softech.eav.domain.specification
- pl.softech.eav.domain.calue
###Frame Api
It is an abstraction which works well in case when we have well defined entity model. Each object of class MyObject
can be framed with an interface.
Thanks to this manipulation and querying tasks on MyObject
can be performed on the interface simply calling appropriate methods.
Examples
public interface Computer {
DictionaryEntry getMake();
String getModel();
void setModel(String model);
}
MyObject object = new MyObject(
categoryRepository.findByIdentifier("computer"),
"MAUI"
);
Computer computer = frameFactory.frame(Computer.class, object);
computer.setModel("Studio15");
###Model manipulation/query api
Examples
Create a computer object
MyObject computer = new MyObject(
categoryRepository.findByIdentifier("computer"),
"STAR"
);
Create a person object
MyObject person = new MyObject(
categoryRepository.findByIdentifier("person"),
"Slavik"
);
Add dictionary value to the computer object
computer.addValue(
attributeRepository.findByIdentifier(new AttributeIdentifier("make")),
new DictionaryEntryValue(dictionaryEntryRepository.findByIdentifier("dell"))
);
Add string value to the computer object
computer.addValue(
attributeRepository.findByIdentifier(new AttributeIdentifier("model")),
new StringValue("Studio15")
);
Configure 'has computer' relation between person and computer
RelationConfiguration hasComputer = new RelationConfiguration(
new RelationIdentifier("has_computer"),
"has",
categoryRepository.findByIdentifier("person"),
categoryRepository.findByIdentifier("computer")
);
Add 'has computer' relation to the person object
person.addRelation(hasComputer, computer);
Ask for a value for the given attribute as a string
ObjectValue makeValue = obj.getValueByAttribute(new AttributeIdentifier("make"));
String make = makeValue.getValueAsString();
Ask for a value for given attribute, assuming that the value is of DictionaryEntry type
ObjectValue makeValue = obj.getValueByAttribute(new AttributeIdentifier("make"));
final DictionaryEntryValue[] bag = new DictionaryEntryValue[1];
makeValue.accept(new ValueVisitorAdapter() {
@Override
public void visit(DictionaryEntryValue value) {
bag[0] = value;
}
});
Ask for a value for a given attribue, assuming that the value is of Integer type
ObjectValue ramValue = obj.getValueByAttribute(new AttributeIdentifier("ram"));
final Integer[] bag = new Integer[1];
ramValue.accept(new ValueVisitorAdapter() {
@Override
public void visit(IntegerValue value) {
bag[0] = value;
}
});
Ask for a collection of values for a given attribute
Set<ObjectValue> oss = obj.getValuesByAttribute(new AttributeIdentifier("os"));
Ask for a given relation
Relation relation = person.getRelationByIdentifier(
new RelationIdentifier("has_computer")
);
##EAV Dsl parser
Characteristics
- hand written
- top-down parsing strategy
- clear separation from domain model (
CreateModelVisitor
) - utilized visitor and context object pattern
CreateModelVisitor visitor = new CreateModelVisitor(
dictionaryRepository,
dataTypeSerialisationService
);
Parser p = new Parser(visitor);
p.parse(buffer.toString());
##EAV Domain specific language
Pros
- easy to learn, clear structure
- fast domain model preparation
- easy to maintenance
- ideal for the domain experts
- supports comments
Cons
- there is no IDE support for syntax highlighting
- there is no IDE support for syntax autocomplete
Resources
###Dsl syntax
Category definition
Category definition consists of two elements
- identifier - follows
category
keyword name
Example
category computer
name : "Computer"
end
Attributes definition
Attribute definition consists of four elements
- identifier - follows
attribute
keyword name
category
data_type
Data type element (data_type
) can appear in the dsl in one of the two forms
- dictionary type
- simple type
text
double
integer
boolean
date
Examples
dictionary type
attribute make
name : "Make"
category : "computer"
data_type : dictionary of "computer_make"
end
simple type
attribute model
name : "Model"
category : "computer"
data_type : text
end
Relation definition
Relation definition consists of 4 elements
- identifier - follows
relation
keyword name
owner
target
identifier
and name
are self explanatory, only owner
and target
need to be explained.
Owner and target elements can only take category's identifier. Owner describes the owning side of the relation,
target in the other hand is the second side.
Example
relation has_computer
name : "Has computer"
owner : "person"
target : "computer"
end
Object definition
Object must be defined with at least three elements
- identifier - follows
object
keyword - category - is between
of
andcategory
keywords name
In object
definition any kind of matching attribute can appear. Attribute can be used in
the object
definition if only it has the same category
as object
. Value of the attribute
must fit with the attribute
datatype.
Relations are set in the special block starting with the relations
keyword and must have the same owner
category as the object
.
Value of the relation must fit with the target object category.
Example
object maui of computer category
name : "MAUI"
make : "dell"
model : "Studio15"
type : "notebook"
cpu : "Core 2 Duo 2.4GHz"
drive : "320Gb 5400rpm"
video : "Intel Acc"
ram : "4"
optical : "DVD RW"
battery : "6 cell"
screen : "15"
os : "win7"
os : "linux"
end
object with relations
object gyles of person category
name : "gyles"
firstname : "Gyles"
lastname : "Aitken"
age : "22"
relations
has_computer : "maui"
has_parent : "colton"
has_friend : "colton"
has_friend : "jermaine"
end
##Todo
- dsl parser
- add line number to the parse error
- try to print as many errors as possible
- add possibility to reference objects not yet defined
- examples
- add 'Curriculum Vitae' example
- add relation and objects creating on the fly example
- documentation
- document computer-person example
- javadoc ?
- core
- introduce new json object value
- enhance eav entities with audit capability
- introduce liquibase migrations
- mysql
- hsql
- oracle
- test
- implement tests marked with //TODO implement
- setup continuous integration (maybe should try http://www.cloudbees.com/)
##Resources
- http://en.wikipedia.org/wiki/Entity-attribute-value_model
- http://stackoverflow.com/questions/1336449/eav-over-sql-server/1336471#1336471
- http://eav.codeplex.com/
- http://stackoverflow.com/questions/126271/key-value-pairs-in-relational-database
- http://stackoverflow.com/questions/6661841/the-concept-of-implementing-key-value-stores-with-relational-database-languages
##Problems
##License
Copyright 2013 Sławomir Śledź slawomir.sledz@sof-tech.pl.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.