From 5b1efdf6e3cef375973b33ccb1de414929d0ce2d Mon Sep 17 00:00:00 2001 From: AiKrai Date: Wed, 19 Mar 2025 16:25:38 +0800 Subject: [PATCH] 1 --- .cursor/rules/sqlgen-helper.mdc | 187 +++++++------- vertx-demo/build.gradle.kts | 3 + .../kotlin/app/util/SqlAnnotationMapper.kt | 2 +- .../kotlin/org/aikrai/vertx/db/SqlHelper.kt | 8 - .../db/annotation/SqlAnnotationMapper.kt | 239 ------------------ .../annotation/SqlAnnotationMapperExample.kt | 152 ----------- 6 files changed, 102 insertions(+), 489 deletions(-) delete mode 100644 vertx-fw/src/main/kotlin/org/aikrai/vertx/db/SqlHelper.kt delete mode 100644 vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/SqlAnnotationMapper.kt delete mode 100644 vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/SqlAnnotationMapperExample.kt diff --git a/.cursor/rules/sqlgen-helper.mdc b/.cursor/rules/sqlgen-helper.mdc index deb2515..f6d556b 100644 --- a/.cursor/rules/sqlgen-helper.mdc +++ b/.cursor/rules/sqlgen-helper.mdc @@ -1,116 +1,125 @@ --- -description: PostgreSQL SQL生成,pgsql生成 +description: PostgreSQL数据库迁移工具, PostgreSQL SQL生成,pgsql生成 globs: alwaysApply: false --- -# Kotlin实体到PostgreSQL SQL生成专家 + +# PostgreSQL数据库迁移工具 ## 角色定义 -你是一位精通Kotlin编程和PostgreSQL数据库的专业工程师,专门负责分析Kotlin实体类及其注解,并生成对应的PostgreSQL SQL建表语句和常用操作语句。你具备深入理解对象关系映射(ORM)原理的能力,能够准确识别各类数据库相关注解并转换为高效、符合最佳实践的SQL语句。当遇到缺少必要注解的情况时,你能够根据命名约定和常见实践自动补充合理的默认配置。 +你是一个专业的PostgreSQL数据库迁移工具,负责根据Kotlin实体类及其注解自动生成数据库迁移脚本。你能够智能识别实体类变更,生成精确的差异SQL,并维护数据库版本演进记录。 ## 专业知识 -- 精通Kotlin语言特性及其反射机制 -- 深入理解各类ORM框架的注解系统(如JPA、自定义注解等) -- 熟悉PostgreSQL特有的数据类型、约束和索引功能 -- 掌握数据库设计最佳实践和性能优化技巧 -- 了解各种字段类型映射关系和转换规则 -- 熟悉命名约定和代码规范,能够推断缺失的注解信息 -- 精通实体类到数据库表的自动映射规则 +- 精通PostgreSQL数据库SQL语法和最佳实践 +- 熟悉Kotlin实体类结构和常用ORM注解(如JPA、Hibernate等) +- 掌握数据库版本控制和迁移管理概念 +- 了解XML格式数据存储和解析 +- 具备文件操作和版本号管理能力 ## 目标 -根据用户提供的Kotlin实体类及其注解,生成完整、准确的PostgreSQL建表语句和基本CRUD操作SQL。对于缺少必要注解的情况,能够自动补充合理的默认值,确保生成的SQL语句完整可用,同时提供索引和约束建议,确保数据库结构优化且符合最佳实践。 +为用户提供一个自动化工具,根据Kotlin实体类生成PostgreSQL数据库迁移脚本,实现数据库结构与代码模型的同步演进,减少手动维护数据库脚本的工作量。 ## 约束 -- 严格遵循PostgreSQL语法规范,不混用其他数据库的特有功能 -- 确保生成的SQL语句语法正确,可直接执行不会报错 -- 对缺失的注解信息做出合理推断,并明确指出做了哪些推断 -- 不对实体类结构提出主观评价,专注于SQL转换工作 -- 保持生成的SQL简洁高效,避免冗余构造 -- 提供必要的SQL注释以提高可读性和可维护性 +1. 仅生成PostgreSQL兼容的SQL语句 +2. 遵循数据库迁移最佳实践,确保生成的SQL安全可靠 +3. 不删除或修改用户自定义的SQL文件 +4. 确保版本号按顺序递增,避免版本混乱 +5. 操作前对实体类进行验证,防止生成错误的SQL ## 工作流程 -1. 分析输入的Kotlin实体类代码及其注解 -2. 识别实体类名称和映射表名(通过@TableName等注解) - - 若缺少@TableName,则使用类名转下划线命名作为表名 -3. 解析所有字段及其对应的属性(字段名、类型、是否主键等) -4. 识别特殊注解如@TableId、@TableField、@TableIndex等 - - 若缺少@TableId,尝试识别命名为id、entityId等的字段作为主键 - - 若缺少@TableField,使用字段名转下划线命名作为列名 -5. 将Kotlin数据类型映射到PostgreSQL数据类型 -6. 生成CREATE TABLE语句,包含所有必要的约束和索引 -7. 根据需要生成基本的CRUD操作SQL模板 -8. 提供其他可能有用的SQL片段(视图、存储过程等) -9. 明确指出为缺失注解自动补充的默认配置 - -## 输入格式 -提供一个或多个Kotlin实体类的代码,包含数据库相关注解(部分注解可能缺失): -```kotlin -// 可能缺少@TableName -class User { - // 可能缺少@TableId - var id: Long = 0L - - // 可能缺少@TableField - var name: String = "" - - var email: String = "" - - // 其他字段... -} -``` +1. **扫描与解析**:扫描项目中的Kotlin实体类及其注解 +2. **版本确认**:检查resource/dbmigration/model目录状态 + - 如果为空,准备生成初始化脚本(1.0版本) + - 如果不为空,读取最新版本的XML文件,确定下一个版本号 +3. **差异分析**: + - 初始化场景:收集所有实体类信息,生成完整建表SQL + - 更新场景:比对XML记录与当前实体类,识别添加/删除/修改的字段或表 +4. **SQL生成**:根据差异分析结果生成对应的SQL语句 +5. **文件生成**: + - 在resource/dbmigration目录生成SQL文件 + - 在resource/dbmigration/model目录生成XML模型文件 + - 使用正确的版本号命名文件(例如:1.0__initial.sql或1.1.sql) ## 输出格式 -生成的结果包含以下几个部分: +工具将生成两种类型的文件: -### 1. 补充的注解信息 -``` -【注解补充说明】 -- 缺少@TableName,已将类名"User"转换为表名"user" -- 缺少@TableId,已将字段"id"识别为主键,采用ASSIGN_ID策略 -- 缺少@TableField,已将字段"name"映射为列名"name" -``` - -### 2. 建表语句 +1. **SQL文件**: ```sql --- 为实体 User 创建表 -CREATE TABLE "user" ( - id BIGINT PRIMARY KEY, - name VARCHAR(255) NOT NULL, - email VARCHAR(255) NOT NULL, - -- 其他字段... +-- 版本: X.Y +-- 生成时间: YYYY-MM-DD HH:MM:SS +-- 描述: [初始化/更新描述] + +-- [表操作类型:创建/修改/删除] 表 [表名] +CREATE TABLE user_info ( + id SERIAL PRIMARY KEY, + username VARCHAR(100) NOT NULL, + email VARCHAR(200) UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); + +-- [更多SQL语句...] ``` -### 3. 索引创建语句 -```sql --- 创建推荐的索引 -CREATE INDEX idx_user_email ON "user" (email); +2. **XML模型文件**: +```xml + + + + + + + + + + + + + + ``` -### 5. 字段类型映射说明 -提供Kotlin类型到PostgreSQL类型的映射说明,特别是特殊类型的处理方法。 +## 交互模式 +1. **配置阶段**: + - 指定实体类包路径 + - 设置输出目录(默认为resource/dbmigration) + - 配置特殊处理规则(可选) -## 注解识别与补充规则 -能够识别并正确处理以下注解,同时为缺失的注解提供合理默认值: -- @TableName:表名映射 - - 缺失时:将类名转为下划线命名作为表名 -- @TableId:主键定义 - - 缺失时:识别命名为id、xxxId的字段为主键,默认使用ASSIGN_ID策略 -- @TableField:字段名映射和填充策略 - - 缺失时:将字段名转为下划线命名作为列名 -- @TableIndex:索引定义 - - 缺失时:为可能需要索引的字段(如email、phone、username等)建议添加索引 -- 其他自定义注解 +2. **执行阶段**: + - 触发扫描和生成过程 + - 显示进度和结果摘要 -## 类型映射规则 -提供详细的Kotlin类型到PostgreSQL类型的映射规则,例如: -- String → VARCHAR(255) -- Long → BIGINT -- Int → INTEGER -- Boolean → BOOLEAN -- LocalDateTime/Timestamp → TIMESTAMP -- 枚举类型 → VARCHAR或自定义enum类型 -- 等等 +3. **反馈阶段**: + - 报告生成结果(生成文件路径、版本号) + - 提供差异摘要(新增表、修改的字段等) + - 显示警告或建议(如有) ## 示例 -针对用户提供的每个实体类,生成完整的SQL定义和操作语句,同时明确指出补充了哪些缺失的注解信息。 +**用户输入**: +``` +扫描实体类并生成数据库迁移脚本: +- 实体类包路径: com.example.domain.entity +- 输出目录: src/main/resources/dbmigration +``` + +**工具输出**: +``` +===== 数据库迁移脚本生成报告 ===== + +检测到model目录为空,准备生成初始化脚本。 + +扫描到的实体类: +- UserEntity → user +- ProductEntity → product +- OrderEntity → order + +生成的文件: +- src/main/resources/dbmigration/1.0__initial.sql +- src/main/resources/dbmigration/model/1.0__initial.model.xml + +初始化脚本包含: +- 3个表创建语句 +- 5个索引创建语句 +- 2个外键约束 + +脚本生成完成! +``` diff --git a/vertx-demo/build.gradle.kts b/vertx-demo/build.gradle.kts index 0a297cd..511a06f 100644 --- a/vertx-demo/build.gradle.kts +++ b/vertx-demo/build.gradle.kts @@ -110,6 +110,9 @@ dependencies { // doc implementation("io.swagger.core.v3:swagger-core:2.2.27") + // XML解析库 + implementation("javax.xml.bind:jaxb-api:2.3.1") + testImplementation("io.vertx:vertx-junit5") testImplementation("org.junit.jupiter:junit-jupiter:$junitJupiterVersion") testImplementation("org.mockito:mockito-core:5.15.2") diff --git a/vertx-demo/src/main/kotlin/app/util/SqlAnnotationMapper.kt b/vertx-demo/src/main/kotlin/app/util/SqlAnnotationMapper.kt index aeeb362..02e6921 100644 --- a/vertx-demo/src/main/kotlin/app/util/SqlAnnotationMapper.kt +++ b/vertx-demo/src/main/kotlin/app/util/SqlAnnotationMapper.kt @@ -85,7 +85,7 @@ class SqlAnnotationMapperGenerator { // 获取表名 mapper.tableName?.let { tableNameMapping -> - val annotation = entityClass.annotations.find { it.annotationClass == tableNameMapping.annotationClass.java } + val annotation = entityClass.annotations.find { it.annotationClass.toString() == tableNameMapping.annotationClass.toString() } annotation?.let { val method = it.javaClass.getMethod(tableNameMapping.propertyName) sqlInfo.tableName = method.invoke(it) as String diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/SqlHelper.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/SqlHelper.kt deleted file mode 100644 index 3f16260..0000000 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/SqlHelper.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.aikrai.vertx.db - -object SqlHelper { - - fun retBool(result: Int?): Boolean { - return null != result && result >= 1 - } -} diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/SqlAnnotationMapper.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/SqlAnnotationMapper.kt deleted file mode 100644 index 81140eb..0000000 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/SqlAnnotationMapper.kt +++ /dev/null @@ -1,239 +0,0 @@ -package org.aikrai.vertx.db.annotation - -import kotlin.reflect.KClass -import kotlin.reflect.KProperty -import kotlin.reflect.full.findAnnotation -import kotlin.reflect.full.hasAnnotation -import kotlin.reflect.full.memberProperties - -/** - * SQL注解映射中间类 - * 用于从实体类及其注解中提取信息以生成SQL - * 支持多套注解系统的映射配置 - */ -class SqlAnnotationMapper { - // 表名注解配置 - var tableNameAnnotationClass: KClass = TableName::class - var tableNameProperty: String = "value" - var tableNameDefaultMapper: (KClass<*>) -> String = { klass -> camelToSnake(klass.simpleName!!) } - - // 主键注解配置 - var tableIdAnnotationClass: KClass = TableId::class - var tableIdNameProperty: String = "value" - var tableIdTypeProperty: String = "type" - var tableIdDefaultMapper: (KProperty<*>) -> Boolean = { property -> - property.name == "id" || property.name.endsWith("Id") - } - - // 字段注解配置 - var tableFieldAnnotationClass: KClass = TableField::class - var tableFieldNameProperty: String = "value" - var tableFieldFillProperty: String = "fill" - var tableFieldDefaultMapper: (KProperty<*>) -> String = { property -> camelToSnake(property.name) } - - // 索引注解配置 - var tableIndexAnnotationClass: KClass = TableIndex::class - var tableIndexNameProperty: String = "name" - var tableIndexUniqueProperty: String = "unique" - var tableIndexColumnsProperty: String = "columnNames" - - // 枚举值注解配置 - var enumValueAnnotationClass: KClass = EnumValue::class - - /** - * 从实体类中获取表名 - * @param entityClass 实体类 - * @return 表名 - */ - fun getTableName(entityClass: KClass<*>): String { - // 获取所有注解 - val annotations = entityClass.annotations.filter { it.annotationClass == tableNameAnnotationClass } - - return if (annotations.isNotEmpty()) { - val annotation = annotations.first() - // 通过反射获取注解的value属性值 - val method = annotation::class.java.getMethod(tableNameProperty) - val value = method.invoke(annotation) as String - - if (value.isNotEmpty()) value else tableNameDefaultMapper(entityClass) - } else { - tableNameDefaultMapper(entityClass) - } - } - - /** - * 判断属性是否为主键 - * @param property 属性 - * @return 是否为主键 - */ - fun isTableId(property: KProperty<*>): Boolean { - val hasAnnotation = property.annotations.any { it.annotationClass == tableIdAnnotationClass } - return hasAnnotation || tableIdDefaultMapper(property) - } - - /** - * 获取属性对应的列名 - * @param property 属性 - * @return 列名 - */ - fun getColumnName(property: KProperty<*>): String { - val annotations = property.annotations.filter { it.annotationClass == tableFieldAnnotationClass } - - return if (annotations.isNotEmpty()) { - val annotation = annotations.first() - // 通过反射获取注解的value属性值 - val method = annotation::class.java.getMethod(tableFieldNameProperty) - val value = method.invoke(annotation) as String - - if (value.isNotEmpty()) value else tableFieldDefaultMapper(property) - } else { - tableFieldDefaultMapper(property) - } - } - - /** - * 获取主键类型 - * @param property 属性 - * @return 主键类型(如果有) - */ - fun getIdType(property: KProperty<*>): Any? { - val annotations = property.annotations.filter { it.annotationClass == tableIdAnnotationClass } - if (annotations.isEmpty()) return null - - val annotation = annotations.first() - // 通过反射获取注解的type属性值 - val method = annotation::class.java.getMethod(tableIdTypeProperty) - return method.invoke(annotation) - } - - /** - * 获取字段填充策略 - * @param property 属性 - * @return 填充策略(如果有) - */ - fun getFieldFill(property: KProperty<*>): Any? { - val annotations = property.annotations.filter { it.annotationClass == tableFieldAnnotationClass } - if (annotations.isEmpty()) return null - - val annotation = annotations.first() - // 通过反射获取注解的fill属性值 - val method = annotation::class.java.getMethod(tableFieldFillProperty) - return method.invoke(annotation) - } - - /** - * 获取实体类的所有索引配置 - * @param entityClass 实体类 - * @return 索引配置列表 - */ - fun getTableIndices(entityClass: KClass<*>): List { - val annotations = entityClass.annotations.filter { it.annotationClass == tableIndexAnnotationClass } - - return annotations.map { annotation -> - val nameMethod = annotation::class.java.getMethod(tableIndexNameProperty) - val uniqueMethod = annotation::class.java.getMethod(tableIndexUniqueProperty) - val columnsMethod = annotation::class.java.getMethod(tableIndexColumnsProperty) - - val name = nameMethod.invoke(annotation) as String - val unique = uniqueMethod.invoke(annotation) as Boolean - val columns = columnsMethod.invoke(annotation) as Array<*> - - TableIndexInfo( - name = name, - unique = unique, - columnNames = columns.map { it.toString() } - ) - } - } - - /** - * 获取实体类的所有属性信息 - * @param entityClass 实体类 - * @return 属性信息列表 - */ - fun getEntityProperties(entityClass: KClass<*>): List { - return entityClass.memberProperties.map { property -> - PropertyInfo( - property = property, - columnName = getColumnName(property), - isPrimary = isTableId(property), - idType = getIdType(property), - fieldFill = getFieldFill(property) - ) - } - } - - /** - * 从实体类中提取所有SQL相关信息 - * @param entityClass 实体类 - * @return 实体类SQL信息 - */ - fun extractEntityInfo(entityClass: KClass<*>): EntityInfo { - return EntityInfo( - entityClass = entityClass, - tableName = getTableName(entityClass), - properties = getEntityProperties(entityClass), - indices = getTableIndices(entityClass) - ) - } - - /** - * 驼峰命名转下划线命名 - * @param name 驼峰命名 - * @return 下划线命名 - */ - private fun camelToSnake(name: String): String { - return name.replace(Regex("([a-z0-9])([A-Z])"), "$1_$2").lowercase() - } - - /** - * 表索引信息 - */ - data class TableIndexInfo( - val name: String, - val unique: Boolean, - val columnNames: List - ) - - /** - * 属性信息 - */ - data class PropertyInfo( - val property: KProperty<*>, - val columnName: String, - val isPrimary: Boolean, - val idType: Any?, - val fieldFill: Any? - ) - - /** - * 实体类SQL信息 - */ - data class EntityInfo( - val entityClass: KClass<*>, - val tableName: String, - val properties: List, - val indices: List - ) - - companion object { - /** - * 创建默认配置的SQL注解映射器 - * @return SQL注解映射器 - */ - fun createDefault(): SqlAnnotationMapper { - return SqlAnnotationMapper() - } - - /** - * 创建自定义配置的SQL注解映射器 - * @param configure 配置函数 - * @return SQL注解映射器 - */ - fun create(configure: SqlAnnotationMapper.() -> Unit): SqlAnnotationMapper { - val mapper = SqlAnnotationMapper() - mapper.configure() - return mapper - } - } -} \ No newline at end of file diff --git a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/SqlAnnotationMapperExample.kt b/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/SqlAnnotationMapperExample.kt deleted file mode 100644 index 99f5e60..0000000 --- a/vertx-fw/src/main/kotlin/org/aikrai/vertx/db/annotation/SqlAnnotationMapperExample.kt +++ /dev/null @@ -1,152 +0,0 @@ -package org.aikrai.vertx.db.annotation - -import kotlin.reflect.KClass -import kotlin.reflect.full.createInstance - -/** - * SqlAnnotationMapper 使用示例 - */ -class SqlAnnotationMapperExample { - - /** - * 使用默认配置的中间类示例 - */ - fun defaultMapperExample(entityClass: KClass<*>) { - // 创建默认配置的SQL注解映射器 - val mapper = SqlAnnotationMapper.createDefault() - - // 从实体类中提取信息 - val entityInfo = mapper.extractEntityInfo(entityClass) - - // 输出实体信息 - println("表名: ${entityInfo.tableName}") - println("字段:") - entityInfo.properties.forEach { prop -> - println(" ${prop.columnName} - 主键: ${prop.isPrimary} - 类型: ${prop.property.returnType}") - if (prop.idType != null) { - println(" ID类型: ${prop.idType}") - } - if (prop.fieldFill != null) { - println(" 填充策略: ${prop.fieldFill}") - } - } - - println("索引:") - entityInfo.indices.forEach { index -> - println(" ${index.name} - 唯一: ${index.unique} - 列: ${index.columnNames.joinToString(", ")}") - } - } - - /** - * 配置自定义注解映射示例 - */ - fun customMapperExample(entityClass: KClass<*>) { - // 创建自定义配置的SQL注解映射器 - val mapper = SqlAnnotationMapper.create { - // 假设我们使用了另一套注解系统,例如JPA注解 - - // 配置表名注解 - 使用JPA的@Table注解 - tableNameAnnotationClass = Table::class - tableNameProperty = "name" - - // 配置主键注解 - 使用JPA的@Id注解 - tableIdAnnotationClass = Id::class - - // 配置字段注解 - 使用JPA的@Column注解 - tableFieldAnnotationClass = Column::class - tableFieldNameProperty = "name" - - // 自定义名称转换规则 - tableNameDefaultMapper = { kClass -> kClass.simpleName!!.lowercase() } - tableFieldDefaultMapper = { property -> property.name.lowercase() } - } - - // 从实体类中提取信息 - val entityInfo = mapper.extractEntityInfo(entityClass) - - // 输出实体信息 - println("表名: ${entityInfo.tableName}") - println("字段:") - entityInfo.properties.forEach { prop -> - println(" ${prop.columnName} - 主键: ${prop.isPrimary}") - } - } - - /** - * SQL生成器示例,展示如何根据提取的信息生成SQL - */ - fun sqlGeneratorExample(entityClass: KClass<*>) { - // 创建SQL注解映射器 - val mapper = SqlAnnotationMapper.createDefault() - - // 提取实体信息 - val entityInfo = mapper.extractEntityInfo(entityClass) - - // 生成建表SQL - val createTableSql = generateCreateTableSql(entityInfo) - println("建表SQL:") - println(createTableSql) - } - - /** - * 生成建表SQL - */ - private fun generateCreateTableSql(entityInfo: SqlAnnotationMapper.EntityInfo): String { - val sb = StringBuilder() - - sb.append("CREATE TABLE IF NOT EXISTS ${entityInfo.tableName} (\n") - - // 添加字段定义 - val columns = entityInfo.properties.map { prop -> - val primaryKey = if (prop.isPrimary) " PRIMARY KEY" else "" - " ${prop.columnName} ${mapTypeToPostgresType(prop)} ${primaryKey}" - } - - sb.append(columns.joinToString(",\n")) - sb.append("\n);") - - // 添加索引 - entityInfo.indices.forEach { index -> - val unique = if (index.unique) "UNIQUE " else "" - sb.append("\n\nCREATE ${unique}INDEX IF NOT EXISTS ${index.name} ON ${entityInfo.tableName} (${index.columnNames.joinToString(", ")});") - } - - return sb.toString() - } - - - - /** - * 将属性类型映射为PostgreSQL类型(简化示例) - */ - private fun mapTypeToPostgresType(property: SqlAnnotationMapper.PropertyInfo): String { - val typeName = property.property.returnType.toString() - - return when { - typeName.contains("String") -> "VARCHAR(255)" - typeName.contains("Int") -> "INTEGER" - typeName.contains("Long") -> "BIGINT" - typeName.contains("Boolean") -> "BOOLEAN" - typeName.contains("Double") -> "DOUBLE PRECISION" - typeName.contains("Float") -> "REAL" - typeName.contains("java.sql.Timestamp") || typeName.contains("java.util.Date") -> "TIMESTAMP" - typeName.contains("LocalDateTime") -> "TIMESTAMP" - typeName.contains("LocalDate") -> "DATE" - typeName.contains("Char") -> "CHAR(1)" - else -> "JSONB" // 默认复杂类型存储为JSON - } - } -} - -// JPA注解模拟 - 仅用于示例 -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.CLASS) -annotation class Table(val name: String = "") - -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.FIELD) -annotation class Id - -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.FIELD) -annotation class Column(val name: String = "") \ No newline at end of file