store many of relation 1:1 between various type of objects : decoupling & high performance

前端 未结 6 1240
夕颜
夕颜 2021-02-01 08:26

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

6条回答
  •  执笔经年
    2021-02-01 09:02

    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

提交回复
热议问题