src/decorator/columns/Column.ts
import {ColumnOptions} from "../options/ColumnOptions";
import {getMetadataArgsStorage} from "../../index";
import {
ColumnType,
SimpleColumnType,
WithLengthColumnType,
WithPrecisionColumnType
} from "../../driver/types/ColumnTypes";
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
import {ColumnCommonOptions} from "../options/ColumnCommonOptions";
import {ColumnWithLengthOptions} from "../options/ColumnWithLengthOptions";
import {ColumnNumericOptions} from "../options/ColumnNumericOptions";
import {ColumnEnumOptions} from "../options/ColumnEnumOptions";
import {ColumnEmbeddedOptions} from "../options/ColumnEmbeddedOptions";
import {EmbeddedMetadataArgs} from "../../metadata-args/EmbeddedMetadataArgs";
//列装饰工厂声明
export function Column(): Function;
//带有选项的工厂函数声明
export function Column(options: ColumnOptions): Function;
//参数为简单类型与通用列选项
export function Column(type: SimpleColumnType, options?: ColumnCommonOptions): Function;
//参数为带有长度列类型,选项多了长度属性
export function Column(type: WithLengthColumnType, options?: ColumnCommonOptions & ColumnWithLengthOptions): Function;
//参数为带有精度列类型,选项多了精度、标度类型
export function Column(type: WithPrecisionColumnType, options?: ColumnCommonOptions & ColumnNumericOptions): Function;
//枚举类型,选项中多了枚举值数组属性
export function Column(type: "enum", options?: ColumnCommonOptions & ColumnEnumOptions): Function;
//第一个参数为函数,返回值为内嵌类构造函数,第二个参数为内嵌列选项,指定了所有内嵌列的前缀
export function Column(type: (type?: any) => Function, options?: ColumnEmbeddedOptions): Function;
//函数实现
export function Column(typeOrOptions?: ((type?: any) => Function)|ColumnType|(ColumnOptions&ColumnEmbeddedOptions), options?: (ColumnOptions&ColumnEmbeddedOptions)): Function {
//列类型
let type: ColumnType|undefined;
//如果是string类型或者函数类型,Function代表了返回内嵌类构造函数的函数
if (typeof typeOrOptions === "string" || typeOrOptions instanceof Function) {
type = typeOrOptions;
}
//如果不是,第一个参数为列选项
else if (typeOrOptions) {
options = typeOrOptions;
//类型为type属性
type = typeOrOptions.type;
}
//返回装饰器
return function (object: Object, propertyName: string) {
//如果为函数,代表了内嵌对象列
if (typeOrOptions instanceof Function) {
//内嵌类型
const reflectMetadataType = Reflect && (Reflect as any).getMetadata ? (Reflect as any).getMetadata("design:type", object, propertyName) : undefined;
//内嵌类是否为数组
const isArray = reflectMetadataType === Array || (options && (options.isArray === true || options.array === true)) ? true : false;
//内嵌类元数据参数
const args: EmbeddedMetadataArgs = {
//实体构造函数
target: object.constructor,
//属性名
propertyName: propertyName,
//内嵌类是否为数组
isArray: isArray,
//内嵌列前缀
prefix: options && options.prefix !== undefined ? options.prefix : undefined,
//返回内嵌类构造函数的方法
type: typeOrOptions as (type?: any) => Function
};
//添加内嵌类元数据
getMetadataArgsStorage().embeddeds.push(args);
}
//为正常列类
else {
//如果既没有直接在参数中指定列类型,又没有在选项中指定列类型,获取实体中列属性声明的类型
if (!type) {
const reflectMetadataType = Reflect && (Reflect as any).getMetadata ? (Reflect as any).getMetadata("design:type", object, propertyName) : undefined;
if (reflectMetadataType)
type = reflectMetadataType; // todo: need to determine later on driver level
}
//列选项最次为空对象
if (!options) options = {} as ColumnOptions;
//将type合并到选项的type属性
if (!options.type && type)
options = Object.assign({ type: type } as ColumnOptions, options);
//列元数据参数
const args: ColumnMetadataArgs = {
//构造函数
target: object.constructor,
//列属性名
propertyName: propertyName,
//模式
mode: "regular",
//列选项
options: options
};
getMetadataArgsStorage().columns.push(args);
}
};
}
可以看到装饰器有几种参数情况:
1.无参数,默认采用属性名为列名
测试:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@PrimaryColumn("integer")
id: number;
@Column()
nameDDDDD:string
}
表结构:
-----------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` ( `id` int(11) NOT NULL,
`nameDDDDD` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------+
src/decorator/options/ColumnOptions.ts
import {ColumnType} from "../../driver/types/ColumnTypes";
import {ValueTransformer} from "./ValueTransformer";
//列选项接口
export interface ColumnOptions {
//列类型
type?: ColumnType;
//列名
name?: string;
//某些列类型带有长度参数,如string->varchar
length?: string|number;
//是否可以为空
nullable?: boolean;
//列是否只读,true意味着只有第一次插入表数据时可以初始化这个列值,不可更新它
readonly?: boolean;
//指明列是否总是被QueryBuilder和find操作选择,默认值为true
select?: boolean;
//列默认值
default?: any;
//指明列是否是主键列
//与@PrimaryColumn装饰器作用一样
primary?: boolean;
//列值在数据库中是否唯一
unique?: boolean;
//列注解,目前不支持
comment?: string;
//精度
precision?: number;
//标度,只有某些类支持
scale?: number;
//列字符集,目前不支持
charset?: string;
//列校对
collation?: string;
//对枚举类型,指定枚举值数组
enum?: any[]|Object;
//列是否为数组,只有postgres支持
isArray?: boolean;
//列是否为数组,只有postgres支持
array?: boolean;
//指定一个值转换器,用于读写数据库时对列值进行读写
transformer?: ValueTransformer;
}
测试1:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@PrimaryColumn("integer")
id: number;
@Column({
type:'varchar',
name:'nameAAAA',
length:50,
nullable:false,
unique:true,
default:'dazhentan'
})
nameDDDDD:string;
}
表结构:
| Table | Create Table
|
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
`nameAAAA` varchar(50) NOT NULL DEFAULT 'dazhentan',
PRIMARY KEY (`id`),
UNIQUE KEY `nameAAAA` (`nameAAAA`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
可以看到type、name、length、nullable、unique、default属性的作用
测试2:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@Column({
primary:true,
})
id: number;
}
表结构:
+-------+------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+------------------------------------------------------------------------------------------------------------+
可以看到primary为true,相当于PrimaryColumn装饰器,但是注意这种方式并不能设置generated属性,即不能指定自动生成主键
3.参数一为简单类型,参数二为通用选项
import {ValueTransformer} from "./ValueTransformer";
//所有列类型通用的列选项
//Column装饰器通用选项接口
export interface ColumnCommonOptions {
//列是否被QueryBuilder与find操作默认选择,即执行查询时这个列数据是否默认被选择
select?: boolean;
//列名
name?: string;
//是否为主键列,与@PrimaryColumn装饰器作用一样
primary?: boolean;
//列值是否自动生成,只有primary为true时起作用,也就是只有主键列可以自动生成
generated?: boolean;
//列值是否唯一,会对表添加唯一性约束
unique?: boolean;
//列值是否可为空
nullable?: boolean;
//默认值
default?: any;
//列注解,目前不支持
comment?: string;
//是否为数组列,只有postgres支持
isArray?: boolean;
//指定一个值转换器,当向数据库读写数据时对数据进行转换
transformer?: ValueTransformer;
}
可以看到,通用选项中没有type、length、readonly、pricision、scale、enum等属性,这些属性都放在了单独接口中(除了readonly外),但是多了一个generated属性,即可以设置自动生成主键列
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@PrimaryColumn()
id: number;
@Column('year',{
name:'yeareDDDD',
unique:true,
nullable:false,
default:1980
})
yearAAAA:string;
}
表结构:
| Table | Create Table
|
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
`yeareDDDD` year(4) NOT NULL DEFAULT '1980',
PRIMARY KEY (`id`),
UNIQUE KEY `yeareDDDD` (`yeareDDDD`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
注意简单类型,不包含varchar类型,是year来测试
4.参数一为带长度列类型,参数二为通用选项加上length属性
src/decorator/options/ColumnWithLengthOptions.ts
//带有长度的列选项
export interface ColumnWithLengthOptions {
//长度值,数字字符串或者数字
length?: string|number;
}
测试一:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@PrimaryColumn()
id:number;
@Column('varchar',{
length:20
})
name:string;
}
表结构:
+-------+-------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------+
测试二:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@Column('int',{
primary:true,
generated:true
})
id:number;
}
表结构:
+-------+------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+------------------------------------------------------------------------------------------------------------+
很尴尬,generated属性没有预期作用
5.参数一位带精度列类型,参数二为通用选项加上precision、scale属性
src/decorator/options/ColumnNumericOptions.ts
//带有精度列类型选项
export interface ColumnNumericOptions {
//精度
precision?: number;
//标度
scale?: number;
}
测试:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@PrimaryColumn()
id:number;
@Column('decimal',{
precision:8,
scale:2
})
name:number;
}
表结构:
+-------+--------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
`name` decimal(8,2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------+
6.参数一为enum,参数二为通用选项加上枚举值数组属性
src/decoratoc/options/ColumnEnumOptions.ts
//枚举类型列选项
export interface ColumnEnumOptions {
//枚举值数组
enum?: any[]|Object;
}
测试:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@PrimaryColumn()
id:number;
@Column('enum',{
enum:['A','B','C']
})
name:string;
}
表结构:
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
`name` enum('A','B','C') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------+
mysql中由enum类型,括号里面为枚举值,枚举值在数据库中存储都是使用字符串类型
7.参数一为函数,返回内嵌类构造函数,参数二为通用选项加上内嵌类的列前缀
src/decoratoc/options/ColumnEmbeddedOptions.ts
//内嵌列类型选项
export interface ColumnEmbeddedOptions {
//内嵌列前缀
prefix?: string;
}
测试:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
class Embed{
@Column()
firstname:string;
@Column()
lastname:string;
}
@Entity()
export class Post {
@PrimaryColumn()
id:number;
@Column(type=>Embed,{
prefix:'embed'
})
embed:Embed;
}
表结构:
| Table | Create Table
|
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
`embedFirstname` varchar(255) NOT NULL,
`embedLastname` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
可以看到,内嵌类属性必须使用列注解,最后的列名为prefix前缀加上原有内嵌列属性名首字母大写
最后还有一个simple-array类型,这个类型是可以将数组类型属性,存储为数据库中的文本类型,转换由typeorm内部自动进行
测试:
import {Column, Entity} from "../../../src/index";
import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
import {Generated} from "../../../src/decorator/Generated";
@Entity()
export class Post {
@PrimaryColumn()
id:number;
@Column('simple-array')
array:string[];
}
import "reflect-metadata";
import {ConnectionOptions, createConnection} from "../../src/index";
import {Post} from "./entity/Post";
const options: ConnectionOptions = {
"name": "mysql",
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "123456",
"database": "test",
synchronize: true,
entities: [Post]
};
createConnection(options).then(async connection => {
let post = new Post()
post.id = 1
post.array = ['AAA','BBB','CCC']
connection.getRepository(Post).save(post)
}, error => console.log("Cannot connect: ", error));
表结构:
+-------+-------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------+
| post | CREATE TABLE `post` (
`id` int(11) NOT NULL,
`array` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------+
表数据:
+----+-------------+
| id | array |
+----+-------------+
| 1 | AAA,BBB,CCC |
+----+-------------+
可见,typeorm将simple-array类型的数组属性存储为mysql中的text类型,存储值为数组元素用逗号拼接起来,从数据库中查询array属性,会直接得到数组