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