diff --git a/build.gradle b/build.gradle index 247911b..9d27d35 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ configure(subprojects - project(':android')) { } subprojects { - version = '2.0.3' + version = '2.0.5' ext.appName = 'Desku-App' repositories { mavenCentral() diff --git a/core/build.gradle b/core/build.gradle index 4160466..4c429da 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -16,10 +16,13 @@ dependencies { implementation "mysql:mysql-connector-java:8.0.24" implementation 'net.java.dev.jna:jna:5.14.0' implementation 'net.java.dev.jna:jna-platform:5.14.0' + implementation "ru.lanwen.verbalregex:java-verbal-expressions:1.8" testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.0" testImplementation "org.junit.jupiter:junit-jupiter-engine:5.3.0" + testImplementation "com.vaadin:vaadin-bom:24.4.7" + testImplementation "com.vaadin:vaadin-core:24.4.7" } @@ -37,3 +40,7 @@ sourceSets { } // This must also be included if you want to generate the sources jar without issues tasks.withType(Jar).configureEach { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } + +tasks.test { + useJUnitPlatform() // For JUnit 5 +} diff --git a/core/src/main/java/com/osiris/jsqlgen/MainView.java b/core/src/main/java/com/osiris/jsqlgen/MainView.java index 1463723..3019493 100644 --- a/core/src/main/java/com/osiris/jsqlgen/MainView.java +++ b/core/src/main/java/com/osiris/jsqlgen/MainView.java @@ -930,46 +930,12 @@ private void updateColumn(Vertical listColumns, String dbName, String tableName, Objects.requireNonNull(t); Column col = Data.findColumn(t.columns, oldName); Objects.requireNonNull(col); - col.updateName(newName); - String oldDefinition = col.definition; - col.definition = newDefinition; - col.comment = newComment; + Column colOld = col.duplicate(); - // Update current change - t.currentChange.deletedColumnNames.remove(oldName); - if(t.currentChange.addedColumnNames.contains(oldName)){ - int i = t.currentChange.addedColumnNames.indexOf(oldName); - t.currentChange.addedColumnNames.set(i, newName); - t.currentChange.addedColumnDefinitions.set(i, newDefinition); - } else{ - // Existing column, check for changes - int i = t.currentChange.newColumnNames.indexOf(oldName); - if(i >= 0){ - t.currentChange.newColumnNames.remove(i); - t.currentChange.newColumnNames_Definitions.remove(i); - t.currentChange.oldColumnNames.remove(i); - } - if(!newName.equals(oldName)) { - t.currentChange.newColumnNames.add(newName); - t.currentChange.newColumnNames_Definitions.add(newDefinition); - t.currentChange.oldColumnNames.add(oldName); - } - - i = t.currentChange.newColumnDefinitions.indexOf(oldDefinition); - if(i >= 0){ - t.currentChange.newColumnDefinitions.remove(i); - t.currentChange.oldColumnDefinitions.remove(i); - t.currentChange.newColumnDefinitions_Names.remove(i); - } - if(!newDefinition.equals(oldDefinition)) { - t.currentChange.newColumnDefinitions.add(newDefinition); - t.currentChange.oldColumnDefinitions.add(oldDefinition); - t.currentChange.newColumnDefinitions_Names.add(newName); - } - } + t.updateCol(col, oldName, newName, newDefinition, newComment); - if(!oldDefinition.equals(newDefinition)) - AL.info("Updating column definition "+oldDefinition+ " -> " + newDefinition); + if(!colOld.definition.equals(newDefinition)) + AL.info("Updating column definition "+colOld.definition+ " -> " + newDefinition); Data.save(); AL.info("OK!"); } @@ -979,14 +945,7 @@ private void addNewColumn(Vertical listColumns, String dbName, String tableName, Table t = Data.findTable(db.tables, tableName); Objects.requireNonNull(t); col.id = Main.idCounter.getAndIncrement(); - t.columns.add(col); - - // Update current change - if(!t.currentChange.addedColumnNames.contains(col.name)){ - t.currentChange.addedColumnNames.add(col.name); - t.currentChange.addedColumnDefinitions.add(col.definition); - t.currentChange.deletedColumnNames.remove(col.name); - } + t.addCol(col); Data.save(); updateColumnsList(listColumns, dbName, tableName); @@ -998,17 +957,8 @@ private void deleteColumn(Vertical listColumns, String dbName, String tableName, Objects.requireNonNull(t); Column col = Data.findColumn(t.columns, columnName); Objects.requireNonNull(col); - t.columns.remove(col); - // Update current change - if(!t.currentChange.deletedColumnNames.contains(col.name)){ - t.currentChange.deletedColumnNames.add(col.name); - int i = t.currentChange.addedColumnNames.indexOf(col.name); - if(i >= 0) { - t.currentChange.addedColumnNames.remove(i); - t.currentChange.addedColumnDefinitions.remove(i); - } - } + t.removeCol(col); Data.save(); updateColumnsList(listColumns, dbName, tableName); diff --git a/core/src/main/java/com/osiris/jsqlgen/generator/GenCreateMethods.java b/core/src/main/java/com/osiris/jsqlgen/generator/GenCreateMethods.java index 475ebc6..458b65c 100644 --- a/core/src/main/java/com/osiris/jsqlgen/generator/GenCreateMethods.java +++ b/core/src/main/java/com/osiris/jsqlgen/generator/GenCreateMethods.java @@ -3,6 +3,8 @@ import com.osiris.jsqlgen.model.Column; import com.osiris.jsqlgen.model.Table; +import static com.osiris.jsqlgen.utils.UString.containsIgnoreCase; + public class GenCreateMethods { public static String s(Table t, String tNameQuoted, JavaCodeGenerator.Constructor constructor, JavaCodeGenerator.Constructor minimalConstructor, boolean hasMoreFields) { StringBuilder sb = new StringBuilder(); diff --git a/core/src/main/java/com/osiris/jsqlgen/generator/GenDatabaseFile.java b/core/src/main/java/com/osiris/jsqlgen/generator/GenDatabaseFile.java index e5212bf..ac10d2e 100644 --- a/core/src/main/java/com/osiris/jsqlgen/generator/GenDatabaseFile.java +++ b/core/src/main/java/com/osiris/jsqlgen/generator/GenDatabaseFile.java @@ -7,6 +7,8 @@ import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.concurrent.CopyOnWriteArrayList; public class GenDatabaseFile { @@ -14,15 +16,12 @@ public static void s(Database db, File databaseFile, String rawUrl, String url, databaseFile.getParentFile().mkdirs(); databaseFile.createNewFile(); - StringBuilder s = new StringBuilder((!db.getJavaProjectDirs().isEmpty() ? "package com.osiris.jsqlgen." + db.name + ";\n" : "") + - "import java.sql.*;\n" + - "import java.util.*;\n" + - "import java.io.File;\n" + - (db.isWithMariadb4j ? "" + - "import ch.vorburger.exec.ManagedProcessException;\n" + - "import ch.vorburger.mariadb4j.DB;\n" + - "import ch.vorburger.mariadb4j.DBConfigurationBuilder;\n" : "")+ - "\n"+ + LinkedHashSet imports = new LinkedHashSet<>(); + imports.add("import java.sql.*;"); + imports.add("import java.util.*;"); + imports.add("import java.io.File;"); + + StringBuilder s = new StringBuilder( "/*\n" + "Auto-generated class that is used by all table classes to create connections.
\n" + "It holds the database credentials (set by you at first run of jSQL-Gen).
\n" + @@ -330,9 +329,27 @@ public static void initIntegratedMariaDB() { """); } + // Add other dependencies + s.append(GenDefBlobClass.s(imports)); + s.append("}\n"); - Files.writeString(databaseFile.toPath(), s.toString()); + + String sNoImports = s.toString(); + + String finalS = (!db.getJavaProjectDirs().isEmpty() ? "package com.osiris.jsqlgen." + db.name + ";\n" : "") + + (db.isWithMariadb4j ? "" + + "import ch.vorburger.exec.ManagedProcessException;\n" + + "import ch.vorburger.mariadb4j.DB;\n" + + "import ch.vorburger.mariadb4j.DBConfigurationBuilder;\n" : "")+ + "\n"; + for (String anImport : imports) { + finalS += anImport+"\n"; + } + finalS += sNoImports; + + + Files.writeString(databaseFile.toPath(), finalS.toString()); } } diff --git a/core/src/main/java/com/osiris/jsqlgen/generator/GenDefBlobClass.java b/core/src/main/java/com/osiris/jsqlgen/generator/GenDefBlobClass.java index 64d1db0..2137986 100644 --- a/core/src/main/java/com/osiris/jsqlgen/generator/GenDefBlobClass.java +++ b/core/src/main/java/com/osiris/jsqlgen/generator/GenDefBlobClass.java @@ -10,67 +10,69 @@ public static String s(LinkedHashSet imports) { imports.add("import java.sql.Blob;"); imports.add("import java.sql.SQLException;"); - return "public static class DefaultBlob implements Blob{\n" + - " private byte[] data;\n" + - "\n" + - " // Constructor that accepts a byte array\n" + - " public DefaultBlob(byte[] data) {\n" + - " this.data = data;\n" + - " }\n" + - " @Override\n" + - " public long length() throws SQLException {\n" + - " return data.length;\n" + - " }\n" + - "\n" + - " @Override\n" + - " public byte[] getBytes(long pos, int length) throws SQLException {\n" + - " return data;\n" + - " }\n" + - "\n" + - " @Override\n" + - " public InputStream getBinaryStream() throws SQLException {\n" + - " return new ByteArrayInputStream(data);\n" + - " }\n" + - "\n" + - " @Override\n" + - " public long position(byte[] pattern, long start) throws SQLException {\n" + - " return 0;\n" + - " }\n" + - "\n" + - " @Override\n" + - " public long position(Blob pattern, long start) throws SQLException {\n" + - " return 0;\n" + - " }\n" + - "\n" + - " @Override\n" + - " public int setBytes(long pos, byte[] bytes) throws SQLException {\n" + - " return 0;\n" + - " }\n" + - "\n" + - " @Override\n" + - " public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException {\n" + - " return 0;\n" + - " }\n" + - "\n" + - " @Override\n" + - " public OutputStream setBinaryStream(long pos) throws SQLException {\n" + - " return null;\n" + - " }\n" + - "\n" + - " @Override\n" + - " public void truncate(long len) throws SQLException {\n" + - "\n" + - " }\n" + - "\n" + - " @Override\n" + - " public void free() throws SQLException {\n" + - "\n" + - " }\n" + - "\n" + - " @Override\n" + - " public InputStream getBinaryStream(long pos, long length) throws SQLException {\n" + - " return new ByteArrayInputStream(data);\n" + - " }\n" + - "}\n"; + return """ + public static class DefaultBlob implements Blob{ + private byte[] data; + + // Constructor that accepts a byte array + public DefaultBlob(byte[] data) { + this.data = data; + } + @Override + public long length() throws SQLException { + return data.length; + } + + @Override + public byte[] getBytes(long pos, int length) throws SQLException { + return data; + } + + @Override + public InputStream getBinaryStream() throws SQLException { + return new ByteArrayInputStream(data); + } + + @Override + public long position(byte[] pattern, long start) throws SQLException { + return 0; + } + + @Override + public long position(Blob pattern, long start) throws SQLException { + return 0; + } + + @Override + public int setBytes(long pos, byte[] bytes) throws SQLException { + return 0; + } + + @Override + public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { + return 0; + } + + @Override + public OutputStream setBinaryStream(long pos) throws SQLException { + return null; + } + + @Override + public void truncate(long len) throws SQLException { + + } + + @Override + public void free() throws SQLException { + + } + + @Override + public InputStream getBinaryStream(long pos, long length) throws SQLException { + return new ByteArrayInputStream(data); + } + } + """; } } diff --git a/core/src/main/java/com/osiris/jsqlgen/generator/JavaCodeGenerator.java b/core/src/main/java/com/osiris/jsqlgen/generator/JavaCodeGenerator.java index 24cb0c8..4a3b717 100644 --- a/core/src/main/java/com/osiris/jsqlgen/generator/JavaCodeGenerator.java +++ b/core/src/main/java/com/osiris/jsqlgen/generator/JavaCodeGenerator.java @@ -11,6 +11,7 @@ import net.sf.jsqlparser.statement.Statements; import net.sf.jsqlparser.statement.alter.Alter; import net.sf.jsqlparser.statement.alter.AlterExpression; +import ru.lanwen.verbalregex.VerbalExpression; import java.io.File; import java.nio.file.Files; @@ -22,6 +23,48 @@ public class JavaCodeGenerator { + public static String[] sqlDefinitionWords = { + // Data Types, not needed since this is checked by another class + "", // Empty string allowed + + // Some values, (numbers, strings will be excluded later) + "TRUE", "FALSE", + + // Constraints + "PRIMARY", "KEY", "FOREIGN", "KEY", "UNIQUE", "NOT", "NULL", "DEFAULT", "CHECK", + "AUTO_INCREMENT", "IDENTITY", "ON", "DELETE", "ON", "UPDATE", "REFERENCES", + + // Modifiers + "UNSIGNED", "ZEROFILL", "CHARACTER", "SET", "COLLATE", + + // Indexing + "INDEX", "FULLTEXT", "SPATIAL", "HASH", "BTREE", + + // Table Options + "ENGINE", "AUTO_INCREMENT", "CHARSET", "ROW_FORMAT", "COMMENT", + + // Miscellaneous + "GENERATED", "ALWAYS", "AS", "STORED", "VIRTUAL", "COLUMN", + + // Dialect-Specific Keywords + // MySQL + "NOW", "CURDATE", "CURTIME", "CURRENT_TIMESTAMP", + + // PostgreSQL + "SERIAL", "BIGSERIAL", "SMALLSERIAL", "JSONB", "UUID", "ARRAY", + "CITEXT", "MONEY", "XML", "TSVECTOR", "TSQUERY", "INTERVAL", + + // Oracle + "NUMBER", "VARCHAR2", "NVARCHAR2", "CLOB", "NCLOB", "BFILE", + "RAW", "LONG RAW", "ROWID", "UROWID", "XMLTYPE", "TIMESTAMP", "WITH", "TIME", "ZONE", + "TIMESTAMP", "WITH", "LOCAL", "TIME", "ZONE", + + // SQL Server + "NVARCHAR", "NTEXT", "UNIQUEIDENTIFIER", "SMALLDATETIME", "DATETIME2", + "DATETIMEOFFSET", "SQL_VARIANT", "GEOMETRY", "GEOGRAPHY", "IMAGE", + "HIERARCHYID", "ROWVERSION" + }; + public static void prepareTables(Database db) throws Exception { for (Table t : db.tables) { for (Column col : t.columns) { @@ -65,19 +108,61 @@ public static void prepareTables(Database db) throws Exception { } } + var validWordsInDefinition = Arrays.asList(sqlDefinitionWords); for (Table t : db.tables) { for (Column col : t.columns) { // CHECK SQL try{ Alter sql = (Alter) CCJSqlParserUtil.parse("ALTER TABLE `table` ADD COLUMN `column` " + col.definition); - for (AlterExpression alterExpression : sql.getAlterExpressions()) { - for (AlterExpression.ColumnDataType columnDataType : alterExpression.getColDataTypeList()) { - for (String columnSpec : columnDataType.getColumnSpecs()) { - // TODO compare each constraint with a list of all supported/valid constraints, since CCJSqlParserUtil does not do that - // or instead launch a MySQL server for example and run the SQL to see if it works - // to make this perfect, we would need to run other other servers like PostgreSQL too, if the user wants to use that instead - } - } + // TODO compare each constraint with a list of all supported/valid constraints, since CCJSqlParserUtil does not do that + // or instead launch a MySQL server for example and run the SQL to see if it works + // to make this perfect, we would need to run other other servers like PostgreSQL too, if the user wants to use that instead + VerbalExpression parenthesesRegex = VerbalExpression.regex() + .find("(").anything().find(")") + .build(); + + VerbalExpression doubleQuotesRegex = VerbalExpression.regex() + .find("\"").anything().find("\"") + .build(); + + VerbalExpression singleQuotesRegex = VerbalExpression.regex() + .find("'").anything().find("'") + .build(); + + VerbalExpression backticksRegex = VerbalExpression.regex() + .find("`").anything().find("`") + .build(); + + VerbalExpression numbersRegex = VerbalExpression.regex() + .maybe("-") // Optional negative sign + .digit() // Matches digits + .zeroOrMore() // Matches any number of digits (integer part) + .maybe(".") // Matches the decimal point + .digit().zeroOrMore() // Matches the decimal part if exists + .build(); + + // Test string + String input = "Example text (remove this) and \"this too\" or `this one`, even 'this', plus -123.45 and 567 or -89 numbers."; + + // Test string + //String example = "Example text (remove this) and \"this too\" or `this one`, even 'this', plus 123 numbers."; + + // Remove data types from def since this was already checked before + // This also has the side-effect that only one data type is allowed in a definition + String def = col.definition; + for (String s : col.type.inSQL) { + def = def.replaceAll(s, ""); + } + + // Apply all regex replacements + def = def.replaceAll(parenthesesRegex.toString(), "") + .replaceAll(doubleQuotesRegex.toString(), "") + .replaceAll(singleQuotesRegex.toString(), "") + .replaceAll(backticksRegex.toString(), "") + .replaceAll(numbersRegex.toString(), ""); + for (String word : def.split(" ")) { + if(!validWordsInDefinition.contains(word.toUpperCase())) + throw new Exception("'"+word+"' is very likely an invalid SQL definition in '"+def+"'! If not create a pull request on GitHub."); } } catch (Throwable e) { throw new RuntimeException("Invalid SQL found in "+db.name+"."+t.name+"."+col.name+": "+e.getMessage(), e); @@ -174,9 +259,6 @@ public static String generateTableFile(File oldGeneratedClass, Table t, Database classContentBuilder.append(generatedEnumClass); } - // Add other dependencies - classContentBuilder.append(GenDefBlobClass.s(importsList)); - // Add listeners classContentBuilder.append("/** Limitation: Not executed in constructor, but only the create methods. */\n" + "public static CopyOnWriteArrayList> onCreate = new CopyOnWriteArrayList>();\n" + @@ -268,6 +350,13 @@ public static String generateTableFile(File oldGeneratedClass, Table t, Database (col.comment != null ? (col.comment + "\n") : "") + "*/\n" + "public " + col.type.inJava + " " + col.name + ";\n"); + classContentBuilder.append("" + + "/**\n" + + "Database field/value: " + col.definition + ".
\n" + + (col.comment != null ? (col.comment + "\n") : "") + "\n"+ + "Convenience builder-like setter with method-chaining.\n"+ + "*/\n" + + "public "+t.name+" "+col.name+"("+col.type.inJava+" "+col.name+"){ this."+ col.name+" = "+col.name+"; return this;}\n"); } // CREATE INIT DEFAULTS METHOD: @@ -287,7 +376,7 @@ public static String generateTableFile(File oldGeneratedClass, Table t, Database // CREATE COUNT METHOD: - classContentBuilder.append("public static int count(){ return count(null, null); }\n" + + classContentBuilder.append("public static int count(){ return count(null, (Object[]) null); }\n" + "\n" + "public static int count(String where, Object... whereValues) " + (t.isNoExceptions ? "" : "throws Exception") + " {\n" + "String sql = \"SELECT COUNT(`id`) AS recordCount FROM " + tNameQuoted + "\" +\n" + @@ -713,7 +802,7 @@ else if (col.type.isText()) { if (col.type == ColumnType.YEAR) fieldsBuilder.append(objName + "." + col.name + "=" + val + "; "); else fieldsBuilder.append(objName + "." + col.name + "=new " + col.type.inJava + "(" + val + "); "); } else if (col.type.isBlob()) { - fieldsBuilder.append(objName + "." + col.name + "=new DefaultBlob(new byte[0]); "); + fieldsBuilder.append(objName + "." + col.name + "=new Database.DefaultBlob(new byte[0]); "); // This is not directly supported by SQL } else if (col.type.isNumber() || col.type.isDecimalNumber()) { fieldsBuilder.append(objName + "." + col.name + "=" + val + "; "); diff --git a/core/src/main/java/com/osiris/jsqlgen/jsqlgen_structure.json b/core/src/main/java/com/osiris/jsqlgen/jsqlgen_structure.json index dc8543f..65a6c95 100644 --- a/core/src/main/java/com/osiris/jsqlgen/jsqlgen_structure.json +++ b/core/src/main/java/com/osiris/jsqlgen/jsqlgen_structure.json @@ -52,7 +52,20 @@ "DATETIME NOT NULL" ] } - ] + ], + "currentChange": { + "oldTableName": "", + "newTableName": "", + "oldColumnNames": [], + "newColumnNames": [], + "newColumnNames_Definitions": [], + "oldColumnDefinitions": [], + "newColumnDefinitions": [], + "newColumnDefinitions_Names": [], + "deletedColumnNames": [], + "addedColumnNames": [], + "addedColumnDefinitions": [] + } }, { "id": 598, @@ -123,7 +136,20 @@ "TEXT DEFAULT \u0027\u0027" ] } - ] + ], + "currentChange": { + "oldTableName": "", + "newTableName": "", + "oldColumnNames": [], + "newColumnNames": [], + "newColumnNames_Definitions": [], + "oldColumnDefinitions": [], + "newColumnDefinitions": [], + "newColumnDefinitions_Names": [], + "deletedColumnNames": [], + "addedColumnNames": [], + "addedColumnDefinitions": [] + } }, { "id": 601, @@ -167,7 +193,20 @@ "TEXT DEFAULT \u0027New Task\u0027" ] } - ] + ], + "currentChange": { + "oldTableName": "", + "newTableName": "", + "oldColumnNames": [], + "newColumnNames": [], + "newColumnNames_Definitions": [], + "oldColumnDefinitions": [], + "newColumnDefinitions": [], + "newColumnDefinitions_Names": [], + "deletedColumnNames": [], + "addedColumnNames": [], + "addedColumnDefinitions": [] + } } ], "javaProjectDir": "C:\\Users\\arman\\jSQL-Gen ;D:\\Coding\\JAVA\\jSQL-Gen\\core ;", diff --git a/core/src/main/java/com/osiris/jsqlgen/model/Table.java b/core/src/main/java/com/osiris/jsqlgen/model/Table.java index b208540..6432089 100644 --- a/core/src/main/java/com/osiris/jsqlgen/model/Table.java +++ b/core/src/main/java/com/osiris/jsqlgen/model/Table.java @@ -23,7 +23,77 @@ public Table addIdColumn(){ Column idColumn = new Column("id"); idColumn.id = Main.idCounter.getAndIncrement(); idColumn.definition = "INT NOT NULL PRIMARY KEY"; - columns.add(idColumn); + addCol(idColumn); + return this; + } + + public Table addCol(Column col){ + columns.add(col); + + // Update current change + if(!currentChange.addedColumnNames.contains(col.name)){ + currentChange.addedColumnNames.add(col.name); + currentChange.addedColumnDefinitions.add(col.definition); + currentChange.deletedColumnNames.remove(col.name); + } + + return this; + } + + public Table updateCol(Column col, String oldName, String newName, String newDefinition, String newComment){ + + col.updateName(newName); + String oldDefinition = col.definition; + col.definition = newDefinition; + col.comment = newComment; + + // Update current change + currentChange.deletedColumnNames.remove(oldName); + if(currentChange.addedColumnNames.contains(oldName)){ + int i = currentChange.addedColumnNames.indexOf(oldName); + currentChange.addedColumnNames.set(i, newName); + currentChange.addedColumnDefinitions.set(i, newDefinition); + } else{ + // Existing column, check for changes + int i = currentChange.newColumnNames.indexOf(oldName); + if(i >= 0){ + currentChange.newColumnNames.remove(i); + currentChange.newColumnNames_Definitions.remove(i); + currentChange.oldColumnNames.remove(i); + } + if(!newName.equals(oldName)) { + currentChange.newColumnNames.add(newName); + currentChange.newColumnNames_Definitions.add(newDefinition); + currentChange.oldColumnNames.add(oldName); + } + + i = currentChange.newColumnDefinitions.indexOf(oldDefinition); + if(i >= 0){ + currentChange.newColumnDefinitions.remove(i); + currentChange.oldColumnDefinitions.remove(i); + currentChange.newColumnDefinitions_Names.remove(i); + } + if(!newDefinition.equals(oldDefinition)) { + currentChange.newColumnDefinitions.add(newDefinition); + currentChange.oldColumnDefinitions.add(oldDefinition); + currentChange.newColumnDefinitions_Names.add(newName); + } + } + return this; + } + + public Table removeCol(Column col){ + columns.remove(col); + + // Update current change + if(!currentChange.deletedColumnNames.contains(col.name)){ + currentChange.deletedColumnNames.add(col.name); + int i = currentChange.addedColumnNames.indexOf(col.name); + if(i >= 0) { + currentChange.addedColumnNames.remove(i); + currentChange.addedColumnDefinitions.remove(i); + } + } return this; } diff --git a/core/src/test/java/com/osiris/jsqlgen/generator/JavaCodeGeneratorTest.java b/core/src/test/java/com/osiris/jsqlgen/generator/JavaCodeGeneratorTest.java index 3e000a0..ff600f6 100644 --- a/core/src/test/java/com/osiris/jsqlgen/generator/JavaCodeGeneratorTest.java +++ b/core/src/test/java/com/osiris/jsqlgen/generator/JavaCodeGeneratorTest.java @@ -1,10 +1,10 @@ package com.osiris.jsqlgen.generator; +import com.osiris.jlib.logger.AL; import com.osiris.jsqlgen.SQLTestServer; import com.osiris.jsqlgen.model.Column; import com.osiris.jsqlgen.model.Database; import com.osiris.jsqlgen.model.Table; -import com.osiris.jsqlgen.testDB.Person; import org.junit.jupiter.api.Test; import java.io.File; @@ -17,10 +17,12 @@ class JavaCodeGeneratorTest { @Test void generate() throws Exception { + AL.start(); + Database db = new Database(); db.name = "testDB"; File dir = new File(System.getProperty("user.dir")+"/src/test/java/com/osiris/jsqlgen/testDB"); - db.javaProjectDir = dir; + db.setJavaProjectDirs(dir+";"); Table t = new Table().addIdColumn(); db.tables.add(t); @@ -44,16 +46,12 @@ void generate() throws Exception { t2.name = "PersonOrder"; t2.columns.add(new Column("personId").definition("INT")); t2.columns.add(new Column("name").definition("TEXT DEFAULT ''")); + t2.columns.add(new Column("time").definition("INT DEFAULT 10000")); t2.isCache = true; t2.isDebug = true; t2.isVaadinFlowUI = true; t2.isNoExceptions = true; - GenDatabaseFile.s(db, new File(dir+"/Database.java"), - "getRawDbUrlFrom(url)", - "\"jdbc:mysql://localhost:3307/testDB\"", - "\"testDB\"", "\"root\"", "\"\""); - // Expect error Exception e = null; @@ -70,14 +68,19 @@ void generate() throws Exception { // No error JavaCodeGenerator.prepareTables(db); + GenDatabaseFile.s(db, new File(dir+"/Database.java"), + "getRawDbUrlFrom(url)", + "\"jdbc:mysql://localhost:3307/testDB\"", + "\"testDB\"", "\"root\"", "\"\""); + File javaFile = new File(dir + "/" + t.name + ".java"); javaFile.createNewFile(); - Files.writeString(javaFile.toPath(), (db.javaProjectDir != null ? "package com.osiris.jsqlgen." + db.name + ";\n" : "") + + Files.writeString(javaFile.toPath(), (!db.getJavaProjectDirs().isEmpty() ? "package com.osiris.jsqlgen." + db.name + ";\n" : "") + JavaCodeGenerator.generateTableFile(javaFile, db.tables.get(0), db)); File javaFile2 = new File(dir + "/" + t2.name + ".java"); javaFile2.createNewFile(); - Files.writeString(javaFile2.toPath(), (db.javaProjectDir != null ? "package com.osiris.jsqlgen." + db.name + ";\n" : "") + + Files.writeString(javaFile2.toPath(), (!db.getJavaProjectDirs().isEmpty() ? "package com.osiris.jsqlgen." + db.name + ";\n" : "") + JavaCodeGenerator.generateTableFile(javaFile2, db.tables.get(1), db)); System.err.println(""" @@ -85,10 +88,9 @@ void generate() throws Exception { !>>>> has to be recompiled at some pointhus the below tests probably run with the last compiled classes, !>>>> not the current ones. So if encountering errors re-run this."""); - SQLTestServer testDB = SQLTestServer.buildAndRun("testDB", 3307); - - Person.removeAll(); - Person john = Person.createAndAdd("John", 32); - assertFalse(Person.whereName().is(john.name).get().isEmpty()); + //SQLTestServer testDB = SQLTestServer.buildAndRun("testDB", 3307); + //Person.removeAll(); + //Person john = Person.createAndAdd("John", 32); + //assertFalse(Person.whereName().is(john.name).get().isEmpty()); } -} \ No newline at end of file +} diff --git a/core/src/test/java/com/osiris/jsqlgen/testDB/Database.java b/core/src/test/java/com/osiris/jsqlgen/testDB/Database.java index 14125c6..5b88e0f 100644 --- a/core/src/test/java/com/osiris/jsqlgen/testDB/Database.java +++ b/core/src/test/java/com/osiris/jsqlgen/testDB/Database.java @@ -1,7 +1,13 @@ package com.osiris.jsqlgen.testDB; + import java.sql.*; import java.util.*; - +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.ByteArrayInputStream; +import java.sql.Blob; +import java.sql.SQLException; /* Auto-generated class that is used by all table classes to create connections.
It holds the database credentials (set by you at first run of jSQL-Gen).
@@ -24,7 +30,7 @@ public class Database{ * Use synchronized on this before doing changes to it. */ public static final List availableConnections = new ArrayList<>(); -public static final TableMetaData[] tables = new TableMetaData[]{new TableMetaData(1, 0, 0, "Person", new String[]{"id", "name", "age", "flair", "lastName", "parentAge", "myblob", "timestamp"}, new String[]{"INT NOT NULL PRIMARY KEY", "TEXT NOT NULL", "INT NOT NULL", "ENUM('COOL', 'CHILL', 'FLY') DEFAULT 'COOL'", "TEXT DEFAULT ''", "INT DEFAULT 10", "BLOB DEFAULT ''", "TIMESTAMP DEFAULT NOW()"}){public Class getTableClass(){return Person.class;}public List get(){List l = new ArrayList<>(); for(Person obj : Person.get()) l.add(obj); return l;}public Database.Row get(int i){return Person.get(i);}public void update(Database.Row obj){Person.update((Person)obj);}public void add(Database.Row obj){Person.add((Person)obj);}public void remove(Database.Row obj){Person.remove((Person)obj);}}, new TableMetaData(2, 0, 0, "PersonOrder", new String[]{"id", "personId", "name"}, new String[]{"INT NOT NULL PRIMARY KEY", "INT", "TEXT DEFAULT ''"}){public Class getTableClass(){return PersonOrder.class;}public List get(){List l = new ArrayList<>(); for(PersonOrder obj : PersonOrder.get()) l.add(obj); return l;}public Database.Row get(int i){return PersonOrder.get(i);}public void update(Database.Row obj){PersonOrder.update((PersonOrder)obj);}public void add(Database.Row obj){PersonOrder.add((PersonOrder)obj);}public void remove(Database.Row obj){PersonOrder.remove((PersonOrder)obj);}}}; +public static final TableMetaData[] tables = new TableMetaData[]{new TableMetaData(1, 0, 0, "Person", new String[]{"id", "name", "age", "flair", "lastName", "parentAge", "myblob", "timestamp"}, new String[]{"INT NOT NULL PRIMARY KEY", "TEXT NOT NULL", "INT NOT NULL", "ENUM('COOL', 'CHILL', 'FLY') DEFAULT 'COOL'", "TEXT DEFAULT ''", "INT DEFAULT 10", "BLOB DEFAULT ''", "TIMESTAMP DEFAULT NOW()"}){public Class getTableClass(){return Person.class;}public List get(){List l = new ArrayList<>(); for(Person obj : Person.get()) l.add(obj); return l;}public Database.Row get(int i){return Person.get(i);}public void update(Database.Row obj){Person.update((Person)obj);}public void add(Database.Row obj){Person.add((Person)obj);}public void remove(Database.Row obj){Person.remove((Person)obj);}}, new TableMetaData(2, 0, 0, "PersonOrder", new String[]{"id", "personId", "name", "time"}, new String[]{"INT NOT NULL PRIMARY KEY", "INT NOT NULL", "TEXT DEFAULT ''", "INT DEFAULT 10000"}){public Class getTableClass(){return PersonOrder.class;}public List get(){List l = new ArrayList<>(); for(PersonOrder obj : PersonOrder.get()) l.add(obj); return l;}public Database.Row get(int i){return PersonOrder.get(i);}public void update(Database.Row obj){PersonOrder.update((PersonOrder)obj);}public void add(Database.Row obj){PersonOrder.add((PersonOrder)obj);}public void remove(Database.Row obj){PersonOrder.remove((PersonOrder)obj);}}}; static{create();} // Create database if not exists @@ -187,10 +193,12 @@ public static void updateTableMetaData(TableMetaData t) { System.exit(1); } } - public interface Row{ - T update(); - T add(); - T remove(); + public interface Row{ + int getId(); + void setId(int id); + void update(); + void add(); + void remove(); String toPrintString(); String toMinimalPrintString(); } @@ -227,4 +235,66 @@ public static synchronized void printTable(TableMetaData table) { for (Database.Row row : rows) { System.err.println(row.toPrintString()); } -}} +}public static class DefaultBlob implements Blob{ + private byte[] data; + + // Constructor that accepts a byte array + public DefaultBlob(byte[] data) { + this.data = data; + } + @Override + public long length() throws SQLException { + return data.length; + } + + @Override + public byte[] getBytes(long pos, int length) throws SQLException { + return data; + } + + @Override + public InputStream getBinaryStream() throws SQLException { + return new ByteArrayInputStream(data); + } + + @Override + public long position(byte[] pattern, long start) throws SQLException { + return 0; + } + + @Override + public long position(Blob pattern, long start) throws SQLException { + return 0; + } + + @Override + public int setBytes(long pos, byte[] bytes) throws SQLException { + return 0; + } + + @Override + public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { + return 0; + } + + @Override + public OutputStream setBinaryStream(long pos) throws SQLException { + return null; + } + + @Override + public void truncate(long len) throws SQLException { + + } + + @Override + public void free() throws SQLException { + + } + + @Override + public InputStream getBinaryStream(long pos, long length) throws SQLException { + return new ByteArrayInputStream(data); + } +} +} diff --git a/core/src/test/java/com/osiris/jsqlgen/testDB/Person.java b/core/src/test/java/com/osiris/jsqlgen/testDB/Person.java index e9776a8..a198940 100644 --- a/core/src/test/java/com/osiris/jsqlgen/testDB/Person.java +++ b/core/src/test/java/com/osiris/jsqlgen/testDB/Person.java @@ -10,38 +10,45 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; import java.util.Arrays; -import java.time.*; -import java.util.Date; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.sql.*; -import com.vaadin.flow.component.ClickEvent; -import com.vaadin.flow.component.datetimepicker.DateTimePicker; -import com.vaadin.flow.component.select.Select; +import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.formlayout.FormLayout; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.select.Select; import com.vaadin.flow.component.textfield.NumberField; import com.vaadin.flow.component.textfield.TextField; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.ClickEvent; import com.vaadin.flow.component.datepicker.DatePicker; +import com.vaadin.flow.component.datetimepicker.DateTimePicker; import java.time.LocalDateTime; import java.time.OffsetDateTime; -import java.sql.SQLException; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.component.button.ButtonVariant; +import com.vaadin.flow.component.dialog.Dialog; +import java.util.function.Function; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.html.Span; /** +Table Person with id 1 and 0 changes/version.
+Structure (8 fields/columns):
+- int id = INT NOT NULL PRIMARY KEY
+- String name = TEXT NOT NULL
+- int age = INT NOT NULL
+- Flair flair = ENUM('COOL', 'CHILL', 'FLY') DEFAULT 'COOL'
+- String lastName = TEXT DEFAULT ''
+- int parentAge = INT DEFAULT 10
+- Blob myblob = BLOB DEFAULT ''
+- Timestamp timestamp = TIMESTAMP DEFAULT NOW()
+ Generated class by jSQL-Gen that contains static methods for fetching/updating data from the `person` table. A single object/instance of this class represents a single row in the table and data can be accessed via its public fields.

-You can add your own code to the top of this class.
+You can add your own code to the bottom of this class.
Do not modify the rest of this class since those changes will be removed at regeneration. If modifications are really needed create a pull request directly to jSQL-Gen instead.

@@ -55,90 +62,16 @@ - VAADIN FLOW is enabled, which means that an additional obj.toComp() method
will be generated that returns a Vaadin Flow UI Form representation that allows creating/updating/deleting a row/object.

-Structure (8 fields/columns):
-- int id = INT NOT NULL PRIMARY KEY
-- String name = TEXT NOT NULL
-- int age = INT NOT NULL
-- Flair flair = ENUM('COOL', 'CHILL', 'FLY') DEFAULT 'COOL'
-- String lastName = TEXT DEFAULT ''
-- int parentAge = INT DEFAULT 10
-- Blob myblob = BLOB DEFAULT ''
-- Timestamp timestamp = TIMESTAMP DEFAULT NOW()
*/ -public class Person implements Database.Row{ -// The code below will not be removed when re-generating this class. -// Additional code start -> -private Person(){} -// Additional code end <- +public class Person implements Database.Row{ public enum Flair {COOL, CHILL, FLY,} -class DefaultBlob implements Blob{ - private byte[] data; - - // Constructor that accepts a byte array - public DefaultBlob(byte[] data) { - this.data = data; - } - @Override - public long length() throws SQLException { - return data.length; - } - - @Override - public byte[] getBytes(long pos, int length) throws SQLException { - return data; - } - - @Override - public InputStream getBinaryStream() throws SQLException { - return new ByteArrayInputStream(data); - } - - @Override - public long position(byte[] pattern, long start) throws SQLException { - return 0; - } - - @Override - public long position(Blob pattern, long start) throws SQLException { - return 0; - } - - @Override - public int setBytes(long pos, byte[] bytes) throws SQLException { - return 0; - } - - @Override - public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { - return 0; - } - - @Override - public OutputStream setBinaryStream(long pos) throws SQLException { - return null; - } - - @Override - public void truncate(long len) throws SQLException { - - } - - @Override - public void free() throws SQLException { - - } - - @Override - public InputStream getBinaryStream(long pos, long length) throws SQLException { - return new ByteArrayInputStream(data); - } -} /** Limitation: Not executed in constructor, but only the create methods. */ public static CopyOnWriteArrayList> onCreate = new CopyOnWriteArrayList>(); public static CopyOnWriteArrayList> onAdd = new CopyOnWriteArrayList>(); public static CopyOnWriteArrayList> onUpdate = new CopyOnWriteArrayList>(); -/** Limitation: Only executed in remove(obj) method. */ public static CopyOnWriteArrayList> onRemove = new CopyOnWriteArrayList>(); + +private static boolean isEqual(Person obj1, Person obj2){ return obj1.equals(obj2) || obj1.id == obj2.id; } /** * Only works correctly if the package name is com.osiris.jsqlgen. */ @@ -154,6 +87,8 @@ private static String minimalStackString(){ return s +"..."+ stack[1].toString(); //stack[0] == current method, gets ignored } public static java.util.concurrent.atomic.AtomicInteger idCounter = new java.util.concurrent.atomic.AtomicInteger(0); +public int getId(){return id;} +public void setId(int id){this.id = id;} public static volatile boolean hasChanges = false; static { try{ @@ -165,20 +100,6 @@ private static String minimalStackString(){ if(i == 0){ if(t.steps < 1){s.executeUpdate("CREATE TABLE IF NOT EXISTS `person` (`id` INT NOT NULL PRIMARY KEY)"); t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 2){try{s.executeUpdate("ALTER TABLE `person` ADD COLUMN `name` TEXT NOT NULL");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 3){try{s.executeUpdate("ALTER TABLE `person` ADD COLUMN `age` INT NOT NULL");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 4){try{s.executeUpdate("ALTER TABLE `person` ADD COLUMN `flair` ENUM('COOL', 'CHILL', 'FLY') DEFAULT 'COOL'");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 5){try{s.executeUpdate("ALTER TABLE `person` ADD COLUMN `lastName` TEXT DEFAULT ''");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 6){try{s.executeUpdate("ALTER TABLE `person` ADD COLUMN `parentAge` INT DEFAULT 10");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 7){try{s.executeUpdate("ALTER TABLE `person` ADD COLUMN `myblob` BLOB DEFAULT ''");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 8){try{s.executeUpdate("ALTER TABLE `person` ADD COLUMN `timestamp` TIMESTAMP DEFAULT NOW()");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} t.steps = 0; t.version++; Database.updateTableMetaData(t); } @@ -305,7 +226,13 @@ public Person (int id, String name, int age, Flair flair, String lastName, int p Initialises the DEFAULT fields with the provided default values mentioned in the columns definition. */ protected Person initDefaultFields() { -this.flair=Flair.COOL; this.lastName=""; this.parentAge=10; this.myblob=new DefaultBlob(new byte[0]); this.timestamp=new Timestamp(System.currentTimeMillis()); return this; +this.flair=Flair.COOL; this.lastName=""; this.parentAge=10; this.myblob=new Database.DefaultBlob(new byte[0]); this.timestamp=new Timestamp(System.currentTimeMillis()); return this; +} + +public static class Optionals{ +public Optionals(){this.flair=Flair.COOL; this.lastName=""; this.parentAge=10; this.myblob=new Database.DefaultBlob(new byte[0]); this.timestamp=new Timestamp(System.currentTimeMillis()); } +public Flair flair; public String lastName; public int parentAge; public Blob myblob; public Timestamp timestamp; +public Optionals flair(Flair flair){ this.flair = flair; return this;} public Optionals lastName(String lastName){ this.lastName = lastName; return this;} public Optionals parentAge(int parentAge){ this.parentAge = parentAge; return this;} public Optionals myblob(Blob myblob){ this.myblob = myblob; return this;} public Optionals timestamp(Timestamp timestamp){ this.timestamp = timestamp; return this;} } /** @@ -314,13 +241,26 @@ Increments the id (thread-safe) and sets it for this object (basically reserves Note that the parameters of this method represent "NOT NULL" fields in the table and thus should not be null. Also note that this method will NOT add the object to the table. */ -public static Person create( String name, int age) { +public static Person create(String name, int age) { int id = idCounter.getAndIncrement(); Person obj = new Person(id, name, age); onCreate.forEach(code -> code.accept(obj)); return obj; } +/** +Creates and returns an object that can be added to this table. +Increments the id (thread-safe) and sets it for this object (basically reserves a space in the database). +Note that this method will NOT add the object to the table. +*/ +public static Person create(String name, int age, Flair flair, String lastName, int parentAge, Blob myblob, Timestamp timestamp) { +int id = idCounter.getAndIncrement(); +Person obj = new Person(); +obj.id=id; obj.name=name; obj.age=age; obj.flair=flair; obj.lastName=lastName; obj.parentAge=parentAge; obj.myblob=myblob; obj.timestamp=timestamp; +onCreate.forEach(code -> code.accept(obj)); +return obj; +} + /** Creates and returns an in-memory object with -1 as id, that can be added to this table AFTER you manually did obj.id = idCounter.getAndIncrement(). @@ -328,7 +268,7 @@ public static Person create( String name, int age) { Note that the parameters of this method represent "NOT NULL" fields in the table and thus should not be null. Also note that this method will NOT add the object to the table. */ -public static Person createInMem( String name, int age) { +public static Person createInMem(String name, int age) { int id = -1; Person obj = new Person(id, name, age); onCreate.forEach(code -> code.accept(obj)); @@ -336,12 +276,14 @@ public static Person createInMem( String name, int age) { } /** -Creates and returns an object that can be added to this table. -Increments the id (thread-safe) and sets it for this object (basically reserves a space in the database). -Note that this method will NOT add the object to the table. +Creates and returns an in-memory object with -1 as id, that can be added to this table +AFTER you manually did obj.id = idCounter.getAndIncrement(). +This is useful for objects that may never be added to the table. +Note that the parameters of this method represent "NOT NULL" fields in the table and thus should not be null. +Also note that this method will NOT add the object to the table. */ -public static Person create( String name, int age, Flair flair, String lastName, int parentAge, Blob myblob, Timestamp timestamp) { -int id = idCounter.getAndIncrement(); +public static Person createInMem(String name, int age, Flair flair, String lastName, int parentAge, Blob myblob, Timestamp timestamp) { +int id = -1; Person obj = new Person(); obj.id=id; obj.name=name; obj.age=age; obj.flair=flair; obj.lastName=lastName; obj.parentAge=parentAge; obj.myblob=myblob; obj.timestamp=timestamp; onCreate.forEach(code -> code.accept(obj)); @@ -352,7 +294,7 @@ public static Person create( String name, int age, Flair flair, String lastName, Convenience method for creating and directly adding a new object to the table. Note that the parameters of this method represent "NOT NULL" fields in the table and thus should not be null. */ -public static Person createAndAdd( String name, int age) { +public static Person createAndAdd(String name, int age) { int id = idCounter.getAndIncrement(); Person obj = new Person(id, name, age); onCreate.forEach(code -> code.accept(obj)); @@ -363,7 +305,7 @@ public static Person createAndAdd( String name, int age) { /** Convenience method for creating and directly adding a new object to the table. */ -public static Person createAndAdd( String name, int age, Flair flair, String lastName, int parentAge, Blob myblob, Timestamp timestamp) { +public static Person createAndAdd(String name, int age, Flair flair, String lastName, int parentAge, Blob myblob, Timestamp timestamp) { int id = idCounter.getAndIncrement(); Person obj = new Person(); obj.id=id; obj.name=name; obj.age=age; obj.flair=flair; obj.lastName=lastName; obj.parentAge=parentAge; obj.myblob=myblob; obj.timestamp=timestamp; @@ -372,6 +314,15 @@ public static Person createAndAdd( String name, int age, Flair flair, String las return obj; } +public static Person createAndAdd(String name, int age, Optionals optionals) { +int id = idCounter.getAndIncrement(); +Person obj = new Person(id, name, age); +obj.flair = optionals.flair; obj.lastName = optionals.lastName; obj.parentAge = optionals.parentAge; obj.myblob = optionals.myblob; obj.timestamp = optionals.timestamp; +onCreate.forEach(code -> code.accept(obj)); +add(obj); +return obj; +} + /** @return a list containing all objects in this table. */ @@ -519,7 +470,7 @@ public static Thread getLazySync(Consumer> onResultReceived, Consum return thread; } -public static int count(){ return count(null, null); } +public static int count(){ return count(null, (Object[]) null); } public static int count(String where, Object... whereValues) { String sql = "SELECT COUNT(`id`) AS recordCount FROM `person`" + @@ -603,20 +554,20 @@ public static void add(Person obj) { } /** -Unsets its references (sets them to -1) and deletes the provided object from the database. +Unsets its references (sets them to -1/'') and deletes the provided object from the database. */ public static void remove(Person obj) { -remove(obj, true, false); +remove(obj, true, Database.isRemoveRefs); } /** * Deletes the provided object from the database. - * @param unsetRefs If true, sets ids in other tables to -1. + * @param unsetRefs If true, sets ids in other tables to -1/''. * @param removeRefs !!! EXTREME CAUTION REQUIRED, MAJOR DATA-LOSS POSSIBLE !!! If true removes the complete obj/row(s) in all tables that reference/contain this id. * This is recursive. It's highly recommended to call removeRefs() before instead, which allows to explicitly exclude some tables. */ public static void remove(Person obj, boolean unsetRefs, boolean removeRefs) { -if(unsetRefs) unsetRefs(obj, PersonOrder.class); -if(removeRefs) removeRefs(obj, PersonOrder.class); +if(unsetRefs) unsetRefs(obj, PersonOrder.class, true); +if(removeRefs) removeRefs(obj, PersonOrder.class, true); remove("WHERE id = "+obj.id); onRemove.forEach(code -> code.accept(obj)); } @@ -650,57 +601,46 @@ public static void remove(String where, Object... whereValues) { } public static void removeAll() { -String sql = "DELETE FROM `person`"; -long msGetCon = System.currentTimeMillis(); long msJDBC = 0; -Connection con = Database.getCon(); -msGetCon = System.currentTimeMillis() - msGetCon; -msJDBC = System.currentTimeMillis(); - try (PreparedStatement ps = con.prepareStatement(sql)) { - ps.executeUpdate(); -msJDBC = System.currentTimeMillis() - msJDBC; -}catch(Exception e){throw new RuntimeException(e);} - finally{System.err.println(sql+" /* //// msGetCon="+msGetCon+" msJDBC="+msJDBC+" con="+con+" minimalStack="+minimalStackString()+" */"); -Database.freeCon(con); -clearCache(); -} +getLazySync(objs -> {for(Person obj : objs) {obj.remove();}}); } /** - * @see #remove(Person, boolean, boolean) - */ -public static void unsetRefs(Person obj, Class personId_in_PersonOrder) { -if (personId_in_PersonOrder != null) {PersonOrder.getLazySync(results -> { - for(PersonOrder obj1 : results) {obj1.personId = -1; obj1.update();}; + * @see #remove(Person, boolean, boolean) + */ +public static void unsetRefs(Person obj, Class personId_in_PersonOrder, boolean remove_personId_in_PersonOrder) { +if (remove_personId_in_PersonOrder) {PersonOrder.getLazySync(results -> { + for(PersonOrder refObj : results) {refObj.personId = -1; refObj.update();}; }, totalCount -> {}, 100, PersonOrder.wherePersonId().is(obj.id));} + } /** !!! EXTREME CAUTION REQUIRED, MAJOR DATA-LOSS POSSIBLE !!! * @see #remove(Person, boolean, boolean) */ -public static void removeRefs(Person obj, Class personId_in_PersonOrder) { -if (personId_in_PersonOrder != null) {PersonOrder.getLazySync(results -> { - for(PersonOrder obj1 : results) {obj1.remove(false, true);}; +public static void removeRefs(Person obj, Class personId_in_PersonOrder, boolean remove_personId_in_PersonOrder) { +// Take care of direct refs and indirect refs +if (remove_personId_in_PersonOrder) {PersonOrder.getLazySync(results -> { + for(PersonOrder refObj : results) {PersonOrder.removeRefs(refObj);refObj.remove();}; }, totalCount -> {}, 100, PersonOrder.wherePersonId().is(obj.id));} + + + } public Person clone(){ return new Person(this.id,this.name,this.age,this.flair,this.lastName,this.parentAge,this.myblob,this.timestamp); } -public Person add(){ +public void add(){ Person.add(this); -return this; } -public Person update(){ +public void update(){ Person.update(this); -return this; } -public Person remove(){ +public void remove(){ Person.remove(this); -return this; } -public Person remove(boolean unsetRefs, boolean removeRefs){ +public void remove(boolean unsetRefs, boolean removeRefs){ Person.remove(this, unsetRefs, removeRefs); -return this; } public String toPrintString(){ return ""+"id="+this.id+" "+"name="+this.name+" "+"age="+this.age+" "+"flair="+this.flair+" "+"lastName="+this.lastName+" "+"parentAge="+this.parentAge+" "+"myblob="+this.myblob+" "+"timestamp="+this.timestamp+" "; @@ -708,8 +648,6 @@ public String toPrintString(){ public String toMinimalPrintString(){ return ""+this.id+"; "+this.name+"; "+this.age+"; "+this.flair+"; "+this.lastName+"; "+this.parentAge+"; "+""; } - public static class Comp extends VerticalLayout{ - public static class BooleanSelect extends Select { public Span yes = genYesLabel(); public Span no = genNoLabel(); @@ -748,7 +686,103 @@ public Span genNoLabel(){ txt.getStyle().set("background-color", "var(--lumo-error-color)"); return txt; } +}// Executed for all objects +public static Consumer onCreateV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {Person.onCreate.remove(code2);}); Person.onCreate.add(code2); return code2; +} +// Executed for all objects +public static Consumer onAddV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {Person.onAdd.remove(code2);}); Person.onAdd.add(code2); return code2; +} +// Executed for all objects +public static Consumer onUpdateV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {Person.onUpdate.remove(code2);}); Person.onUpdate.add(code2); return code2; +} +// Executed for all objects +public static Consumer onRemoveV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {Person.onRemove.remove(code2);}); Person.onRemove.add(code2); return code2; +} + + +// Executed only for this object +public Consumer onCreateThisV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {if(!isEqual(this, obj)) return; ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {Person.onCreate.remove(code2);}); Person.onCreate.add(code2); return code2; +} +// Executed only for this object +public Consumer onAddThisV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {if(!isEqual(this, obj)) return; ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {Person.onAdd.remove(code2);}); Person.onAdd.add(code2); return code2; +} +// Executed only for this object +public Consumer onUpdateThisV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {if(!isEqual(this, obj)) return; ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {Person.onUpdate.remove(code2);}); Person.onUpdate.add(code2); return code2; +} +// Executed only for this object +public Consumer onRemoveThisV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {if(!isEqual(this, obj)) return; ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {Person.onRemove.remove(code2);}); Person.onRemove.add(code2); return code2; } + + +public static ComboBox newTableComboBox(){ + ComboBox comboBox = new ComboBox("Person"); + {comboBox.setItems(Person.get()); + comboBox.setRenderer(new ComponentRenderer<>(obj -> { + Div div = new Div(); + div.setText(obj.toMinimalPrintString()); + return div;})); + comboBox.setItemLabelGenerator(obj -> { + return obj.toMinimalPrintString(); + }); + } +return comboBox; +} + +public static NumberField newNfId(){ + NumberField nfId = new NumberField("Id"); +return nfId; +} + +public static TextField newTfName(){ + TextField tfName = new TextField("Name"); +return tfName; +} + +public static NumberField newNfAge(){ + NumberField nfAge = new NumberField("Age"); +return nfAge; +} + +public static Select newSelFlair(){ + Select selFlair = new Select(); + {selFlair.setLabel("Flair"); selFlair.setItems(Person.Flair.values()); } +return selFlair; +} + +public static TextField newTfLastName(){ + TextField tfLastName = new TextField("LastName"); +return tfLastName; +} + +public static NumberField newNfParentAge(){ + NumberField nfParentAge = new NumberField("ParentAge"); +return nfParentAge; +} + +public static DateTimePicker newDfTimestamp(){ + DateTimePicker dfTimestamp = new DateTimePicker("Timestamp"); +return dfTimestamp; +} + + /** + * Gets executed later if {@link #isOnlyInMemory()}, otherwise provided + * code gets executed directly. + */ public void whenReadyV(Consumer code) { + if(isOnlyInMemory()) onAddThisV(obj -> code.accept(obj)); + else code.accept(this); + } + + public static class Comp extends VerticalLayout{ + + public Person dataPerson; public Person data; // Form and fields @@ -770,7 +804,7 @@ public Span genNoLabel(){ updateData(); data.id = idCounter.getAndIncrement(); Person.add(data); - e.unregisterListener(); // Make sure it gets only added once to the database + e.unregisterListener(); // Make sure it gets only executed once updateButtons(); }; public Button btnSave = new Button("Save"); @@ -787,12 +821,13 @@ public Span genNoLabel(){ public Consumer> onBtnDeleteClick = (e) -> { btnDelete.setEnabled(false); Person.remove(data); - e.unregisterListener(); // Make sure it gets only added once to the database + e.unregisterListener(); // Make sure it gets only executed once updateButtons(); }; public Comp(Person data) { this.data = data; + this.dataPerson = this.data; setWidthFull(); setPadding(false); @@ -852,10 +887,13 @@ public void updateButtons(){ hlButtons.add(btnDelete); hlButtons.addAndExpand(btnSave); } + } + public static volatile Function global_fn_toComp = (obj) -> {return new Person.Comp(obj);}; + public volatile Function fn_toComp = (_null) -> {return global_fn_toComp.apply(this);}; public Person.Comp toComp(){ - return new Person.Comp(this); + return fn_toComp.apply(null); } public boolean isOnlyInMemory(){ @@ -921,6 +959,16 @@ public List get() { return Person.get(where+orderBy+limitBuilder.toString(), (T[]) null); } + /** + * Executes the generated SQL statement + * and returns the first object matching the query or null if none. + */ + public Person getFirstOrNull() { + List results = get(); + if(results.isEmpty()) return null; + else return results.get(0); + } + /** * Executes the generated SQL statement * and returns the size of the list of objects matching the query. @@ -1037,6 +1085,51 @@ public WHERE like(T obj) { whereObjects.add(obj); return this; } + /** + * columnName LIKE ?
+ * Example: WHERE CustomerName LIKE 'a%'
+ * Explanation: Finds any values that start with "a"
+ * Note: Your provided obj gets turned to a string and if it already contains '_' or '%' these get escaped with '/' to ensure a correct query.
+ * + * @see https://www.w3schools.com/mysql/mysql_like.asp + */ + public WHERE startsWith(T obj) { + String s = obj.toString().replace("_", "/_").replace("%", "/%"); + s = s + "%"; + sqlBuilder.append(columnName).append(" LIKE ? ESCAPE '/' "); + whereObjects.add(s); + return this; + } + /** + * columnName LIKE ?
+ * Example: WHERE CustomerName LIKE '%a'
+ * Explanation: Finds any values that end with "a"
+ * Note: Your provided obj gets turned to a string and if it already contains '_' or '%' these get escaped with '/' to ensure a correct query.
+ * + * @see https://www.w3schools.com/mysql/mysql_like.asp + */ + public WHERE endsWith(T obj) { + String s = obj.toString().replace("_", "/_").replace("%", "/%"); + s = "%" + s; + sqlBuilder.append(columnName).append(" LIKE ? ESCAPE '/' "); + whereObjects.add(s); + return this; + } + /** + * columnName LIKE ?
+ * Example: WHERE CustomerName LIKE '%or%'
+ * Explanation: Finds any values that have "or" in any position
+ * Note: Your provided obj gets turned to a string and if it already contains '_' or '%' these get escaped with '/' to ensure a correct query.
+ * + * @see https://www.w3schools.com/mysql/mysql_like.asp + */ + public WHERE contains(T obj) { + String s = obj.toString().replace("_", "/_").replace("%", "/%"); + s = "%" + s + "%"; + sqlBuilder.append(columnName).append(" LIKE ? ESCAPE '/' "); + whereObjects.add(s); + return this; + } /** * columnName NOT LIKE ?
@@ -1126,4 +1219,8 @@ public WHERE limit(int num) { } } +// The code below will not be removed when re-generating this class. +// Additional code start -> + private Person(){} +// Additional code end <- } diff --git a/core/src/test/java/com/osiris/jsqlgen/testDB/PersonOrder.java b/core/src/test/java/com/osiris/jsqlgen/testDB/PersonOrder.java index 65c0b40..f2ac307 100644 --- a/core/src/test/java/com/osiris/jsqlgen/testDB/PersonOrder.java +++ b/core/src/test/java/com/osiris/jsqlgen/testDB/PersonOrder.java @@ -9,10 +9,6 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; import java.util.Arrays; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.ByteArrayInputStream; -import java.sql.SQLException; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.formlayout.FormLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; @@ -29,15 +25,25 @@ import com.vaadin.flow.component.html.Div; import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.component.button.ButtonVariant; +import com.vaadin.flow.component.dialog.Dialog; +import java.util.function.Function; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.html.Span; /** +Table PersonOrder with id 2 and 0 changes/version.
+Structure (4 fields/columns):
+- int id = INT NOT NULL PRIMARY KEY
+- int personId = INT NOT NULL
+- String name = TEXT DEFAULT ''
+- int time = INT DEFAULT 10000
+ Generated class by jSQL-Gen that contains static methods for fetching/updating data from the `personorder` table. A single object/instance of this class represents a single row in the table and data can be accessed via its public fields.

-You can add your own code to the top of this class.
+You can add your own code to the bottom of this class.
Do not modify the rest of this class since those changes will be removed at regeneration. If modifications are really needed create a pull request directly to jSQL-Gen instead.

@@ -51,84 +57,15 @@ - VAADIN FLOW is enabled, which means that an additional obj.toComp() method
will be generated that returns a Vaadin Flow UI Form representation that allows creating/updating/deleting a row/object.

-Structure (3 fields/columns):
-- int id = INT NOT NULL PRIMARY KEY
-- int personId = INT NOT NULL
-- String name = TEXT DEFAULT ''
*/ -public class PersonOrder implements Database.Row{ -// The code below will not be removed when re-generating this class. -// Additional code start -> - private PersonOrder(){} -// Additional code end <- -class DefaultBlob implements Blob{ - private byte[] data; - - // Constructor that accepts a byte array - public DefaultBlob(byte[] data) { - this.data = data; - } - @Override - public long length() throws SQLException { - return data.length; - } - - @Override - public byte[] getBytes(long pos, int length) throws SQLException { - return data; - } - - @Override - public InputStream getBinaryStream() throws SQLException { - return new ByteArrayInputStream(data); - } - - @Override - public long position(byte[] pattern, long start) throws SQLException { - return 0; - } - - @Override - public long position(Blob pattern, long start) throws SQLException { - return 0; - } - - @Override - public int setBytes(long pos, byte[] bytes) throws SQLException { - return 0; - } - - @Override - public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { - return 0; - } - - @Override - public OutputStream setBinaryStream(long pos) throws SQLException { - return null; - } - - @Override - public void truncate(long len) throws SQLException { - - } - - @Override - public void free() throws SQLException { - - } - - @Override - public InputStream getBinaryStream(long pos, long length) throws SQLException { - return new ByteArrayInputStream(data); - } -} +public class PersonOrder implements Database.Row{ /** Limitation: Not executed in constructor, but only the create methods. */ public static CopyOnWriteArrayList> onCreate = new CopyOnWriteArrayList>(); public static CopyOnWriteArrayList> onAdd = new CopyOnWriteArrayList>(); public static CopyOnWriteArrayList> onUpdate = new CopyOnWriteArrayList>(); -/** Limitation: Only executed in remove(obj) method. */ public static CopyOnWriteArrayList> onRemove = new CopyOnWriteArrayList>(); + +private static boolean isEqual(PersonOrder obj1, PersonOrder obj2){ return obj1.equals(obj2) || obj1.id == obj2.id; } /** * Only works correctly if the package name is com.osiris.jsqlgen. */ @@ -144,6 +81,8 @@ private static String minimalStackString(){ return s +"..."+ stack[1].toString(); //stack[0] == current method, gets ignored } public static java.util.concurrent.atomic.AtomicInteger idCounter = new java.util.concurrent.atomic.AtomicInteger(0); +public int getId(){return id;} +public void setId(int id){this.id = id;} public static volatile boolean hasChanges = false; static { try{ @@ -155,10 +94,6 @@ private static String minimalStackString(){ if(i == 0){ if(t.steps < 1){s.executeUpdate("CREATE TABLE IF NOT EXISTS `personorder` (`id` INT NOT NULL PRIMARY KEY)"); t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 2){try{s.executeUpdate("ALTER TABLE `personorder` ADD COLUMN `personId` INT NOT NULL");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} -if(t.steps < 3){try{s.executeUpdate("ALTER TABLE `personorder` ADD COLUMN `name` TEXT DEFAULT ''");}catch(Exception exAdd){if(!exAdd.getMessage().startsWith("Duplicate")) throw exAdd;} -t.steps++; Database.updateTableMetaData(t);} t.steps = 0; t.version++; Database.updateTableMetaData(t); } @@ -245,9 +180,9 @@ public PersonOrder (int id, int personId){ if you plan to add this object to the database in the future, since that method fetches and sets/reserves the {@link #id}. */ -public PersonOrder (int id, int personId, String name){ +public PersonOrder (int id, int personId, String name, int time){ initDefaultFields(); -this.id = id;this.personId = personId;this.name = name; +this.id = id;this.personId = personId;this.name = name;this.time = time; } /** Database field/value: INT NOT NULL PRIMARY KEY.
@@ -262,10 +197,20 @@ public PersonOrder (int id, int personId, String name){ */ public String name; /** +Database field/value: INT DEFAULT 10000.
+*/ +public int time; +/** Initialises the DEFAULT fields with the provided default values mentioned in the columns definition. */ protected PersonOrder initDefaultFields() { -this.name=""; return this; +this.name=""; this.time=10000; return this; +} + +public static class Optionals{ +public Optionals(){this.name=""; this.time=10000; } +public String name; public int time; +public Optionals name(String name){ this.name = name; return this;} public Optionals time(int time){ this.time = time; return this;} } /** @@ -274,13 +219,26 @@ Increments the id (thread-safe) and sets it for this object (basically reserves Note that the parameters of this method represent "NOT NULL" fields in the table and thus should not be null. Also note that this method will NOT add the object to the table. */ -public static PersonOrder create( int personId) { +public static PersonOrder create(int personId) { int id = idCounter.getAndIncrement(); PersonOrder obj = new PersonOrder(id, personId); onCreate.forEach(code -> code.accept(obj)); return obj; } +/** +Creates and returns an object that can be added to this table. +Increments the id (thread-safe) and sets it for this object (basically reserves a space in the database). +Note that this method will NOT add the object to the table. +*/ +public static PersonOrder create(int personId, String name, int time) { +int id = idCounter.getAndIncrement(); +PersonOrder obj = new PersonOrder(); +obj.id=id; obj.personId=personId; obj.name=name; obj.time=time; +onCreate.forEach(code -> code.accept(obj)); +return obj; +} + /** Creates and returns an in-memory object with -1 as id, that can be added to this table AFTER you manually did obj.id = idCounter.getAndIncrement(). @@ -288,7 +246,7 @@ public static PersonOrder create( int personId) { Note that the parameters of this method represent "NOT NULL" fields in the table and thus should not be null. Also note that this method will NOT add the object to the table. */ -public static PersonOrder createInMem( int personId) { +public static PersonOrder createInMem(int personId) { int id = -1; PersonOrder obj = new PersonOrder(id, personId); onCreate.forEach(code -> code.accept(obj)); @@ -296,14 +254,16 @@ public static PersonOrder createInMem( int personId) { } /** -Creates and returns an object that can be added to this table. -Increments the id (thread-safe) and sets it for this object (basically reserves a space in the database). -Note that this method will NOT add the object to the table. +Creates and returns an in-memory object with -1 as id, that can be added to this table +AFTER you manually did obj.id = idCounter.getAndIncrement(). +This is useful for objects that may never be added to the table. +Note that the parameters of this method represent "NOT NULL" fields in the table and thus should not be null. +Also note that this method will NOT add the object to the table. */ -public static PersonOrder create( int personId, String name) { -int id = idCounter.getAndIncrement(); +public static PersonOrder createInMem(int personId, String name, int time) { +int id = -1; PersonOrder obj = new PersonOrder(); -obj.id=id; obj.personId=personId; obj.name=name; +obj.id=id; obj.personId=personId; obj.name=name; obj.time=time; onCreate.forEach(code -> code.accept(obj)); return obj; } @@ -312,7 +272,7 @@ public static PersonOrder create( int personId, String name) { Convenience method for creating and directly adding a new object to the table. Note that the parameters of this method represent "NOT NULL" fields in the table and thus should not be null. */ -public static PersonOrder createAndAdd( int personId) { +public static PersonOrder createAndAdd(int personId) { int id = idCounter.getAndIncrement(); PersonOrder obj = new PersonOrder(id, personId); onCreate.forEach(code -> code.accept(obj)); @@ -323,10 +283,19 @@ public static PersonOrder createAndAdd( int personId) { /** Convenience method for creating and directly adding a new object to the table. */ -public static PersonOrder createAndAdd( int personId, String name) { +public static PersonOrder createAndAdd(int personId, String name, int time) { int id = idCounter.getAndIncrement(); PersonOrder obj = new PersonOrder(); -obj.id=id; obj.personId=personId; obj.name=name; +obj.id=id; obj.personId=personId; obj.name=name; obj.time=time; +onCreate.forEach(code -> code.accept(obj)); +add(obj); +return obj; +} + +public static PersonOrder createAndAdd(int personId, Optionals optionals) { +int id = idCounter.getAndIncrement(); +PersonOrder obj = new PersonOrder(id, personId); +obj.name = optionals.name; obj.time = optionals.time; onCreate.forEach(code -> code.accept(obj)); add(obj); return obj; @@ -356,7 +325,7 @@ public static PersonOrder get(int id) { if that statement is null, returns all the contents of this table. */ public static List get(String where, Object... whereValues) { -String sql = "SELECT `id`,`personId`,`name`" + +String sql = "SELECT `id`,`personId`,`name`,`time`" + " FROM `personorder`" + (where != null ? where : ""); synchronized(cachedResults){ CachedResult cachedResult = cacheContains(sql, whereValues); @@ -379,6 +348,7 @@ public static List get(String where, Object... whereValues) { obj.id = rs.getInt(1); obj.personId = rs.getInt(2); obj.name = rs.getString(3); +obj.time = rs.getInt(4); } msJDBC = System.currentTimeMillis() - msJDBC; }catch(Exception e){throw new RuntimeException(e);} @@ -474,7 +444,7 @@ public static Thread getLazySync(Consumer> onResultReceived, C return thread; } -public static int count(){ return count(null, null); } +public static int count(){ return count(null, (Object[]) null); } public static int count(String where, Object... whereValues) { String sql = "SELECT COUNT(`id`) AS recordCount FROM `personorder`" + @@ -505,7 +475,7 @@ Searches the provided object in the database (by its id), @throws Exception when failed to find by id or other SQL issues. */ public static void update(PersonOrder obj) { -String sql = "UPDATE `personorder` SET `id`=?,`personId`=?,`name`=? WHERE id="+obj.id; +String sql = "UPDATE `personorder` SET `id`=?,`personId`=?,`name`=?,`time`=? WHERE id="+obj.id; long msGetCon = System.currentTimeMillis(); long msJDBC = 0; Connection con = Database.getCon(); msGetCon = System.currentTimeMillis() - msGetCon; @@ -514,6 +484,7 @@ public static void update(PersonOrder obj) { ps.setInt(1, obj.id); ps.setInt(2, obj.personId); ps.setString(3, obj.name); +ps.setInt(4, obj.time); ps.executeUpdate(); msJDBC = System.currentTimeMillis() - msJDBC; }catch(Exception e){throw new RuntimeException(e);} @@ -528,7 +499,7 @@ public static void update(PersonOrder obj) { Adds the provided object to the database (note that the id is not checked for duplicates). */ public static void add(PersonOrder obj) { -String sql = "INSERT INTO `personorder` (`id`,`personId`,`name`) VALUES (?,?,?)"; +String sql = "INSERT INTO `personorder` (`id`,`personId`,`name`,`time`) VALUES (?,?,?,?)"; long msGetCon = System.currentTimeMillis(); long msJDBC = 0; Connection con = Database.getCon(); msGetCon = System.currentTimeMillis() - msGetCon; @@ -537,6 +508,7 @@ public static void add(PersonOrder obj) { ps.setInt(1, obj.id); ps.setInt(2, obj.personId); ps.setString(3, obj.name); +ps.setInt(4, obj.time); ps.executeUpdate(); msJDBC = System.currentTimeMillis() - msJDBC; }catch(Exception e){throw new RuntimeException(e);} @@ -548,14 +520,14 @@ public static void add(PersonOrder obj) { } /** -Unsets its references (sets them to -1) and deletes the provided object from the database. +Unsets its references (sets them to -1/'') and deletes the provided object from the database. */ public static void remove(PersonOrder obj) { -remove(obj, true, false); +remove(obj, true, Database.isRemoveRefs); } /** * Deletes the provided object from the database. - * @param unsetRefs If true, sets ids in other tables to -1. + * @param unsetRefs If true, sets ids in other tables to -1/''. * @param removeRefs !!! EXTREME CAUTION REQUIRED, MAJOR DATA-LOSS POSSIBLE !!! If true removes the complete obj/row(s) in all tables that reference/contain this id. * This is recursive. It's highly recommended to call removeRefs() before instead, which allows to explicitly exclude some tables. */ @@ -595,58 +567,43 @@ public static void remove(String where, Object... whereValues) { } public static void removeAll() { -String sql = "DELETE FROM `personorder`"; -long msGetCon = System.currentTimeMillis(); long msJDBC = 0; -Connection con = Database.getCon(); -msGetCon = System.currentTimeMillis() - msGetCon; -msJDBC = System.currentTimeMillis(); - try (PreparedStatement ps = con.prepareStatement(sql)) { - ps.executeUpdate(); -msJDBC = System.currentTimeMillis() - msJDBC; -}catch(Exception e){throw new RuntimeException(e);} - finally{System.err.println(sql+" /* //// msGetCon="+msGetCon+" msJDBC="+msJDBC+" con="+con+" minimalStack="+minimalStackString()+" */"); -Database.freeCon(con); -clearCache(); -} +getLazySync(objs -> {for(PersonOrder obj : objs) {obj.remove();}}); } /** - * @see #remove(Person, boolean, boolean) - */public static void unsetRefs(PersonOrder obj) { + * @see #remove(PersonOrder, boolean, boolean) + */ +public static void unsetRefs(PersonOrder obj) { } /** !!! EXTREME CAUTION REQUIRED, MAJOR DATA-LOSS POSSIBLE !!! - * @see #remove(Person, boolean, boolean) - */public static void removeRefs(PersonOrder obj) { + * @see #remove(PersonOrder, boolean, boolean) + */ +public static void removeRefs(PersonOrder obj) { +// Take care of direct refs and indirect refs } public PersonOrder clone(){ -return new PersonOrder(this.id,this.personId,this.name); +return new PersonOrder(this.id,this.personId,this.name,this.time); } -public PersonOrder add(){ +public void add(){ PersonOrder.add(this); -return this; } -public PersonOrder update(){ +public void update(){ PersonOrder.update(this); -return this; } -public PersonOrder remove(){ +public void remove(){ PersonOrder.remove(this); -return this; } -public PersonOrder remove(boolean unsetRefs, boolean removeRefs){ +public void remove(boolean unsetRefs, boolean removeRefs){ PersonOrder.remove(this, unsetRefs, removeRefs); -return this; } public String toPrintString(){ -return ""+"id="+this.id+" "+"personId="+this.personId+" "+"name="+this.name+" "; +return ""+"id="+this.id+" "+"personId="+this.personId+" "+"name="+this.name+" "+"time="+this.time+" "; } public String toMinimalPrintString(){ -return ""+this.id+"; "+this.personId+"; "+this.name+"; "+""; +return ""+this.id+"; "+this.personId+"; "+this.name+"; "+this.time+"; "+""; } - public static class Comp extends VerticalLayout{ - public static class BooleanSelect extends Select { public Span yes = genYesLabel(); public Span no = genNoLabel(); @@ -685,7 +642,96 @@ public Span genNoLabel(){ txt.getStyle().set("background-color", "var(--lumo-error-color)"); return txt; } +}// Executed for all objects +public static Consumer onCreateV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {PersonOrder.onCreate.remove(code2);}); PersonOrder.onCreate.add(code2); return code2; +} +// Executed for all objects +public static Consumer onAddV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {PersonOrder.onAdd.remove(code2);}); PersonOrder.onAdd.add(code2); return code2; +} +// Executed for all objects +public static Consumer onUpdateV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {PersonOrder.onUpdate.remove(code2);}); PersonOrder.onUpdate.add(code2); return code2; +} +// Executed for all objects +public static Consumer onRemoveV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {PersonOrder.onRemove.remove(code2);}); PersonOrder.onRemove.add(code2); return code2; +} + + +// Executed only for this object +public Consumer onCreateThisV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {if(!isEqual(this, obj)) return; ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {PersonOrder.onCreate.remove(code2);}); PersonOrder.onCreate.add(code2); return code2; +} +// Executed only for this object +public Consumer onAddThisV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {if(!isEqual(this, obj)) return; ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {PersonOrder.onAdd.remove(code2);}); PersonOrder.onAdd.add(code2); return code2; +} +// Executed only for this object +public Consumer onUpdateThisV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {if(!isEqual(this, obj)) return; ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {PersonOrder.onUpdate.remove(code2);}); PersonOrder.onUpdate.add(code2); return code2; +} +// Executed only for this object +public Consumer onRemoveThisV(Consumer code){ +UI ui = UI.getCurrent(); Consumer code2 = (obj) -> {if(!isEqual(this, obj)) return; ui.access(() -> {code.accept(obj);});}; ui.addDetachListener(e -> {PersonOrder.onRemove.remove(code2);}); PersonOrder.onRemove.add(code2); return code2; +} + + +public static ComboBox newTableComboBox(){ + ComboBox comboBox = new ComboBox("PersonOrder"); + {comboBox.setItems(PersonOrder.get()); + comboBox.setRenderer(new ComponentRenderer<>(obj -> { + Div div = new Div(); + div.setText(obj.toMinimalPrintString()); + return div;})); + comboBox.setItemLabelGenerator(obj -> { + return obj.toMinimalPrintString(); + }); + } +return comboBox; +} + +public static NumberField newNfId(){ + NumberField nfId = new NumberField("Id"); +return nfId; +} + +public static ComboBox newCbPerson(){ + ComboBox cbPerson = new ComboBox("Person"); + {cbPerson.setItems(Person.get()); + cbPerson.setRenderer(new ComponentRenderer<>(obj -> { + Div div = new Div(); + div.setText(obj.toMinimalPrintString()); + return div;})); + cbPerson.setItemLabelGenerator(obj -> { + return obj.toMinimalPrintString(); + }); + } +return cbPerson; } + +public static TextField newTfName(){ + TextField tfName = new TextField("Name"); +return tfName; +} + +public static NumberField newNfTime(){ + NumberField nfTime = new NumberField("Time"); +return nfTime; +} + + /** + * Gets executed later if {@link #isOnlyInMemory()}, otherwise provided + * code gets executed directly. + */ public void whenReadyV(Consumer code) { + if(isOnlyInMemory()) onAddThisV(obj -> code.accept(obj)); + else code.accept(this); + } + + public static class Comp extends VerticalLayout{ + + public PersonOrder dataPersonOrder; public PersonOrder data; // Form and fields @@ -702,6 +748,7 @@ public Span genNoLabel(){ }); } public TextField tfName = new TextField("Name"); + public NumberField nfTime = new NumberField("Time"); // Buttons public HorizontalLayout hlButtons = new HorizontalLayout(); public Button btnAdd = new Button("Add"); @@ -711,7 +758,7 @@ public Span genNoLabel(){ updateData(); data.id = idCounter.getAndIncrement(); PersonOrder.add(data); - e.unregisterListener(); // Make sure it gets only added once to the database + e.unregisterListener(); // Make sure it gets only executed once updateButtons(); }; public Button btnSave = new Button("Save"); @@ -728,12 +775,13 @@ public Span genNoLabel(){ public Consumer> onBtnDeleteClick = (e) -> { btnDelete.setEnabled(false); PersonOrder.remove(data); - e.unregisterListener(); // Make sure it gets only added once to the database + e.unregisterListener(); // Make sure it gets only executed once updateButtons(); }; public Comp(PersonOrder data) { this.data = data; + this.dataPersonOrder = this.data; setWidthFull(); setPadding(false); @@ -746,6 +794,7 @@ public Comp(PersonOrder data) { form.add(nfId); form.add(cbPerson); form.add(tfName); + form.add(nfTime); // Add buttons add(hlButtons); @@ -763,11 +812,13 @@ public void updateFields(){ nfId.setValue(0.0 + data.id); cbPerson.setValue(data.personId != -1 ? Person.get(data.personId) : null); tfName.setValue(data.name); + nfTime.setValue(0.0 + data.time); } public void updateData(){ data.id = (int) nfId.getValue().doubleValue(); data.personId = cbPerson.getValue() != null ? cbPerson.getValue().id : -1; data.name = tfName.getValue(); + data.time = (int) nfTime.getValue().doubleValue(); } public void updateButtons(){ @@ -781,10 +832,13 @@ public void updateButtons(){ hlButtons.add(btnDelete); hlButtons.addAndExpand(btnSave); } + } + public static volatile Function global_fn_toComp = (obj) -> {return new PersonOrder.Comp(obj);}; + public volatile Function fn_toComp = (_null) -> {return global_fn_toComp.apply(this);}; public PersonOrder.Comp toComp(){ - return new PersonOrder.Comp(this); + return fn_toComp.apply(null); } public boolean isOnlyInMemory(){ @@ -799,6 +853,9 @@ public static WHERE wherePersonId() { public static WHERE whereName() { return new WHERE("`name`"); } +public static WHERE whereTime() { +return new WHERE("`time`"); +} public static class WHERE { /** * Remember to prepend WHERE on the final SQL statement. @@ -835,6 +892,16 @@ public List get() { return PersonOrder.get(where+orderBy+limitBuilder.toString(), (T[]) null); } + /** + * Executes the generated SQL statement + * and returns the first object matching the query or null if none. + */ + public PersonOrder getFirstOrNull() { + List results = get(); + if(results.isEmpty()) return null; + else return results.get(0); + } + /** * Executes the generated SQL statement * and returns the size of the list of objects matching the query. @@ -951,6 +1018,51 @@ public WHERE like(T obj) { whereObjects.add(obj); return this; } + /** + * columnName LIKE ?
+ * Example: WHERE CustomerName LIKE 'a%'
+ * Explanation: Finds any values that start with "a"
+ * Note: Your provided obj gets turned to a string and if it already contains '_' or '%' these get escaped with '/' to ensure a correct query.
+ * + * @see https://www.w3schools.com/mysql/mysql_like.asp + */ + public WHERE startsWith(T obj) { + String s = obj.toString().replace("_", "/_").replace("%", "/%"); + s = s + "%"; + sqlBuilder.append(columnName).append(" LIKE ? ESCAPE '/' "); + whereObjects.add(s); + return this; + } + /** + * columnName LIKE ?
+ * Example: WHERE CustomerName LIKE '%a'
+ * Explanation: Finds any values that end with "a"
+ * Note: Your provided obj gets turned to a string and if it already contains '_' or '%' these get escaped with '/' to ensure a correct query.
+ * + * @see https://www.w3schools.com/mysql/mysql_like.asp + */ + public WHERE endsWith(T obj) { + String s = obj.toString().replace("_", "/_").replace("%", "/%"); + s = "%" + s; + sqlBuilder.append(columnName).append(" LIKE ? ESCAPE '/' "); + whereObjects.add(s); + return this; + } + /** + * columnName LIKE ?
+ * Example: WHERE CustomerName LIKE '%or%'
+ * Explanation: Finds any values that have "or" in any position
+ * Note: Your provided obj gets turned to a string and if it already contains '_' or '%' these get escaped with '/' to ensure a correct query.
+ * + * @see https://www.w3schools.com/mysql/mysql_like.asp + */ + public WHERE contains(T obj) { + String s = obj.toString().replace("_", "/_").replace("%", "/%"); + s = "%" + s + "%"; + sqlBuilder.append(columnName).append(" LIKE ? ESCAPE '/' "); + whereObjects.add(s); + return this; + } /** * columnName NOT LIKE ?
@@ -1040,4 +1152,8 @@ public WHERE limit(int num) { } } +// The code below will not be removed when re-generating this class. +// Additional code start -> + private PersonOrder(){} +// Additional code end <- } diff --git a/settings.gradle b/settings.gradle index 8cb8f95..0b2799e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,24 +18,4 @@ apply plugin: 'org.kordamp.gradle.enforcer' Note that having this check is crucial to avoid runtime "method not found" errors that will lead to crashes and/or unexpected behaviour. */ -enforce { - rule(enforcer.rules.DependencyConvergence) { r -> - r.enabled - r.enforcerLevel - r.phases - r.failOnDynamicVersions - r.failOnChangingVersions - r.failOnNonReproducibleResolution - r.activateDependencyLocking - r.deactivateDependencyLocking - } - rule(enforcer.rules.ForceDependencies) { r -> - r.dependencies.add('org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10') - r.dependencies.add('org.jetbrains:annotations:23.0.0') - r.dependencies.add('org.slf4j:slf4j-api:2.0.13') - r.dependencies.add('org.apache.commons:commons-lang3:3.12.0') - r.dependencies.add('org.apache.commons:commons-collections4:4.4') - r.dependencies.add('commons-io:commons-io:2.16.1') - r.dependencies.add('com.google.code.gson:gson:2.11.0') - } -} +