I have 300+ classes. They are related in some ways.
For simplicity, all relation are 1:1.
Here is a sample diagram.
(In real case, there are aroun
I followed an approach similar to that of R Sahu to create a raw persistence library. In my implementation each entity must implement a base interface, called IEntity. The entity basically contains a vector of fields, represented by the interface IField, like follows:
typedef shared_ptr field_ptr;
typedef vector fields_vec;
class IEntity
{
public:
virtual string const& getEntityName() = 0;
virtual bool allowDuplicates() = 0;
virtual fields_vec const& getFields() = 0;
virtual void setFieldValue(string fieldName, string fieldValue) = 0;
//callback is called after queries to fill the queryResult map (fieldName, fieldValue)
virtual void callback(map queryResult) = 0;
};
class IField
{
public:
typedef enum
{
INTEGER,
FLOAT,
REAL,
NUMERIC,
DATE,
TIME,
TIMESTAMP,
VARCHAR
} Type;
virtual string const& getName() const = 0;
virtual Type getType() const = 0;
virtual string const& getValue() const = 0;
virtual bool isPrimaryKey() const = 0;
virtual bool isForeignKey() const = 0;
virtual bool isUnique() const = 0;
virtual bool isAutoIncrement() const = 0;
virtual bool isNotNull() const = 0;
virtual int getVarcharSize() const = 0;
virtual void setValue(string value) = 0;
// Manage relations
virtual IEntity* const getReferenceEntity() const = 0;
virtual string const& getReferenceField() const = 0;
};
class CField :
public IField
{
public:
CField(string name, Type type, bool primaryKey, bool unique, bool autoincrement,
bool notNull = false, int varcharSize = 0)
{
...
}
CField(string name, Type type, IEntity* const referenceEntity, string const& referenceField,
bool notNull = false, int varcharSize = 0)
{
...
}
...
};
Then, I have an entity manager that provides basic persistence functions:
class CEntityManager
{
public:
CEntityManager();
virtual ~CEntityManager();
//--------------------------------------------//
//Initializes db and creates tables if they not exist
bool initialize(string sDbName, vector> const& entities);
//--------------------------------------------//
//Returns a shared_ptr instance of IField
field_ptr createField(string name, IField::Type type,
bool primaryKey = false, bool unique = false, bool autoincrement = false, bool notNull = false, int varcharSize = 0);
//--------------------------------------------//
//Returns a shared_ptr instance of IField,
//When the field represents a foreign key, 'referenceField' specifies the column referenced to the 'referenceEntity'
// and 'updateBy' specifies the column of the referenceEntity to check for update.
field_ptr createField(string name, IField::Type type,
IEntity* const referenceEntity, string referenceField, string updateBy, bool notNull = false, int varcharSize = 0);
//--------------------------------------------//
//Begin a new transaction
void beginTransaction();
//--------------------------------------------//
//Commit query to database
bool commit();
//--------------------------------------------//
//Persists an entity instance to db
void persist(IEntity * const entity);
//--------------------------------------------//
template
vector> find(vector restrictions)
//--------------------------------------------//
//Removes one or more entities given the specified conditions
void remove(string const& entityName, vector restrictions);
};
class WhereClause
{
public:
typedef enum
{
EQUAL,
NOT_EQUAL,
GREATER_THAN,
LESS_THAN,
GREATER_THAN_OR_EQUAL,
LESS_THAN_OR_EQUAL,
BETWEEN,
LIKE,
IN_RANGE
} Operator;
string fieldName;
string fieldValue;
Operator op;
};
The PROs of this solution are reusability, high abstraction level and ease of changing DB engine.
The CONs is that it will be slower with respect to a direct solution
However I use it with sqlite on a db of a thousand of records with time of response in the range of 100 - 600 ms, which is acceptable to me.
In your case you will have something like:
class Egg:
public IEntity
{
public:
Egg()
{
m_fields.push_back(shared_ptr(new CField("Id", IField::INTEGER, ...));
// add fields
}
private:
fields_vec m_fields;
};
class Hen :
public IEntity
{
public:
Hen()
{
m_fields.push_back(shared_ptr(new CField("Id", IField::INTEGER, ...));
// add fields
//here we add a field which represent a reference to an Egg record through the field 'Id' of Egg entity
m_fields.push_back(shared_ptr(new CField("EggId", IField::INTEGER, dynamic_cast (m_egg.get()), string("Id")));
}
private:
fields_vec m_fields;
unique_ptr m_egg;
};
Then, you can get your Hen record, containing its Egg reference, from the EntityManager
vector restrictions;
restrictions.push_back(WhereClause("Id", idToFind, EQUALS));
vector> vec = m_entityManager->find(restrictions);
This example represents a 1:1 relation between Hen and Egg. For a 1:N relation you can invert the representation and put a reference of Hen in Egg