From c2d1d743e0e51258709d94154668867360048cea Mon Sep 17 00:00:00 2001 From: Enaium Date: Sun, 25 May 2025 16:02:12 +0800 Subject: [PATCH] Database enhancement #16 --- .github/workflows/deploy-document.yml | 4 + core/build.gradle.kts | 7 +- .../buddy/database/generate/EntityGenerate.kt | 51 +++++++- .../database/generate/JavaEntityGenerate.kt | 24 +++- .../database/generate/KotlinEntityGenerate.kt | 22 +++- .../database/model/GenerateEntityModel.kt | 24 ++-- .../jimmer/buddy/database/model/UniqueKey.kt | 2 +- .../enaium/jimmer/buddy/dialog/AddDatabase.kt | 11 +- .../buddy/dialog/GenerateEntityDialog.kt | 70 +++++++---- .../jimmer/buddy/dialog/NewDtoFileDialog.kt | 8 +- ...ctTemplateFile.kt => BuddyTemplateFile.kt} | 4 +- .../wizard/JimmerProjectBuilderAdapter.kt | 14 +-- .../extensions/wizard/JimmerProjectPanel.kt | 15 +-- .../buddy/storage/JimmerBuddySetting.kt | 3 +- .../cn/enaium/jimmer/buddy/utility/jdbc.kt | 2 +- .../cn/enaium/jimmer/buddy/utility/ui.kt | 112 ++++++++++++++++++ .../j2ee/generate-entity-doc.txt.ft | 0 gradle.properties | 2 +- gradle/libs.versions.toml | 2 + gradle/wrapper/gradle-wrapper.properties | 2 +- since/shared/resources/META-INF/plugin.xml | 2 +- 21 files changed, 302 insertions(+), 79 deletions(-) rename core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/template/{JimmerProjectTemplateFile.kt => BuddyTemplateFile.kt} (91%) create mode 100644 core/src/main/kotlin/cn/enaium/jimmer/buddy/utility/ui.kt create mode 100644 core/src/main/resources/fileTemplates/j2ee/generate-entity-doc.txt.ft diff --git a/.github/workflows/deploy-document.yml b/.github/workflows/deploy-document.yml index c985bba..322dbce 100644 --- a/.github/workflows/deploy-document.yml +++ b/.github/workflows/deploy-document.yml @@ -3,8 +3,12 @@ name: Deploy Document on: push: branches: [master] + paths: + - 'document/**' pull_request: branches: [master] + paths: + - 'document/**' workflow_dispatch: permissions: diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 5c0fed0..4829db5 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -6,12 +6,6 @@ plugins { group = "cn.enaium" version = "${property("version")}" -sourceSets { - main { - resources.srcDir("../shared/src/main/resources") - } -} - repositories { mavenCentral() intellijPlatform { @@ -38,6 +32,7 @@ dependencies { implementation(libs.kotlinpoet) implementation(libs.javapoet) implementation(libs.antlr4.intellij.adaptor) + implementation(libs.h2) implementation(project(":common")) } diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/EntityGenerate.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/EntityGenerate.kt index a93c3cc..a73ff9f 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/EntityGenerate.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/EntityGenerate.kt @@ -19,6 +19,7 @@ package cn.enaium.jimmer.buddy.database.generate import cn.enaium.jimmer.buddy.database.model.Column import cn.enaium.jimmer.buddy.database.model.GenerateEntityModel import cn.enaium.jimmer.buddy.database.model.Table +import com.intellij.openapi.project.Project import java.nio.file.Path /** @@ -26,7 +27,7 @@ import java.nio.file.Path */ interface EntityGenerate { fun generate( - projectDir: Path, + project: Project, generateEntity: GenerateEntityModel, tables: Set ): List @@ -36,6 +37,54 @@ interface EntityGenerate { .filter { it -> it.value.size == tables.count { it.primaryKeys.isNotEmpty() } }.map { it.value.first() } .toSet() } + + fun replaceName(tables: Set
, generateEntity: GenerateEntityModel): Set
{ + return tables.map { table -> + table.copy( + name = table.name.replace(Regex(generateEntity.tableNameRegex), generateEntity.tableNameReplace), + columns = table.columns.map { column -> + column.copy( + name = column.name.replace( + Regex(generateEntity.columnNameRegex), + generateEntity.columnNameReplace + ) + ) + }.toSet(), + primaryKeys = table.primaryKeys.map { primaryKey -> + primaryKey.copy( + column = primaryKey.column.copy( + name = primaryKey.column.name.replace( + Regex(generateEntity.columnNameRegex), + generateEntity.columnNameReplace + ) + ) + ) + }.toSet(), + foreignKeys = table.foreignKeys.map { foreignKey -> + foreignKey.copy( + column = foreignKey.column.copy( + name = foreignKey.column.name.replace( + Regex(generateEntity.columnNameRegex), + generateEntity.columnNameReplace + ) + ) + ) + }.toMutableSet(), + uniqueKeys = table.uniqueKeys.map { uniqueKey -> + uniqueKey.copy( + columns = uniqueKey.columns.map { column -> + column.copy( + name = column.name.replace( + Regex(generateEntity.columnNameRegex), + generateEntity.columnNameReplace + ) + ) + }.toSet() + ) + }.toSet() + ) + }.toSet() + } } internal const val BASE_ENTITY = "BaseEntity" \ No newline at end of file diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/JavaEntityGenerate.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/JavaEntityGenerate.kt index 1713def..f71e252 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/JavaEntityGenerate.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/JavaEntityGenerate.kt @@ -19,10 +19,14 @@ package cn.enaium.jimmer.buddy.database.generate import cn.enaium.jimmer.buddy.database.model.Column import cn.enaium.jimmer.buddy.database.model.ForeignKey import cn.enaium.jimmer.buddy.database.model.GenerateEntityModel +import cn.enaium.jimmer.buddy.extensions.template.BuddyTemplateFile import cn.enaium.jimmer.buddy.storage.JimmerBuddySetting import cn.enaium.jimmer.buddy.utility.firstCharLowercase import cn.enaium.jimmer.buddy.utility.snakeToCamelCase import cn.enaium.jimmer.buddy.utility.toPlural +import com.intellij.ide.fileTemplates.FileTemplateManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.guessProjectDir import com.squareup.javapoet.* import org.babyfish.jimmer.sql.* import org.jetbrains.annotations.Nullable @@ -38,10 +42,13 @@ class JavaEntityGenerate : EntityGenerate { val javaTypeMappings = JimmerBuddySetting.INSTANCE.state.typeMapping.mapValues { it.value.javaType } override fun generate( - projectDir: Path, + project: Project, generateEntity: GenerateEntityModel, tables: Set ): List { + + val tables = replaceName(tables, generateEntity) + val idSuffix = "_${generateEntity.primaryKeyName}" val commonColumns = getCommonColumns(tables) @@ -111,6 +118,15 @@ class JavaEntityGenerate : EntityGenerate { } } + try { + val templateManager = FileTemplateManager.getInstance(project) + templateManager.getInternalTemplate(BuddyTemplateFile.GENERATE_ENTITY_DOC).also { + type.addJavadoc(it.getText(templateManager.defaultProperties)) + } + } catch (_: Throwable) { + + } + if (commonColumns.isNotEmpty()) type.addSuperinterface(ClassName.get(packageName, BASE_ENTITY)) // Add table columns type.addMethods( @@ -289,12 +305,12 @@ class JavaEntityGenerate : EntityGenerate { } // Write to file - return type2Builder.map { (_, type) -> - projectDir.resolve(generateEntity.relativePath).also { + return type2Builder.mapNotNull { (_, type) -> + project.guessProjectDir()?.toNioPath()?.resolve(generateEntity.relativePath)?.let { JavaFile.builder(packageName, type.build()) .indent(" ") .build() - .writeTo(it) + .writeToPath(it) } } } diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/KotlinEntityGenerate.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/KotlinEntityGenerate.kt index f623d65..dbb595e 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/KotlinEntityGenerate.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/generate/KotlinEntityGenerate.kt @@ -19,10 +19,14 @@ package cn.enaium.jimmer.buddy.database.generate import cn.enaium.jimmer.buddy.database.model.Column import cn.enaium.jimmer.buddy.database.model.ForeignKey import cn.enaium.jimmer.buddy.database.model.GenerateEntityModel +import cn.enaium.jimmer.buddy.extensions.template.BuddyTemplateFile import cn.enaium.jimmer.buddy.storage.JimmerBuddySetting import cn.enaium.jimmer.buddy.utility.firstCharLowercase import cn.enaium.jimmer.buddy.utility.snakeToCamelCase import cn.enaium.jimmer.buddy.utility.toPlural +import com.intellij.ide.fileTemplates.FileTemplateManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.guessProjectDir import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import org.babyfish.jimmer.sql.* @@ -37,10 +41,13 @@ class KotlinEntityGenerate : EntityGenerate { val kotlinTypeMappings = JimmerBuddySetting.INSTANCE.state.typeMapping.mapValues { it.value.kotlinType } override fun generate( - projectDir: Path, + project: Project, generateEntity: GenerateEntityModel, tables: Set ): List { + + val tables = replaceName(tables, generateEntity) + val idSuffix = "_${generateEntity.primaryKeyName}" val commonColumns = getCommonColumns(tables) @@ -110,6 +117,15 @@ class KotlinEntityGenerate : EntityGenerate { } } + try { + val templateManager = FileTemplateManager.getInstance(project) + templateManager.getInternalTemplate(BuddyTemplateFile.GENERATE_ENTITY_DOC).also { + type.addKdoc(it.getText(templateManager.defaultProperties)) + } + } catch (_: Throwable) { + + } + if (commonColumns.isNotEmpty()) type.addSuperinterface(ClassName(packageName, BASE_ENTITY)) // Add table columns type.addProperties( @@ -265,8 +281,8 @@ class KotlinEntityGenerate : EntityGenerate { } // Write to file - return type2Builder.map { (tableName, typeBuilder) -> - projectDir.resolve(generateEntity.relativePath).also { + return type2Builder.mapNotNull { (tableName, typeBuilder) -> + project.guessProjectDir()?.toNioPath()?.resolve(generateEntity.relativePath)?.let { FileSpec.builder(packageName, tableName) .indent(" ") .addType(typeBuilder.build()).build() diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/model/GenerateEntityModel.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/model/GenerateEntityModel.kt index dc740ec..d4d30c6 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/model/GenerateEntityModel.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/model/GenerateEntityModel.kt @@ -24,16 +24,20 @@ import com.intellij.openapi.observable.properties.PropertyGraph */ class GenerateEntityModel : BaseState() { private val graph: PropertyGraph = PropertyGraph() - val relativePathProperty = graph.property("") - val packageNameProperty = graph.property("") + val relativePathProperty = graph.property("") + val packageNameProperty = graph.property("") val languageProperty = graph.property(Language.KOTLIN) - val commentProperty = graph.property(false) - val tableAnnotationProperty = graph.property(false) - val columnAnnotationProperty = graph.property(false) - val idViewAnnotationProperty = graph.property(false) - val joinTableAnnotationProperty = graph.property(false) - val primaryKeyNameProperty = graph.property("id") + val commentProperty = graph.property(false) + val tableAnnotationProperty = graph.property(false) + val columnAnnotationProperty = graph.property(false) + val idViewAnnotationProperty = graph.property(false) + val joinTableAnnotationProperty = graph.property(false) + val primaryKeyNameProperty = graph.property("id") val associationProperty = graph.property(Association.REAL) + val tableNameRegexProperty = graph.property("") + val tableNameReplaceProperty = graph.property("") + val columnNameRegexProperty = graph.property("") + val columnNameReplaceProperty = graph.property("") val relativePath: String by relativePathProperty val packageName: String by packageNameProperty @@ -45,6 +49,10 @@ class GenerateEntityModel : BaseState() { val joinTableAnnotation: Boolean by joinTableAnnotationProperty val primaryKeyName: String by primaryKeyNameProperty val association: Association by associationProperty + val tableNameRegex: String by tableNameRegexProperty + val tableNameReplace: String by tableNameReplaceProperty + val columnNameRegex: String by columnNameRegexProperty + val columnNameReplace: String by columnNameReplaceProperty enum class Language(val text: String) { KOTLIN("Kotlin"), diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/model/UniqueKey.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/model/UniqueKey.kt index 75d953b..3e6bd74 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/model/UniqueKey.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/database/model/UniqueKey.kt @@ -22,7 +22,7 @@ package cn.enaium.jimmer.buddy.database.model data class UniqueKey( val name: String, val tableName: String, - val columns: List, + val columns: Set, ) { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/AddDatabase.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/AddDatabase.kt index 69fe1b8..bd30011 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/AddDatabase.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/AddDatabase.kt @@ -18,6 +18,7 @@ package cn.enaium.jimmer.buddy.dialog import cn.enaium.jimmer.buddy.storage.JimmerBuddySetting import cn.enaium.jimmer.buddy.storage.JimmerBuddySetting.DatabaseItem +import cn.enaium.jimmer.buddy.utility.jarFileChooserField import com.intellij.openapi.components.BaseState import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.ui.DialogWrapper @@ -58,6 +59,9 @@ class AddDatabase(val select: DatabaseItem? = null) : DialogWrapper(false) { row("Table Name Pattern:") { textField().align(Align.FILL).bindText(databaseModel.tableNamePatternProperty) } + row("Driver File:") { + jarFileChooserField(databaseModel.driverFileProperty).align(Align.FILL) + } } } @@ -73,19 +77,21 @@ class AddDatabase(val select: DatabaseItem? = null) : DialogWrapper(false) { databaseModel.password, databaseModel.catalog, databaseModel.schemaPattern, - databaseModel.tableNamePattern + databaseModel.tableNamePattern, + databaseModel.driverFile ) super.doOKAction() } private inner class DatabaseModel : BaseState() { private val graph: PropertyGraph = PropertyGraph() - val uriProperty = graph.property(select?.uri ?: "") + val uriProperty = graph.property(select?.uri ?: "") val usernameProperty = graph.property(select?.username ?: "") val passwordProperty = graph.property(select?.password ?: "") val catalogProperty = graph.property(select?.catalog ?: "") val schemaPatternProperty = graph.property(select?.schemaPattern ?: "") val tableNamePatternProperty = graph.property(select?.tableNamePattern ?: "") + val driverFileProperty = graph.property(select?.driverFile ?: "") val uri: String by uriProperty val username: String by usernameProperty @@ -93,5 +99,6 @@ class AddDatabase(val select: DatabaseItem? = null) : DialogWrapper(false) { val catalog: String by catalogProperty val schemaPattern: String by schemaPatternProperty val tableNamePattern: String by tableNamePatternProperty + val driverFile: String by driverFileProperty } } \ No newline at end of file diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/GenerateEntityDialog.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/GenerateEntityDialog.kt index d7438a8..ae3efd3 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/GenerateEntityDialog.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/GenerateEntityDialog.kt @@ -24,6 +24,8 @@ import cn.enaium.jimmer.buddy.database.model.Table import cn.enaium.jimmer.buddy.dialog.panel.TableTreeTable import cn.enaium.jimmer.buddy.storage.JimmerBuddySetting import cn.enaium.jimmer.buddy.utility.getTables +import cn.enaium.jimmer.buddy.utility.packageChooserField +import cn.enaium.jimmer.buddy.utility.relativeLocationField import com.intellij.openapi.project.Project import com.intellij.openapi.project.guessProjectDir import com.intellij.openapi.ui.DialogWrapper @@ -45,6 +47,7 @@ import java.util.* import java.util.logging.Logger import javax.swing.JComponent import kotlin.io.path.Path +import kotlin.io.path.exists import kotlin.io.path.isDirectory import kotlin.io.path.name import kotlin.io.path.walk @@ -78,10 +81,10 @@ class GenerateEntityDialog( return borderPanel { addToTop(panel { row("Relative Path:") { - textField().align(Align.FILL).bindText(generateEntityModel.relativePathProperty) + relativeLocationField(project, generateEntityModel.relativePathProperty).align(Align.FILL) } row("Package Name:") { - textField().align(Align.FILL).bindText(generateEntityModel.packageNameProperty) + packageChooserField(project, generateEntityModel.packageNameProperty).align(Align.FILL) } row("Language:") { JimmerBuddy.Services.UI.segmentedButtonText(this, GenerateEntityModel.Language.entries) { @@ -105,6 +108,14 @@ class GenerateEntityDialog( it.text }.bind(generateEntityModel.associationProperty) } + row("Table Name Regex:") { + textField().align(Align.FILL).bindText(generateEntityModel.tableNameRegexProperty) + textField().align(Align.FILL).bindText(generateEntityModel.tableNameReplaceProperty) + } + row("Column Name Regex:") { + textField().align(Align.FILL).bindText(generateEntityModel.columnNameRegexProperty) + textField().align(Align.FILL).bindText(generateEntityModel.columnNameReplaceProperty) + } } }) addToCenter(tableTreeTable) @@ -113,9 +124,9 @@ class GenerateEntityDialog( private fun getTables(): Set
{ val uri = databaseItem.uri - + val isDDL = uri.startsWith("file:") val jdbcDriver = JdbcDriver.entries.find { uri.startsWith("jdbc:${it.scheme}") } ?: let { - if (uri.startsWith("file:")) { + if (isDDL) { return@let JdbcDriver.H2 } else { Messages.showErrorDialog( @@ -146,23 +157,38 @@ class GenerateEntityDialog( } } - listOfNotNull( - Path(System.getProperty("user.home")).resolve(".gradle"), - System.getenv("GRADLE_USER_HOME")?.takeIf { it.isNotBlank() }?.let { Path.of(it) }, - ).forEach { path -> - path.resolve("caches/modules-2/files-2.1/${jdbcDriver.group}/${jdbcDriver.artifact}") - .walk().findLast { - it.isDirectory().not() - && it.name.endsWith("-sources.jar").not() - && it.name.endsWith("-javadoc.jar").not() - && it.name.endsWith(".jar") - }?.also { - driverJarFile = it - } + if (driverJarFile == null) { + listOfNotNull( + Path(System.getProperty("user.home")).resolve(".gradle"), + System.getenv("GRADLE_USER_HOME")?.takeIf { it.isNotBlank() }?.let { Path.of(it) }, + ).forEach { path -> + path.resolve("caches/modules-2/files-2.1/${jdbcDriver.group}/${jdbcDriver.artifact}") + .walk().findLast { + it.isDirectory().not() + && it.name.endsWith("-sources.jar").not() + && it.name.endsWith("-javadoc.jar").not() + && it.name.endsWith(".jar") + }?.also { + driverJarFile = it + } + } } if (driverJarFile == null) { - Messages.showErrorDialog("Failed to find driver jar, please check your maven or gradle cache", "Error") + driverJarFile = Path(databaseItem.driverFile).takeIf { it.exists() } + } + + if (driverJarFile == null) { + if (isDDL) { + Messages.showErrorDialog( + "Failed to find H2 driver jar, please check your maven or gradle cache", + "Error" + ) + } else if (databaseItem.driverFile.isNotBlank()) { + Messages.showErrorDialog("Driver file is not found", "Error"); + } else { + Messages.showErrorDialog("Failed to find driver jar, please check your maven or gradle cache", "Error") + } return emptySet() } @@ -184,11 +210,10 @@ class GenerateEntityDialog( } } - private fun getConnection(): Connection { - return URI.create(databaseItem.uri).takeIf { it.scheme == "file" }?.let { ddl -> + return URI.create(databaseItem.uri.replace("\\", "/")).takeIf { it.scheme == "file" }?.let { ddl -> DriverManager.getConnection( - "jdbc:h2:mem:test;DATABASE_TO_LOWER=true;INIT=RUNSCRIPT FROM '${ + "jdbc:h2:mem:jimmer-buddy-ddl;DATABASE_TO_LOWER=true;INIT=RUNSCRIPT FROM '${ ddl.toURL().toPath().absolutePath.replace( "\\", "/" @@ -226,10 +251,9 @@ class GenerateEntityDialog( } } - val projectDir = project.guessProjectDir() ?: return JimmerBuddy.getWorkspace(project).asyncRefresh( generate.generate( - projectDir.toNioPath(), + project, generateEntityModel, result ) diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/NewDtoFileDialog.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/NewDtoFileDialog.kt index 014477e..0d1539a 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/NewDtoFileDialog.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/dialog/NewDtoFileDialog.kt @@ -18,7 +18,7 @@ package cn.enaium.jimmer.buddy.dialog import cn.enaium.jimmer.buddy.JimmerBuddy import cn.enaium.jimmer.buddy.dialog.panel.ImmutablePropsChoosePanel -import cn.enaium.jimmer.buddy.extensions.template.JimmerProjectTemplateFile +import cn.enaium.jimmer.buddy.extensions.template.BuddyTemplateFile import cn.enaium.jimmer.buddy.utility.* import com.intellij.diff.DiffContentFactory import com.intellij.diff.DiffManager @@ -75,7 +75,7 @@ class NewDtoFileDialog( textField().align(Align.FILL).bindText(model.immutableNameProperty) } row("Package Name:") { - textField().align(Align.FILL).bindText(model.packageNameProperty) + packageChooserField(project, model.packageNameProperty).align(Align.FILL) } row("DTO File Name:") { textField().align(Align.FILL).bindText(model.dtoFileNameProperty) @@ -118,7 +118,7 @@ class NewDtoFileDialog( findProjectDir(sourceFile)?.also { projectDir -> val dtoFile = projectDir.resolve("src/main/dto/${model.dtoFileName}.dto") val fileTemplateManager = FileTemplateManager.getInstance(project) - val dtoHeadTemplate = fileTemplateManager.getInternalTemplate(JimmerProjectTemplateFile.JIMMER_DTO_HEAD) + val dtoHeadTemplate = fileTemplateManager.getInternalTemplate(BuddyTemplateFile.JIMMER_DTO_HEAD) val dtoHeadContent = dtoHeadTemplate.getText( mapOf( "IMMUTABLE_NAME" to model.immutableName, @@ -126,7 +126,7 @@ class NewDtoFileDialog( ) ) val dtoContentTemplate = - fileTemplateManager.getInternalTemplate(JimmerProjectTemplateFile.JIMMER_DTO_CONTENT) + fileTemplateManager.getInternalTemplate(BuddyTemplateFile.JIMMER_DTO_CONTENT) val dtoContent = dtoContentTemplate.getText( mapOf( "DTO_TYPES" to listOf( diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/template/JimmerProjectTemplateFile.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/template/BuddyTemplateFile.kt similarity index 91% rename from core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/template/JimmerProjectTemplateFile.kt rename to core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/template/BuddyTemplateFile.kt index a791fc6..13025c3 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/template/JimmerProjectTemplateFile.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/template/BuddyTemplateFile.kt @@ -23,7 +23,7 @@ import com.intellij.ide.fileTemplates.FileTemplateGroupDescriptorFactory /** * @author Enaium */ -class JimmerProjectTemplateFile : FileTemplateGroupDescriptorFactory { +class BuddyTemplateFile : FileTemplateGroupDescriptorFactory { companion object { const val MAVEN_WRAPPER = "jimmer-maven-wrapper.properties" @@ -34,6 +34,7 @@ class JimmerProjectTemplateFile : FileTemplateGroupDescriptorFactory { const val GRADLE_TOML = "jimmer-gradle-toml.toml" const val JIMMER_DTO_HEAD = "jimmer-dto-head.dto" const val JIMMER_DTO_CONTENT = "jimmer-dto-content.dto" + const val GENERATE_ENTITY_DOC = "generate-entity-doc.txt" } override fun getFileTemplatesDescriptor(): FileTemplateGroupDescriptor { @@ -46,6 +47,7 @@ class JimmerProjectTemplateFile : FileTemplateGroupDescriptorFactory { group.addTemplate(GRADLE_TOML) group.addTemplate(JIMMER_DTO_HEAD) group.addTemplate(JIMMER_DTO_CONTENT) + group.addTemplate(GENERATE_ENTITY_DOC) return group } } \ No newline at end of file diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/wizard/JimmerProjectBuilderAdapter.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/wizard/JimmerProjectBuilderAdapter.kt index 4e582d8..0cab51f 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/wizard/JimmerProjectBuilderAdapter.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/wizard/JimmerProjectBuilderAdapter.kt @@ -16,7 +16,7 @@ package cn.enaium.jimmer.buddy.extensions.wizard -import cn.enaium.jimmer.buddy.extensions.template.JimmerProjectTemplateFile +import cn.enaium.jimmer.buddy.extensions.template.BuddyTemplateFile import com.intellij.ide.fileTemplates.FileTemplateManager import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.ide.wizard.GeneratorNewProjectWizardBuilderAdapter @@ -245,7 +245,7 @@ class JimmerProjectBuilderAdapter(val jimmerWizard: JimmerProjectWizard = Jimmer val fileTemplateManager = FileTemplateManager.getInstance(project) when (projectModel.builder) { JimmerProjectModel.Builder.GRADLE -> { - val gradleWrapper = fileTemplateManager.getInternalTemplate(JimmerProjectTemplateFile.GRADLE_WRAPPER) + val gradleWrapper = fileTemplateManager.getInternalTemplate(BuddyTemplateFile.GRADLE_WRAPPER) val gradleWrapperContent = gradleWrapper.getText(mapOf("WRAPPER_VERSION" to projectModel.wrapperVersion)) val gradleWrapperPath = projectDir.resolve("gradle/wrapper/gradle-wrapper.properties") @@ -253,7 +253,7 @@ class JimmerProjectBuilderAdapter(val jimmerWizard: JimmerProjectWizard = Jimmer gradleWrapperPath.createParentDirectories() } gradleWrapperPath.writeText(gradleWrapperContent) - val gradleToml = fileTemplateManager.getInternalTemplate(JimmerProjectTemplateFile.GRADLE_TOML) + val gradleToml = fileTemplateManager.getInternalTemplate(BuddyTemplateFile.GRADLE_TOML) val gradleTomlContent = gradleToml.getText( mapOf( "versions" to versions, @@ -263,7 +263,7 @@ class JimmerProjectBuilderAdapter(val jimmerWizard: JimmerProjectWizard = Jimmer ) val gradleTomlPath = projectDir.resolve("gradle/libs.versions.toml") gradleTomlPath.writeText(gradleTomlContent) - val gradleBuild = fileTemplateManager.getInternalTemplate(JimmerProjectTemplateFile.GRADLE_BUILD) + val gradleBuild = fileTemplateManager.getInternalTemplate(BuddyTemplateFile.GRADLE_BUILD) val gradleBuildContent = gradleBuild.getText( mapOf( "GROUP" to projectModel.group, @@ -273,21 +273,21 @@ class JimmerProjectBuilderAdapter(val jimmerWizard: JimmerProjectWizard = Jimmer ) val gradleBuildPath = projectDir.resolve("build.gradle.kts") gradleBuildPath.writeText(gradleBuildContent) - val gradleSettings = fileTemplateManager.getInternalTemplate(JimmerProjectTemplateFile.GRADLE_SETTINGS) + val gradleSettings = fileTemplateManager.getInternalTemplate(BuddyTemplateFile.GRADLE_SETTINGS) val gradleSettingsContent = gradleSettings.getText(mapOf("ARTIFACT" to projectModel.artifact)) val gradleSettingsPath = projectDir.resolve("settings.gradle.kts") gradleSettingsPath.writeText(gradleSettingsContent) } JimmerProjectModel.Builder.MAVEN -> { - val mavenWrapper = fileTemplateManager.getInternalTemplate(JimmerProjectTemplateFile.MAVEN_WRAPPER) + val mavenWrapper = fileTemplateManager.getInternalTemplate(BuddyTemplateFile.MAVEN_WRAPPER) val mavenWrapperContent = mavenWrapper.getText(mapOf("WRAPPER_VERSION" to projectModel.wrapperVersion)) val mavenWrapperPath = projectDir.resolve(".mvn/wrapper/maven-wrapper.properties") if (mavenWrapperPath.exists().not()) { mavenWrapperPath.createParentDirectories() } mavenWrapperPath.writeText(mavenWrapperContent) - val mavenPom = fileTemplateManager.getInternalTemplate(JimmerProjectTemplateFile.MAVEN_POM) + val mavenPom = fileTemplateManager.getInternalTemplate(BuddyTemplateFile.MAVEN_POM) val mavenPomContent = mavenPom.getText( mapOf( "GROUP" to projectModel.group, diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/wizard/JimmerProjectPanel.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/wizard/JimmerProjectPanel.kt index 9fc8265..097a80a 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/wizard/JimmerProjectPanel.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/extensions/wizard/JimmerProjectPanel.kt @@ -17,6 +17,7 @@ package cn.enaium.jimmer.buddy.extensions.wizard import cn.enaium.jimmer.buddy.JimmerBuddy +import cn.enaium.jimmer.buddy.utility.projectLocationField import com.intellij.ide.IdeBundle import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory @@ -142,18 +143,4 @@ class JimmerProjectPanel(propertyGraph: PropertyGraph, private val wizardContext wizardContext.defaultModuleName = entityNameProperty.get() wizardContext.getUserData(JimmerBuddy.PROJECT_MODEL_PROP_KEY)?.set(projectModel) } - - private fun Row.projectLocationField( - locationProperty: GraphProperty, - wizardContext: WizardContext, - ): Cell { - val fileChooserDescriptor = - FileChooserDescriptorFactory.createSingleLocalFileDescriptor() - .withFileFilter { it.isDirectory } - .withPathToTextConvertor(::getPresentablePath) - .withTextToPathConvertor(::getCanonicalPath) - val title = IdeBundle.message("title.select.project.file.directory", wizardContext.presentationName) - val property = locationProperty.transform(::getPresentablePath, ::getCanonicalPath) - return textFieldWithBrowseButton(title, wizardContext.project, fileChooserDescriptor).bindText(property) - } } \ No newline at end of file diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/storage/JimmerBuddySetting.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/storage/JimmerBuddySetting.kt index f69a31e..a5dc3d8 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/storage/JimmerBuddySetting.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/storage/JimmerBuddySetting.kt @@ -110,7 +110,8 @@ class JimmerBuddySetting : PersistentStateComponent var password: String = "", var catalog: String = "", var schemaPattern: String = "", - var tableNamePattern: String = "" + var tableNamePattern: String = "", + var driverFile: String = "", ) data class JavaToKotlin( diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/utility/jdbc.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/utility/jdbc.kt index 4932d94..44d1582 100644 --- a/core/src/main/kotlin/cn/enaium/jimmer/buddy/utility/jdbc.kt +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/utility/jdbc.kt @@ -110,7 +110,7 @@ internal fun DatabaseMetaData.getUniqueKeys(tableName: String): Set { } } return uniqueKey2Columns.map { (name, columns) -> - UniqueKey(name, tableName, columns.map { getColumns(tableName).first { column -> column.name == it } }) + UniqueKey(name, tableName, columns.map { getColumns(tableName).first { column -> column.name == it } }.toSet()) }.toSet() } diff --git a/core/src/main/kotlin/cn/enaium/jimmer/buddy/utility/ui.kt b/core/src/main/kotlin/cn/enaium/jimmer/buddy/utility/ui.kt new file mode 100644 index 0000000..eae3f55 --- /dev/null +++ b/core/src/main/kotlin/cn/enaium/jimmer/buddy/utility/ui.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2025 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.jimmer.buddy.utility + +import com.intellij.icons.AllIcons +import com.intellij.ide.IdeBundle +import com.intellij.ide.util.PackageChooserDialog +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.util.bind +import com.intellij.openapi.observable.util.transform +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.guessProjectDir +import com.intellij.openapi.ui.BrowseFolderDescriptor.Companion.withPathToTextConvertor +import com.intellij.openapi.ui.BrowseFolderDescriptor.Companion.withTextToPathConvertor +import com.intellij.openapi.ui.TextFieldWithBrowseButton +import com.intellij.openapi.ui.addExtension +import com.intellij.openapi.ui.getCanonicalPath +import com.intellij.openapi.ui.getPresentablePath +import com.intellij.ui.components.fields.ExpandableTextField +import com.intellij.ui.components.fields.ExtendableTextField +import com.intellij.ui.dsl.builder.Cell +import com.intellij.ui.dsl.builder.Row +import com.intellij.ui.dsl.builder.bindText +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.pathString +import kotlin.io.path.relativeTo + +/** + * @author Enaium + */ +fun Row.projectLocationField( + locationProperty: GraphProperty, + wizardContext: WizardContext, +): Cell { + val fileChooserDescriptor = + FileChooserDescriptorFactory.createSingleLocalFileDescriptor() + .withFileFilter { it.isDirectory } + .withPathToTextConvertor(::getPresentablePath) + .withTextToPathConvertor(::getCanonicalPath) + val title = IdeBundle.message("title.select.project.file.directory", wizardContext.presentationName) + val property = locationProperty.transform(::getPresentablePath, ::getCanonicalPath) + return textFieldWithBrowseButton(title, wizardContext.project, fileChooserDescriptor).bindText(property) +} + +fun Row.packageChooserField( + project: Project, + property: GraphProperty, +): Cell { + return cell(ExtendableTextField().apply { + bind(property) + addExtension(AllIcons.Nodes.Package) { + val packageChooserDialog = PackageChooserDialog("Package Chooser", project) + if (packageChooserDialog.showAndGet()) { + packageChooserDialog.selectedPackage?.qualifiedName?.also { + property.set(it) + } + } + } + }) +} + +fun Row.relativeLocationField( + project: Project, + property: GraphProperty, +): Cell { + val fileChooserDescriptor = + FileChooserDescriptorFactory.createSingleFolderDescriptor() + .withFileFilter { it.isDirectory } + .withPathToTextConvertor { + getPresentablePath( + Path(it).relativeTo( + project.guessProjectDir()?.toNioPath() ?: return@withPathToTextConvertor getPresentablePath(it) + ).pathString + ) + } + .withTextToPathConvertor { + getCanonicalPath( + project.guessProjectDir()?.toNioPath()?.resolve(it)?.absolutePathString() + ?: return@withTextToPathConvertor getCanonicalPath(it) + ) + } + return textFieldWithBrowseButton("Select Source", project, fileChooserDescriptor).bindText(property) +} + +fun Row.jarFileChooserField( + locationProperty: GraphProperty, +): Cell { + val fileChooserDescriptor = + FileChooserDescriptorFactory.createSingleFileDescriptor("jar") + .withFileFilter { it.isDirectory } + .withPathToTextConvertor(::getPresentablePath) + .withTextToPathConvertor(::getCanonicalPath) + val property = locationProperty.transform(::getPresentablePath, ::getCanonicalPath) + return textFieldWithBrowseButton("Jar Chooser", null, fileChooserDescriptor).bindText(property) +} \ No newline at end of file diff --git a/core/src/main/resources/fileTemplates/j2ee/generate-entity-doc.txt.ft b/core/src/main/resources/fileTemplates/j2ee/generate-entity-doc.txt.ft new file mode 100644 index 0000000..e69de29 diff --git a/gradle.properties b/gradle.properties index e117fda..4132efa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official -version=1.7.0 +version=1.7.1 kotlin.daemon.jvmargs=-Xmx3G org.gradle.jvmargs=-Xmx3G \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 897e26f..ae3dd50 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ javapoet = "1.13.0" antlr4IntellijAdaptor = "0.1" antlr = "4.13.2" changelog = "2.2.1" +h2 = "2.3.232" [libraries] jimmer-core = { module = "org.babyfish.jimmer:jimmer-core", version.ref = "jimmer" } @@ -22,6 +23,7 @@ kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" } javapoet = { module = "com.squareup:javapoet", version.ref = "javapoet" } antlr4-intellij-adaptor = { module = "org.antlr:antlr4-intellij-adaptor", version.ref = "antlr4IntellijAdaptor" } antlr = { module = "org.antlr:antlr4", version.ref = "antlr" } +h2 = { module = "com.h2database:h2", version.ref = "h2" } [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0f0704a..caea35c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Feb 13 20:15:24 CST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/since/shared/resources/META-INF/plugin.xml b/since/shared/resources/META-INF/plugin.xml index 9a8d32c..3f26e4f 100644 --- a/since/shared/resources/META-INF/plugin.xml +++ b/since/shared/resources/META-INF/plugin.xml @@ -21,7 +21,7 @@ anchor="bottom" icon="/icons/log.svg"/> - +