Model 
Create Model 
For example, we create a Model: student in the module demo-student
1. Cli command 
$ vona :create:bean model student --module=demo-student2. Menu command 
TIP
Context Menu - [Module Path]: Vona Create/Model
Model Definition 
import { BeanModelBase, Model } from 'vona-module-a-orm';
import { EntityStudent } from '../entity/student.ts';
@Model({ entity: EntityStudent })
export class ModelStudent extends BeanModelBase<EntityStudent> {}- Inherit from 
BeanModelBaseclass - Use 
Modeldecorator - Set the corresponding 
entity 
Using Model 
Using Model in Vona supports dependency injection and dependency lookup. It is recommended to use dependency lookup because it can make the code more concise and intuitive
1. Lookup in this module 
class ServiceStudent {
  async findAll(): Promise<EntityStudent[]> {
    return await this.scope.model.student.select();
  }
}2. Lookup Cross-module 
class ServiceStudent {
  async findAll(): Promise<EntityStudent[]> {
    return await this.$scope.demoStudent.model.student.select();
  }
}You can also use bean._getBean to get the Model instance directly from the IOC container:
class ServiceStudent {
  async findAll(): Promise<EntityStudent[]> {
    return await this.bean._getBean('demo-student.model.student').select();
  }
}CRUD 
Here, we only introduce basic CRUD operations. For more information, see:
1. Create 
class ServiceStudent {
  async create(student: DtoStudentCreate): Promise<EntityStudent> {
    return await this.scope.model.student.insert(student);
  }
}2. Read 
class ServiceStudent { 
  async findAll(): Promise<EntityStudent[]> { 
    return await this.scope.model.student.select(); 
  } 
  async findOne(id: TableIdentity): Promise<EntityStudent | undefined> { 
    return await this.scope.model.student.getById(id); 
  }
}- id: The type is 
TableIdentity, supporting two field types:numberandbignumber 
3. Update 
class ServiceStudent { 
  async update(id: TableIdentity, student: DtoStudentUpdate) { 
    return await this.scope.model.student.updateById(id, student); 
  }
}4. Delete 
class ServiceStudent { 
  async remove(id: TableIdentity) {
    return await this.scope.model.student.deleteById(id);
  }
}Knex Query Builder 
Vona Model is based on knex. Therefore, it also supports the Query Builder provided by knex
1. builder 
Invoke the builder method of the model to obtain an instance of the knex query builder
class ServiceStudent {
  async findAll(): Promise<EntityStudent[]> {
    return await this.scope.model.student.builder().where('name', 'tom').orderBy('name');
  }
}2. builderSelect 
The builderSelect method also obtains an instance of the knex query builder. Unlike builder, builderSelect supports soft deletion and multi-instance/multi-tenancy
class ServiceStudent {
  async findAll(): Promise<EntityStudent[]> {
    return await this.scope.model.student.builderSelect().where('name', 'tom').orderBy('name');
  }
}3. Raw Sql 
Raw Sql statements can be executed directly
class ServiceStudent {
  async findAll(): Promise<EntityStudent[]> {
    return await this.scope.model.student.query('select * from demoStudent');
  }
}class ServiceStudent {
  async findOne(id: TableIdentity): Promise<EntityStudent | undefined> {
    return await this.scope.model.student.queryOne('select * from demoStudent where id=?', [id]);
  }
}Model Options 
| Name | Description | 
|---|---|
| entity | Entity corresponding to model | 
| table | Table name corresponding to model | 
| disableDeleted | Whether to disable soft deletion, the default is false | 
| disableInstance | Whether to disable multi-instance/multi-tenancy, the default is false | 
| disableCreateTime | Whether to disable create time. The default value is false, and the system automatically sets the create time for new data | 
| disableUpdateTime | Whether to disable update time. The default value is false, and the system automatically sets the update time for the data to be modified | 
| softDeletionPrune | Whether to automatically clean up soft deleted data | 
| client | Specify the datasource | 
| cache | Configure cache parameters, enable redis-based cache by default | 
| relations | Specify models relationship, support: 1:1, 1:n, n:1, n:n | 
- table: 
- If it is empty, the table name is automatically obtained from entity
 - You can also specify a function to achieve dynamic table partitioning
 
 
App config configuration 
Model options can be configured in App config
src/backend/config/config/config.dev.ts
// onions
config.onions = {
  model: {
    'demo-student:student': {
      disableDeleted: true,   // disable soft deletion
      disableInstance: true,  // disable multi-instance/multi-tenancy
      client: 'mysql',    // use datasource:mysql
      cache: false,    // disable cache
    },
  },
};Dynamic table partitioning 
Model supports dynamic table partitioning. For example, we partition the Order table and store the daily orders in a data table in the format of order_YYYYMMDD
@Model({ table: (ctx: VonaContext, where: EntityOrder | undefined, defaultTable: keyof ITableRecord) => {
  return `${defaultTable}_${moment().format('YYYYMMDD')}`;
} })
class ModelOrder {}ctx: can dynamically generate table name based on the context of the current requestwhere: can dynamically generate table name based on the where conditionsdefaultTable: default table name
Soft deletion, multi-instance/multi-tenancy 
Model automatically enables soft deletion by default. When data deleting, it is not physically deleted, but the value of the field deleted is set to true
Model supports multi-instance/multi-tenancy by default. When performing CRUD operations on data, the instance id is automatically obtained from the Request context and passed into the sql statement
For example, execute model.student.select(), then the generated sql is as follows:
select "demoStudent"."id" from "demoStudent"
  where "demoStudent"."iid" = 1 and "demoStudent"."deleted" = false- iid: instance Id
 - deleted: soft deletion
 
1. Temporarily disable soft deletion 
We can also temporarily specify the soft deletion parameter when executing the model method
class ServiceStudent {
  async findAll(): Promise<EntityStudent[]> {
    return await this.scope.model.student.select({}, { disableDeleted: true });
  }
}2. Prune soft deleted data 
The system automatically cleans up expired soft deleted data. softDeletionPrune supports the following configurations:
- boolean: true/false
 - {handler, expired}: 
- handler: Provides a custom prune function
 - expired: Specifies an expiration time
 
 
Datasource 
Vona supports multi-database and multi-datasource. Model methods can be invoked for any datasource
1. Default datasource 
By default, Model uses the default datasource set by the system
env/.env
DATABASE_DEFAULT_CLIENT = 'pg' # pg/mysql2. Static datasource 
- Specify the datasource in Model options
 
@Model({ clientName: 'mysql' })
class ModelBook {}- Specify the datasource in App config
 
src/backend/config/config/config.dev.ts
// onions
config.onions = {
  model: {
    'demo-student:student': {
      clientName: 'mysql', // Use datasource: mysql
    },
  },
};3. Adaptive datasource 
In complex systems, we may need to use a different datasource based on the current request environment. To do this, we can use Adaptive datasource:
@Model({
  client: (ctx: VonaContext) => {
    if (ctx.headers['xxx-xxx'] === 'xxx') return 'mysql';
    return 'pg';
  },
})
export class ModelOrder {}4. Dynamic datasource 
We can also specify the datasource dynamically in the code
class ServiceStudent {
  async findAll(): Promise<EntityStudent[]> {
    const modelMysql = this.scope.model.student.newInstance('mysql');
    return await modelMysql.select();
  }
}Cache 
Vona Model has cache enabled by default, making the system high performant by default
Model supports two-layer cache/mem cache/redis cache. redis cache is used by default. Because redis cache can maintain data consistency in distributed scenarios, while mem cache has a delay in synchronizing data in distributed scenarios. If business data changes infrequently, you can use mem cache or two-layer cache to achieve higher performance
How to set cache configuration:
1. Disable cache 
@Model({ cache: false })
class ModelStudent {}2. Configure cache in Model options 
``` typescript
@Model({ cache: {
  entity: {
    mode: 'all', // all/mem/redis
    mem: {
      max: 500,
      ttl: 5 * 1000, // 5s
    },
    redis: {
      ttl: 5 * 1000, // 5s
    }, 
  },
  query: {
    ...
  },
} })
class ModelStudent {}- mode: cache mode
 - mem: 
mem cacheconfiguration - redis: 
redis cacheconfiguration 
3. Configure cache in App config 
src/backend/config/config/config.dev.ts
// onions
config.onions = {
  model: {
    'demo-student:student': {
      cache: {
        entity: {
          mode: 'all', // all/mem/redis
          mem: {
            max: 500,
            ttl: 5 * 1000, // 5s
          },
          redis: {
            ttl: 5 * 1000, // 5s
          },
        },
        query: {
          ...
        },
      }
    },
  },
};4. Entity Cache and Query Cache 
Vona provides two types of caches: Entity Cache and Query Cache:
Entity Cache: EntityId -> EntityQuery Cache: Hash of Query Clause -> EntityIds
Question 1: Some may ask, doesn't the
Query Cachetake up a lot of space?
- On the contrary, the space occupied by a
 Query cacheis equal toHash + EntityIds, thus saving more cache space. The system automatically uses theEntityIdsto retrieve cached data from theEntity Cacheand assembles it into the final query result
Question 2: How do we ensure cached data consistency?
- When data changes, the
 Entity Cachefor the current data is automatically cleared, and allQuery Cachesfor the current model are automatically cleared