1 Commits

Author SHA1 Message Date
wea_ondara
831a734668 wip 2022-08-15 19:34:28 +02:00
231 changed files with 1440 additions and 4847 deletions

View File

@@ -8,15 +8,13 @@
- references to platform specific impls
## Asm
- method references
- equals function for primitive and string + String::equalsIgnoreCase
- equals function for registered primitive conversion types
- IConst0Fixer: IConst0/1 on its own => false/true
- IConst0Fixer: IConst0/1 false/true depending on compared field type
- actually parse getter
- resolve Predicate functions (not, and, or)
- don't bind external vars in lambdas, instead make information (parameter and intercepted values) available in a context object
- expose more stuff as result (e.g. accessed members/functions, ...)
- cleanup exception handling
- cleanup if else trees
## ModelBuilder
- add registrable conversion types e.g. UUID, Date, Timestamp, Calendar, LocalTime, Instant,...
@@ -24,9 +22,6 @@
- field length
- field precision
- sql type
- ignore() entity/field for db
- object inlining
- entity() with callback
## Annotations
- db type

View File

@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jef</artifactId>
<groupId>jef</groupId>
<version>0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cli</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>jef.main.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>jef</groupId>
<artifactId>migration-creator</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,27 +0,0 @@
package jef.main;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
if (args.length == 0) {
printHelp();
System.exit(1);
}
switch (args[0].toLowerCase()) {
case "help":
printHelp();
case "migration":
MigrationCommandHandler.handleMigration(Arrays.copyOfRange(args, 1, args.length));
default:
printHelp();
}
}
static void printHelp() {
System.out.println("Usage: java -jar thisfile <command> [options]");
MigrationCommandHandler.printHelp();
System.exit(1);
}
}

View File

@@ -1,279 +0,0 @@
package jef.main;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.migration.creator.MigrationCreator;
import jef.platform.dummy.DummyPlatform;
import jef.util.Util;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class MigrationCommandHandler {
static void handleMigration(String[] args) {
if (args.length == 0) {
Main.printHelp();
}
try {
switch (args[0].toLowerCase()) {
case "add":
handleMigrationAdd(Arrays.copyOfRange(args, 1, args.length));
case "remove":
handleMigrationRemove(Arrays.copyOfRange(args, 1, args.length));
case "list":
handleMigrationList(Arrays.copyOfRange(args, 1, args.length));
default:
Main.printHelp();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
private static void handleMigrationList(String[] args) {
}
private static void handleMigrationRemove(String[] args) {
}
private static void handleMigrationAdd(String[] args) throws Exception {
String contextName = null;
String migrationPackage = null;
String targetFolder = null;
String name = null;
String platformType = null;
var classPath = new ArrayList<String>();
for (int i = 0; i < args.length; i++) {
switch (args[i].toLowerCase()) {
case "--cp":
if (i + 1 >= args.length) {
Main.printHelp();
}
classPath.addAll(List.of(args[++i].split(":")));//TODO does not work well on windows (:)
break;
case "--context":
case "-c":
if (i + 1 >= args.length) {
Main.printHelp();
}
contextName = args[++i];
break;
case "--output":
case "-o":
if (i + 1 >= args.length) {
Main.printHelp();
}
targetFolder = args[++i];
break;
case "--platform":
case "-p":
if (i + 1 >= args.length) {
Main.printHelp();
}
platformType = args[++i];
break;
default:
if (name != null) {
Main.printHelp();
}
name = args[i];
break;
}
}
// if (Set.of(contextName, targetFolder, name).contains(null)) {
// Main.printUsage();
// }
if (name == null) {
System.err.println("The migration requires a name.");
System.exit(1);
}
//find data locations
var urls = classPath.stream().map(e -> Util.tryGet(() -> new File(e).toURI().toURL()).orElseThrow()).toArray(URL[]::new);
var cl = new URLClassLoader(urls, MigrationCommandHandler.class.getClassLoader());
var context = contextName == null ? findAnyContext(cl) : findContextByName(cl, contextName);
targetFolder = targetFolder == null ? System.getProperty("user.dir") : targetFolder;
var targetFolderFile = new File(targetFolder);
targetFolderFile.mkdirs();
migrationPackage = findMigrationPackageName(targetFolder);
var sqlPlatform = new DummyPlatform();//TODO find out from migrations or -p param
//find data
var currentSnapshotFile = new File(targetFolderFile, "CurrentSnapshot.java");
var from = !currentSnapshotFile.isFile()
? new ModelBuilder()
: (ModelBuilder) cl.loadClass((migrationPackage != null ? migrationPackage + "." : "") + "CurrentSnapshot").getConstructor().newInstance();
var to = ModelBuilder.from(context.orElseThrow().getClass());
// context.orElseThrow().onModelCreate(to);
//begin
var date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
var className = "M" + date + "_" + name;
var creator = new MigrationCreator(sqlPlatform, from, to, className, migrationPackage, currentSnapshotFile.isFile() ? Files.readString(currentSnapshotFile.toPath()) : null);
var result = creator.createMigration();
var fileOptions = new OpenOption[]{StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE};
Files.writeString(new File(targetFolder, result.getMigrationClassName() + ".java").toPath(), result.getMigration(), fileOptions);
Files.writeString(new File(targetFolder, result.getMigrationSnapshotClassName() + ".java").toPath(), result.getMigrationSnapshot(), fileOptions);
Files.writeString(new File(targetFolder, result.getCurrentSnapshotClassName() + ".java").toPath(), result.getCurrentSnapshot(), fileOptions);
System.exit(0);
}
private static Optional<DbContext> findAnyContext(URLClassLoader cl) {
return findContextByName(cl, "");
}
private static Optional<DbContext> findContextByName(URLClassLoader cl, String contextName) {
Util.ThrowableFunction<String, DbContext> finder = (String entryName) -> {
var cls = (Class<? extends DbContext>) cl.loadClass(entryName.substring(0, entryName.length() - ".class".length()).replace("/", "."));
var parent = (Class<?>) cls;
while (parent != Object.class && parent != DbContext.class) {
parent = parent.getSuperclass();
}
if (parent == DbContext.class) {
return Util.tryGet(() -> cls.getDeclaredConstructor().newInstance()).orElse(null);
}
return null;
};
return Arrays.stream(cl.getURLs()).map(url -> {
URLConnection conn;
try {
conn = url.openConnection();
} catch (IOException ignored) {
return null;
}
if (conn instanceof JarURLConnection) {
try (var zip = new ZipInputStream(conn.getInputStream())) {
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
if (entry.isDirectory() || !entry.getName().endsWith(contextName + ".class")) {
continue;
}
return finder.apply(entry.getName());
}
} catch (Throwable ignored) {
}
} else {
var search = new ArrayList<URL>();
search.add(url);
while (search.size() > 0) {
try {
var currentUrl = search.remove(0);
conn = currentUrl.openConnection();
try (var is = conn.getInputStream();
var isr = new InputStreamReader(is);
var br = new BufferedReader(isr)) {
String entry;
while ((entry = br.readLine()) != null) {
// System.out.println(entry);
var newUri = currentUrl.toURI().toString();
newUri = newUri.endsWith("/") ? newUri : newUri + "/";
newUri += entry;
search.add(new URI(newUri).toURL());
if (!entry.endsWith(contextName + ".class")) {//entry.isDirectory() ||
continue;
}
return finder.apply(new URI(newUri).getPath().substring(url.toURI().getPath().length()));
}
}
} catch (Throwable ignored) {
}
}
}
return null;
})
.filter(Objects::nonNull)
.findFirst();
}
private static String findMigrationPackageName(String dir) {
//search for java file in dir and take package name from there
var javaFiles = new File(dir).listFiles(file -> file.isFile() && file.getName().endsWith(".java"));
if (javaFiles.length > 0) {
try {
var contextFileString = Files.readAllLines(javaFiles[0].toPath()).stream().collect(Collectors.joining(" "));
var pattern = Pattern.compile("\\s*package\\s+(.*?);");
var matcher = pattern.matcher(contextFileString);
if (matcher.find()) {
return matcher.group(1);
}
} catch (IOException ignored) {
}
}
// //try finding out from dir path
var path = new File(dir).getAbsolutePath();
var i = path.indexOf("/src/");
if (i >= 0) {
path = path.substring(i + "/src/".length());
if (path.startsWith("main/")) {
path = path.substring("main/".length());
if (path.startsWith("java/")) {
path = path.substring("java/".length());
}
}
}
i = path.indexOf("/target/");
if (i >= 0) {
path = path.substring(i + "/target/".length());
if (path.startsWith("generated-migrations/")) {
path = path.substring("generated-migrations/".length());
if (path.startsWith("src/")) {
path = path.substring("src/".length());
}
}
}
return path.replace("/", ".");
// return null;
}
public static void printHelp() {
System.out.println("migration add --cp <classpath> [--context/-c <context class name>] [--output/-o <folder for migrations>] <name>");
System.out.println("migration remove --cp <classpath> [--context/-c <context class name>] [--output/-o <folder for migrations>] <name>");
System.out.println("migration list");
}
// private static String findMigrationSnapshot(String dir) {
// var javaFiles = new File(dir).listFiles(file -> file.isFile() && file.getName().endsWith(".java"));
// if (javaFiles.length > 0) {
// try {
// var contextFileString = Files.readAllLines(javaFiles[0].toPath()).stream().collect(Collectors.joining(" "));
// var pattern = Pattern.compile("\\s*package\\s+(.*?);");
// var matcher = pattern.matcher(contextFileString);
// if (matcher.find()) {
// return matcher.group(1);
// }
// } catch (IOException ignored) {
// }
// }
// }
}

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>jef</groupId>
<artifactId>jef</artifactId>
<version>0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>core</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin><!-- generate tests jar so that test utils can be used in downstream projects -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,18 +0,0 @@
package jef;
public class MigrationException extends Exception {
public MigrationException() {
}
public MigrationException(String message) {
super(message);
}
public MigrationException(String message, Throwable cause) {
super(message, cause);
}
public MigrationException(Throwable cause) {
super(cause);
}
}

View File

@@ -1,15 +0,0 @@
package jef.asm;
import jef.expressions.Expression;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.lang.reflect.Field;
import java.util.Set;
@AllArgsConstructor
@Getter
public class AsmParseResult {
private final Expression expression;
private final Set<Field> accessedFields;
}

View File

@@ -1,81 +0,0 @@
package jef.asm;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializablePredicate;
import jef.util.Check;
import lombok.Getter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.stream.IntStream;
@Getter
public class AsmParser {
private final Serializable lambda;
private final Method method;
public AsmParser(SerializablePredicate<?> predicate) {
this.lambda = Check.notNull(predicate, "predicate");
this.method = null;
}
public AsmParser(SerializableFunction<?, ?> function) {
this.lambda = Check.notNull(function, "function");
this.method = null;
}
public AsmParser(Method method) {
this.lambda = null;
this.method = Check.notNull(method, "method");
}
public AsmParseResult parse() throws AsmParseException {
try {
if (this.lambda != null) {
return parseLambdaExpression();
} else if (this.method != null) {
return parseMethodExpression();
}
throw new IllegalStateException("Illegal state");
} catch (Exception e) {
throw new AsmParseException("PredicateParser: failed to parse expression: " + e.getLocalizedMessage(), e);
}
}
private AsmParseResult parseLambdaExpression() throws Exception {
var cls = (Class) lambda.getClass();
var loader = cls.getClassLoader();
var writeReplace = cls.getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
var serlambda = (SerializedLambda) writeReplace.invoke(lambda);
Object[] args = IntStream.range(0, serlambda.getCapturedArgCount()).mapToObj(serlambda::getCapturedArg).toArray();
var classname = serlambda.getImplClass();
var lambdaname = serlambda.getImplMethodName();
cls = Class.forName(classname.replace("/", "."));
var is = loader.getResourceAsStream(cls.getName().replace(".", "/") + ".class");
return parseCommon(is, lambdaname, args);
}
private AsmParseResult parseMethodExpression() throws Exception {
var cls = method.getDeclaringClass();
var loader = cls.getClassLoader();
InputStream is = loader.getResourceAsStream(cls.getName().replace(".", "/") + ".class");
Object[] args = new Object[0];//TODO capturing args here? or maybe not supported since this will only be user by getter evaluation
return parseCommon(is, method.getName(), args);
}
private AsmParseResult parseCommon(InputStream classIs, String methodname, Object[] args) throws Exception {
var cr = new ClassReader(classIs);
var visiter = new FilterClassVisitor(Opcodes.ASM9, methodname, args);
cr.accept(visiter, 0);
return visiter.getResult();
}
}

View File

@@ -1,54 +0,0 @@
package jef.asm;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import java.util.Optional;
class FilterClassVisitor extends ClassVisitor {
private final int api;
private final String lambdaname;
private final Object[] args;
private String className;
private FilterMethodVisitor mv;
protected FilterClassVisitor(int api, String lambdaname, Object[] args) {
super(api);
this.api = api;
this.lambdaname = lambdaname;
this.args = args;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.className = name;
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (!name.equals(lambdaname)) {
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
if (mv != null) {
throw new IllegalStateException("multiple lambda with same name found: " + lambdaname);
}
return mv = new FilterMethodVisitor(api, className, descriptor, args);
}
@Override
public void visitEnd() {
super.visitEnd();
//finish
if (mv == null) {
throw new IllegalStateException("lambda not found: " + lambdaname);
}
}
public AsmParseResult getResult() {
return Optional.ofNullable(mv).map(e -> e.getResult()).orElse(null);
}
}

View File

@@ -1,37 +0,0 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class ConstantExpression implements Expression {
public static final ConstantExpression V0 = new ConstantExpression(0);
public static final ConstantExpression V1 = new ConstantExpression(1);
public static final ConstantExpression V2 = new ConstantExpression(2);
public static final ConstantExpression V3 = new ConstantExpression(3);
public static final ConstantExpression V4 = new ConstantExpression(4);
public static final ConstantExpression V5 = new ConstantExpression(5);
protected final Object value;
@Override
public Type getType() {
return Type.CONSTANT;
}
@Override
public Priority getPriority() {
return Priority.CONSTANT;
}
@Override
public String toString() {
if (value instanceof String) {
return "\"" + value + "\"";
}
return value.toString();
}
}

View File

@@ -1,32 +0,0 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
@Getter
@EqualsAndHashCode
public class FunctionExpression implements Expression {
public static final String TOLOWER = "TOLOWER";
private final String function;
private final List<Expression> parameters;
@Override
public Type getType() {
return Type.FUNCTION;
}
@Override
public Priority getPriority() {
return Priority.UNARY_PRE;
}
@Override
public String toString() {
return function + "(" + parameters.stream().map(Expression::toString).collect(Collectors.joining(", ")) + ")";
}
}

View File

@@ -1,15 +0,0 @@
package jef.model;
import jef.platform.base.DatabaseOptions;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
@AllArgsConstructor
public class DbContextOptions {
private final DatabaseOptions databaseOptions;
}

View File

@@ -1,121 +0,0 @@
package jef.model;
import jef.asm.AsmParser;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializableObject;
import jef.util.Check;
import jef.util.Util;
import lombok.AllArgsConstructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@AllArgsConstructor
public class DbEntityBuilder<T extends SerializableObject> {
private final ModelBuilder modelBuilder;
private final DbEntity<T> entity;
public DbEntityBuilder<T> name(String name) {
entity.setName(name);
return this;
}
public <R> DbFieldBuilder<R> field(SerializableFunction<T, R> getter) {
try {
var prop = entity.getField(getter);
if (prop == null) {
var name = entity.extractFieldName(getter);
var field = ReflectionUtil.getFieldsRecursive(type().get()).stream().filter(f -> f.getName().equals(name)).findFirst().orElse(null);
if (field == null) {
throw new RuntimeException("Field not found: " + name);
}
prop = new DbField<>(entity, (Class<R>) field.getType(), field);
entity.addField(prop);
}
return new DbFieldBuilder<>(prop);
} catch (Exception e) {
throw new RuntimeException("Invalid expression", e);
}
}
public <R> DbFieldBuilder<R> field(Field field) {
var prop = (DbField<R>) entity.getFields().stream().filter(e -> e.getField().equals(field)).findFirst().orElse(null);
if (prop == null) {
prop = new DbField<>(entity, (Class<R>) field.getType(), field);
entity.addField(prop);
}
return new DbFieldBuilder<>(prop);
}
public <R> DbFieldBuilder<R> field(String name, String typeName) {
var prop = (DbField<R>) entity.getFields().stream().filter(e -> e.getName().equals(name)).findFirst().orElse(null);
if (prop == null) {
prop = new DbField<>(entity, name, typeName);
entity.addField(prop);
}
return new DbFieldBuilder<>(prop);
}
//keys
public KeyBuilder<T> hasOne(SerializableFunction<T, ?> getter) {
Check.notNull(getter, "getter");
var set = new AsmParser(getter).parse().getAccessedFields();
var fields = entity.getFields().stream().filter(e -> set.contains(e.getField())).toList();
return new KeyBuilder<>(modelBuilder, entity, fields);
}
public KeyBuilder<T> hasOne(String... getterOrFieldNames) {
Check.notNull(getterOrFieldNames, "getterOrFieldNames");
var set = new HashSet<>(Arrays.asList(getterOrFieldNames));
var fields = entity.getFields().stream().filter(e -> set.contains(e.getName())).toList();
return new KeyBuilder<>(modelBuilder, entity, fields);
}
public KeyBuilder<T> hasMany(SerializableFunction<T, ?> getter) {
Check.notNull(getter, "getter");
var set = new AsmParser(getter).parse().getAccessedFields();
var fields = entity.getFields().stream().filter(e -> set.contains(e.getField())).toList();
return new KeyBuilder<>(modelBuilder, entity, fields);
}
public KeyBuilder<T> hasMany(String... getterOrFieldNames) {
Check.notNull(getterOrFieldNames, "getterOrFieldNames");
var set = new HashSet<>(Arrays.asList(getterOrFieldNames));
var fields = entity.getFields().stream().filter(e -> set.contains(e.getName())).toList();
return new KeyBuilder<>(modelBuilder, entity, fields);
}
//getters
public String name() {
return entity.getName();
}
public List<DbFieldBuilder<?>> fields() {
return (List) entity.getFields().stream().map(e -> new DbFieldBuilder(e)).toList();
}
public Optional<Class<T>> type() {
return Optional.ofNullable(entity.getType()).or(() -> Util.tryGet(() -> (Class<T>) Class.forName(typeName())));
}
public String typeName() {
return entity.getTypeName();
}
public String className() {
var s = entity.getTypeName().replace("$", ".").split("\\.");
return s[s.length - 1];
}
public DbEntity<T> getEntity() { //TODO make this libaray private somehow
return entity;
}
@Override
public String toString() {
return entity.toString();
}
}

View File

@@ -1,95 +0,0 @@
package jef.model;
import jef.model.annotations.Generated;
import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
public class DbFieldBuilder<T> {
private final DbField<T> field;
public DbFieldBuilder<T> name(String name) {
field.setName(name);
return this;
}
public DbFieldBuilder<T> isDatabaseField(boolean isDatabase) {
field.setDatabaseField(isDatabase);
return this;
}
public DbFieldBuilder<T> isModelField(boolean isModel) {
field.setModelField(isModel);
return this;
}
public DbFieldBuilder<T> sqlType(String sqlType) {
field.setSqlType(sqlType);
return this;
}
public DbFieldBuilder<T> isNotNull() {
return isNotNull(true);
}
public DbFieldBuilder<T> isNotNull(boolean notnull) {
field.setNotNull(notnull);
return this;
}
public DbFieldBuilder<T> generated(Generated.Type type) {
field.setGenerated(type);
return this;
}
public void isUnique() {
isUnique(true);
}
public void isUnique(boolean unique) {
var entity = field.getEntity();
var constr = entity.getUniqueKeys().stream().filter(u -> u.getFields().size() == 1 && u.getFields().get(0) == field).findFirst();
if (!constr.isPresent() && unique) {
entity.addUniqueKey(new UniqueKeyConstraint(entity, new ArrayList<>(List.of(field))));
} else if (constr.isPresent() && !unique) {
entity.dropUniqueKey(constr.get());
} //else do nothing
}
public void isKey() {
isKey(true);
}
public void isKey(boolean key) {
var entity = field.getEntity();
var constr = entity.getKeys().stream().filter(u -> u.getFields().size() == 1 && u.getFields().get(0) == field).findFirst();
if (!constr.isPresent() && key) {
entity.addKey(new KeyConstraint(entity, new ArrayList<>(List.of(field))));
} else if (constr.isPresent() && !key) {
entity.dropKey(constr.get());
} //else do nothing
}
public void isIndex() {
isIndex(true);
}
public void isIndex(boolean index) {
var entity = field.getEntity();
var constr = entity.getIndexes().stream().filter(u -> u.getFields().size() == 1 && u.getFields().get(0) == field).findFirst();
if (!constr.isPresent() && index) {
entity.addIndex(new IndexConstraint(entity, new ArrayList<>(List.of(field))));
} else if (constr.isPresent() && !index) {
entity.dropIndex(constr.get());
} //else do nothing
}
public DbField<T> getField() {//TODO make this library private
return field;
}
}

View File

@@ -1,23 +0,0 @@
package jef.model;
import jef.util.Log;
import java.util.Arrays;
public class EntityDefaultConstructorChecker {
static void checkEntities(ModelBuilder mb) {
for (DbEntityBuilder<?> entity : mb.entities()) {
checkEntity(entity);
}
}
static void checkEntity(DbEntityBuilder<?> entity) {
Log.debug("Checking default constructor exists for entity '" + entity.name() + "' of type " + entity.className());
//check no arg constructor
Class<?> clazz = entity.type().orElseThrow();
if (Arrays.stream(clazz.getDeclaredConstructors()).noneMatch(e -> e.getParameterCount() == 0)) {
throw new ModelException("Class '" + clazz.getSimpleName() + "' does not have a default constructor!");
}
}
}

View File

@@ -1,83 +0,0 @@
package jef.model;
import jef.DbSet;
import jef.model.annotations.Clazz;
import jef.model.annotations.Transient;
import jef.serializable.SerializableObject;
import jef.util.Log;
import java.lang.reflect.Field;
import java.util.Collection;
class EntityInitializer {
static void initEntities(ModelBuilder mb, Class<? extends DbContext> context) {
for (Field ctxField : context.getDeclaredFields()) {
if (!DbSet.class.isAssignableFrom(ctxField.getType())) {
continue;
}
Clazz clazzAnnotation = ctxField.getAnnotation(Clazz.class);
if (clazzAnnotation == null) {
throw new ModelException("DbSet " + ctxField.getName() + " is missing the " + Clazz.class.getSimpleName() + " annotation");
}
if (!SerializableObject.class.isAssignableFrom(clazzAnnotation.value())) {
throw new ModelException("DbSet " + ctxField.getName() + " has a class in its " + Clazz.class.getSimpleName() + " annotation that does not inherit from " + SerializableObject.class.getSimpleName() + ": " + clazzAnnotation.value().getSimpleName());
}
var dbSetClazz = (Class<? extends SerializableObject>) clazzAnnotation.value();
initEntity(mb, dbSetClazz, ctxField.getName(), true);
}
}
static void initEntity(ModelBuilder mb, Class<? extends SerializableObject> clazz, String name, boolean overrideName) {
var existingEntity = mb.getEntity(clazz);
if (existingEntity != null) {
//allow DbSets to override the name
if (overrideName) {
existingEntity.setName(name);
}
return;
}
Log.debug("Initializing entity '" + name + "' of type " + clazz.getName());
var entity = mb.entity(clazz);
entity.name(name);
var fields = ReflectionUtil.getFieldsRecursive(clazz);
for (var f : fields) {
if (f.getAnnotationsByType(Transient.class).length > 0) {
continue;
}
Log.debug("Initializing field '" + f.getName() + "' with type " + f.getType().getName());
if (Collection.class.isAssignableFrom(f.getType())) {
//find a Collection field with the same Model
//e.g. class Entity { @Clazz(Entity2.class) List<Entity2> ent; @Clazz(Entity2.class) Set<Entity2> ent2; }
var clazzAnnotation = f.getAnnotation(Clazz.class);
if (clazzAnnotation == null) {
throw new ModelException("Field " + clazz.getSimpleName() + "::" + f.getName() + " is missing the " + Clazz.class.getSimpleName() + " annotation");
}
var fClazz = clazzAnnotation.value();
if (!SerializableObject.class.isAssignableFrom(fClazz)) {
throw new ModelException("Field " + clazz.getSimpleName() + "::" + f.getName() + " has a class in its " + Clazz.class.getSimpleName() + " annotation that does not inherit from " + SerializableObject.class.getSimpleName() + ": " + fClazz.getSimpleName());
}
var foundCollection = entity.fields().stream()
.filter(e -> Collection.class.isAssignableFrom(e.getField().getType())
&& e.getField().isModelField()
&& e.getField().getField().getAnnotationsByType(Clazz.class).length > 0
&& e.getField().getField().getAnnotationsByType(Clazz.class)[0].value() == fClazz)
.findFirst();
if (foundCollection.isPresent()) {
throw new ModelException("Model " + entity.className() + " contains multiple 1 to N relations with type " + fClazz.getSimpleName());
}
entity.field(f);
initEntity(mb, (Class<? extends SerializableObject>) fClazz, fClazz.getSimpleName(), false);
} else if (SerializableObject.class.isAssignableFrom(f.getType())) {
entity.field(f);
initEntity(mb, (Class<? extends SerializableObject>) f.getType(), f.getType().getSimpleName(), false);
} else {
var dbField = entity.field(f);
if (f.getType().isPrimitive()) {
dbField.isNotNull();
}
}
}
}
}

View File

@@ -1,20 +0,0 @@
package jef.model;
import jef.model.constraints.ForeignKeyConstraint;
import jef.serializable.SerializableObject;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class ForeignKeyBuilder<T extends SerializableObject, R extends SerializableObject> {
private final ForeignKeyConstraint foreignKey;
public ForeignKeyBuilder<T, R> onUpdate(ForeignKeyConstraint.Action onUpdate) {
foreignKey.setOnUpdate(onUpdate);
return this;
}
public ForeignKeyBuilder<T, R> onDelete(ForeignKeyConstraint.Action onDelete) {
foreignKey.setOnDelete(onDelete);
return this;
}
}

View File

@@ -1,56 +0,0 @@
package jef.model;
import jef.asm.OptimizedAsmParser;
import jef.model.annotations.ForeignKey;
import jef.serializable.SerializableObject;
import jef.util.Util;
class ForeignKeyExposeInitializer {
static void initForeignKeyExposures(ModelBuilder mb) {
var entities = mb.entities();
for (int i = 0; i < entities.size(); i++) {
var entity = entities.get(i);
initForeignKeyExposures(mb, entity);
}
}
static void initForeignKeyExposures(ModelBuilder mb, DbEntityBuilder<?> entityBuilder) {
var fields = entityBuilder.fields();
for (var f : fields) {
if (SerializableObject.class.isAssignableFrom(f.getField().getType())) {
var idFields = fields.stream()
.filter(e -> {
//ignore self
if (e.getField().getName().equals(f.getField().getName())) {
return false;
}
//match with name in annotation
var anno = e.getField().getField().getAnnotation(ForeignKey.class);
if (anno == null) {
return false;
}
if (anno.entity() != SerializableObject.class) { // not a foreign key exposure, ignore
return false;
}
var fieldname = anno.getterOrField();
//if getter, extract getter field
var method = Util.tryGet(() -> entityBuilder.type().orElseThrow().getMethod(anno.getterOrField()));
if (method.isPresent()) {
var res = new OptimizedAsmParser(method.get()).parse();
fieldname = res.getAccessedFields().stream().findFirst().orElseThrow().getName();
}
if (!SerializableObject.class.isAssignableFrom(entityBuilder.getEntity().getField(fieldname).getType())) {
throw new ModelException("The getter/field in " + entityBuilder.className() + "::" + f.getField().getField().getName() + " is not a " + SerializableObject.class.getSimpleName() + " (via @ForeignKey in TestClass::testFk)");
}
return f.getField().getName().equals(fieldname);
})
.toList();
idFields.forEach(e -> {
e.getField().setExposingForeignKeyOf(f.getField());
e.getField().setDatabaseField(idFields.stream().findFirst().orElseThrow() == e);
});
}
}
}
}

View File

@@ -1,196 +0,0 @@
package jef.model;
import jef.model.annotations.Clazz;
import jef.model.annotations.ForeignKey;
import jef.model.annotations.Transient;
import jef.serializable.SerializableObject;
import jef.util.Log;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Locale;
class ForeignKeyInitializer {
static void initForeignKeys(ModelBuilder mb) {
var entities = mb.entities();
for (int i = 0; i < entities.size(); i++) {
var entity = entities.get(i);
initForeignKeys(mb, entity);
}
}
static void initForeignKeys(ModelBuilder mb, DbEntityBuilder<?> entityBuilder) {
var entity = entityBuilder.getEntity();
var fields = ReflectionUtil.getFieldsRecursive(entityBuilder.type().orElseThrow(() -> new IllegalStateException("Class not found: " + entityBuilder.typeName())));
for (var f : fields) {
if (f.getAnnotationsByType(Transient.class).length > 0) {
continue;
}
if (Collection.class.isAssignableFrom(f.getType())) {
do1toN(mb, entityBuilder, entity, f);
} else if (SerializableObject.class.isAssignableFrom(f.getType())) {//1 to 1 relation
do1to1(mb, entityBuilder, entity, f);
}
}
}
private static void do1toN(ModelBuilder mb, DbEntityBuilder<?> entityBuilder, DbEntity<?> entity, Field f) {
Clazz clazzAnnotation = f.getAnnotation(Clazz.class);
if (clazzAnnotation == null) {
throw new ModelException("Collection " + entityBuilder.className() + "." + f.getName() + " is missing the " + Clazz.class.getSimpleName() + " annotation");
}
var otherEntity = mb.getEntity((Class<? extends SerializableObject>) clazzAnnotation.value());
if (otherEntity == null) {
//all types must be initialized by EntityInitializer
throw new IllegalStateException(ForeignKeyInitializer.class.getSimpleName() + ": Entity of type " + clazzAnnotation.value() + " does not exist");
// EntityInitializer.initEntity(mb, (Class<? extends SerializableObject>) clazzAnnotation.value(), f.getName());
// otherEntity = mb.getEntity((Class<? extends SerializableObject>) clazzAnnotation.value());
// PrimaryKeyInitializer.initPrimaryKeys(mb, otherEntity.builder(mb));
}
var primary = entityBuilder.getEntity().getPrimaryKey();
if (primary == null) {
throw new ModelException("Entity " + entityBuilder.className() + " is missing a primary key and therefore cannot be referenced by " + otherEntity.builder(mb).className());
}
var otherEntityF = otherEntity;
var otherFields = primary.getFields().stream()
.map(e -> {
//find list<object> in other entity (N to N relation)
if (otherEntityF != entity) { //ignore on recursive models
var otherEntityListField = otherEntityF.getFields().stream()
.filter(oef -> Collection.class.isAssignableFrom(oef.getType())
&& oef.getField().getAnnotationsByType(Clazz.class).length > 0
&& oef.getField().getAnnotationsByType(Clazz.class)[0].value().getName().equals(entity.getTypeName())).findFirst();
if (otherEntityListField.isPresent()) {
throw new ModelException("N to N relations need to explicitly defined via a mapping model ("
+ otherEntityF.builder(mb).className() + "::" + otherEntityListField.get().getTypeName() + " and "
+ entityBuilder.className() + "::" + f.getType().getName() + ")");
}
}
//find object in other entity (1 to N relation)
var otherEntityObjectField = otherEntityF.getFields().stream().filter(oef -> oef.getTypeName().equals(entity.getTypeName())).findFirst();
if (otherEntityObjectField.isPresent()) {
return new FieldSearchResult(otherEntityObjectField.get(), false);
}
//find objectId in other entity (1 to N relation)
// var idFieldName = entity.getType().getSimpleName().substring(0, 1).toLowerCase(Locale.ROOT)
// + entity.getType().getSimpleName().substring(1)
// + e.getName().substring(0, 1).toUpperCase(Locale.ROOT)
// + e.getName().substring(1);
var idFieldName = f.getName()
+ e.getName().substring(0, 1).toUpperCase(Locale.ROOT)
+ e.getName().substring(1);
var otherEntityIdField = otherEntityF.getFields().stream().filter(oef -> oef.getName().equals(idFieldName)).findFirst();
if (otherEntityIdField.isPresent()) {
return new FieldSearchResult(otherEntityIdField.get(), otherEntityIdField.get().getField().getAnnotationsByType(ForeignKey.class).length == 0);
}
// //add shadow field
// var entityField = new DbField<>(otherEntityF, f.getDeclaringClass(), null, f.getName());
// entityField.setNotNull(true);
// entityField = otherEntityF.addIfAbsent(entityField);
//add shadow id field
var entityIdField = new DbField<>(otherEntityF, entityBuilder.type().orElseThrow(), null, idFieldName);
entityIdField.setNotNull(true);
entityIdField.setDatabaseField(true);
entityIdField = otherEntityF.addIfAbsent(entityIdField);
// if (entityIdField.getExposingForeignKeyOf() == null) {
// entityIdField.setExposingForeignKeyOf(entityField);
// }
return new FieldSearchResult(entityIdField, true);
})
.toList();
if (otherFields.stream().anyMatch(FieldSearchResult::isCreateForeignKey)) {
otherEntity.builder(mb)
.hasOne(otherFields.stream().map(e -> e.getField().getName()).toArray(String[]::new))
.withMany(entity.getTypeName(), primary.getFields().stream().map(DbField::getName).toArray(String[]::new));
// otherEntity.addForeignKey(new ForeignKeyConstraint(otherEntity, (List) otherFields.stream().map(FieldSearchResult::getField).toList(),
// entity, primary.getFields(), ForeignKeyConstraint.Action.RESTRICT, ForeignKeyConstraint.Action.CASCADE));
}
}
private static void do1to1(ModelBuilder mb, DbEntityBuilder<?> entityBuilder, DbEntity<?> entity, Field f) {
Log.debug("Initializing foreign key for " + entityBuilder.className() + "::" + f.getName());
var otherEntity = mb.getEntity((Class<? extends SerializableObject>) f.getType());
if (otherEntity == null) {
//all types must be initialized by EntityInitializer
throw new IllegalStateException(ForeignKeyInitializer.class.getSimpleName() + ": Entity of type " + f.getType().getName() + " does not exist");
// EntityInitializer.initEntity(mb, (Class<? extends SerializableObject>) f.getType(), f.getName());
// otherEntity = mb.getEntity((Class<? extends SerializableObject>) f.getType());
// PrimaryKeyInitializer.initPrimaryKeys(mb, otherEntity.builder(mb));
}
var primary = otherEntity.getPrimaryKey();
if (primary == null) {
throw new ModelException("Entity " + otherEntity.builder(mb).className() + " is missing a primary key and therefore cannot be referenced by " + entityBuilder.className());
}
var otherEntityF = otherEntity;
var otherFields = primary.getFields().stream()
.map(otherPrimaryField -> {
// //find list<object> in other entity (N to 1 relation)
// var entityListField = entity.getFields().stream()
// .filter(oef -> Collection.class.isAssignableFrom(oef.getType())
// && oef.getField().getAnnotationsByType(Clazz.class).length > 0
// && oef.getField().getAnnotationsByType(Clazz.class)[0].value().getName().equals(otherEntityF.getTypeName())).toList();
//// if (entityListField.size() == 1) {
//// return new FieldSearchResult(entityListField.get(0), true);
//// }
//find object in other entity (1 to 1 relation)
// var entityObjectFields = entity.getFields().stream().filter(oef -> oef.getTypeName().equals(otherEntityF.getTypeName())).toList();
// if (entityObjectField.isPresent()) {
// return new FieldSearchResult(entityObjectField.get(), false);
// }
//find objectId in other entity (1 to 1 relation)
//search for exposing foreign key field first
var entityIdField = entity.getFields().stream()
.filter(oef -> oef.getExposingForeignKeyOf() != null && oef.getExposingForeignKeyOf().getField().equals(f))
.findFirst();
//search for field with name object field name + primary field name
var idFieldName = f.getName()
+ otherPrimaryField.getName().substring(0, 1).toUpperCase(Locale.ROOT)
+ otherPrimaryField.getName().substring(1);
if (!entityIdField.isPresent()) {
entityIdField = entity.getFields().stream().filter(oef -> oef.getName().equals(idFieldName)).findFirst();
}
if (entityIdField.isPresent()) {
return new FieldSearchResult(entityIdField.get(), true);//entityIdField.get().getField().getAnnotationsByType(ForeignKey.class).length == 0);
}
var field = new DbField<>(entity, otherPrimaryField.getType(), null, idFieldName);
field.setExposingForeignKeyOf(entity.getField(f.getName()));
field = entity.addIfAbsent(field);
// if (entityListField.size() == 1) {
//// field.setForeignKeyObjectField(entityListField.get(0));
//// entityListField.get(0).setForeignKeyObjectField(field);
// } else
// if (entityObjectFields.size() == 1) {
// field.setExposingForeignKeyOf(entityObjectFields.get(0));
// entityObjectFields.get(0).setForeignKeyObjectField(field);
// }
return new FieldSearchResult(field, true);
// return new DbField<>(entity, e.getType(), null, f.getName() + e.getName().substring(0, 1).toUpperCase(Locale.ROOT) + e.getName().substring(1))
})
.toList();
if (otherFields.stream().anyMatch(FieldSearchResult::isCreateForeignKey)) {
entityBuilder
.hasOne(otherFields.stream().map(e -> e.getField().getName()).toArray(String[]::new))
.withOne(otherEntity.getTypeName(), primary.getFields().stream().map(DbField::getName).toArray(String[]::new));
// entity.addForeignKey(new ForeignKeyConstraint(entity, (List) otherFields.stream().map(FieldSearchResult::getField).toList(),
// otherEntity, primary.getFields(), ForeignKeyConstraint.Action.RESTRICT, ForeignKeyConstraint.Action.CASCADE));
}
}
@AllArgsConstructor
@Getter
private static class FieldSearchResult {
private final DbField<?> field;
private final boolean createForeignKey;
}
}

View File

@@ -1,186 +0,0 @@
package jef.model;
import jef.asm.AsmParser;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint;
import jef.model.constraints.PrimaryKeyConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializableObject;
import jef.util.Check;
import lombok.RequiredArgsConstructor;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.IntStream;
@RequiredArgsConstructor
public class KeyBuilder<T extends SerializableObject> {
private final ModelBuilder modelBuilder;
private final DbEntity<T> entity;
private final List<DbField<?>> fields;
public <R extends SerializableObject> ForeignKeyBuilder<T, R> withOne(SerializableFunction<T, R> getter) {
Check.notNull(getter, "getter");
var set = new AsmParser(getter).parse().getAccessedFields();
// if (set.isEmpty()) throw new IllegalArgumentException("no fields found");
// else if (set.size() > 1) throw new IllegalArgumentException("more than 1 field found");
// var refFields = entity.getFields().stream().filter(e -> set.contains(e.getField())).toList();
// var refEntities = refFields.stream().map(e -> modelBuilder.getEntity(e.getTypeName())).toList();
// var existingFk = entity.getForeignKeys().stream().filter(fk -> fk.getEntity() == this.entity
// && fk.getFields().equals(this.fields)
// && fk.getReferencedEntity() == refEntities.get(0)
// && fk.getReferencedFields().equals(refFields))
// .findFirst().orElse(null);
// if (existingFk == null) {
// existingFk = new ForeignKeyConstraint(this.entity, this.fields, refEntities.get(0), refFields, ForeignKeyConstraint.Action.RESTRICT, ForeignKeyConstraint.Action.CASCADE);
// this.entity.addForeignKey(existingFk);
// }
// return new ForeignKeyBuilder<>(existingFk);
// var rClassName = getter.getClass().
return withOne("", set.stream().map(Field::getName).toArray(String[]::new));
}
public <R extends SerializableObject> ForeignKeyBuilder<T, R> withOne(String referencedClass, String... getterOrFieldNames) {
Check.notNull(getterOrFieldNames, "getterOrFieldNames");
// var set = new HashSet<>(Arrays.asList(getterOrFieldNames));
// if (set.isEmpty()) throw new IllegalArgumentException("no fields found");
// else if (set.size() > 1) throw new IllegalArgumentException("more than 1 field found");
// var refFields = entity.getFields().stream().filter(e -> set.contains(e.getName())).toList();
// var refEntities = refFields.stream().map(e -> modelBuilder.getEntity(e.getTypeName())).toList();
var refEntities = List.of(modelBuilder.getEntity(referencedClass));
var refFields = IntStream.range(0, getterOrFieldNames.length)
.mapToObj(i -> {
// var field = fields.get(i);
// if (field.getExposingForeignKeyOf() != null) {
// field = field.getExposingForeignKeyOf();
// }
return refEntities.get(0).getField(getterOrFieldNames[i]);
})
.toList();
// var refEntities = refFields.stream().map(e -> e.getEntity()).toList();
var existingFk = entity.getForeignKeys().stream().filter(fk -> fk.getEntity() == this.entity
&& fk.getFields().equals(this.fields)
&& fk.getReferencedEntity() == refEntities.get(0)
&& fk.getReferencedFields().equals(refFields))
.findFirst().orElse(null);
if (existingFk == null) {
existingFk = new ForeignKeyConstraint(this.entity, this.fields, refEntities.get(0), (List) refFields, ForeignKeyConstraint.Action.RESTRICT, ForeignKeyConstraint.Action.CASCADE);
this.entity.addForeignKey(existingFk);
}
return new ForeignKeyBuilder<>(existingFk);
}
public <R extends SerializableObject> ForeignKeyBuilder<T, R> withMany(SerializableFunction<T, R> getter) {
Check.notNull(getter, "getter");
var set = new AsmParser(getter).parse().getAccessedFields();
// if (set.isEmpty()) throw new IllegalArgumentException("no fields found");
// else if (set.size() > 1) throw new IllegalArgumentException("more than 1 field found");
// var refFields = entity.getFields().stream().filter(e -> set.contains(e.getField())).toList();
// var refEntities = refFields.stream().map(e -> modelBuilder.getEntity(e.getTypeName())).toList();
// var existingFk = entity.getForeignKeys().stream().filter(fk -> fk.getEntity() == this.entity
// && fk.getFields().equals(this.fields)
// && fk.getReferencedEntity() == refEntities.get(0)
// && fk.getReferencedFields().equals(refFields))
// .findFirst().orElse(null);
// if (existingFk == null) {
// existingFk = new ForeignKeyConstraint(this.entity, this.fields, refEntities.get(0), refFields, ForeignKeyConstraint.Action.RESTRICT, ForeignKeyConstraint.Action.CASCADE);
// this.entity.addForeignKey(existingFk);
// }
// return new ForeignKeyBuilder<>(existingFk);
return withMany("", set.stream().map(Field::getName).toArray(String[]::new));
}
public <R extends SerializableObject> ForeignKeyBuilder<T, R> withMany(String referencedClass, String... getterOrFieldNames) {
Check.notNull(getterOrFieldNames, "getterOrFieldNames");
// var set = new HashSet<>(Arrays.asList(getterOrFieldNames));
// var refFields = entity.getFields().stream().filter(e -> set.contains(e.getName())).toList();
// var refEntities = refFields.stream().map(e -> modelBuilder.getEntity(e.getTypeName())).toList();
var refEntities = List.of(modelBuilder.getEntity(referencedClass));
var refFields = IntStream.range(0, getterOrFieldNames.length)
.mapToObj(i -> {
// var field = fields.get(i);
// if (field.getExposingForeignKeyOf() != null) {
// field = field.getExposingForeignKeyOf();
// }
// var type = (Class<? extends SerializableObject>) field.getType();
// return modelBuilder.getEntity(type).getField(getterOrFieldNames[i]);
return refEntities.get(0).getField(getterOrFieldNames[i]);
})
.toList();
// var refEntities = refFields.stream().map(e -> e.getEntity()).toList();
var existingFk = entity.getForeignKeys().stream().filter(fk -> fk.getEntity() == this.entity
&& fk.getFields().equals(this.fields)
&& fk.getReferencedEntity() == refEntities.get(0)
&& fk.getReferencedFields().equals(refFields))
.findFirst().orElse(null);
if (existingFk == null) {
existingFk = new ForeignKeyConstraint(this.entity, this.fields, refEntities.get(0), (List) refFields, ForeignKeyConstraint.Action.RESTRICT, ForeignKeyConstraint.Action.CASCADE);
this.entity.addForeignKey(existingFk);
}
return new ForeignKeyBuilder<>(existingFk);
}
public KeyBuilder<T> notNull(boolean notNull) {
//TODO
return this;
}
public void isUnique() {
isUnique(true);
}
public void isUnique(boolean unique) {
var key = new UniqueKeyConstraint(entity, fields);
if (unique) {
entity.addUniqueKey(key);
} else {
entity.dropUniqueKey(key);
}
}
public void isPrimaryKey() {
isPrimaryKey(true);
}
public void isPrimaryKey(boolean primary) {
if (primary) {
entity.setPrimaryKey(new PrimaryKeyConstraint(entity, fields));
} else {
entity.setPrimaryKey(null);
//drop referencing foreign keys
modelBuilder.entities().forEach(e -> {
var remove = e.getEntity().getForeignKeys().stream().filter(fk -> fk.getReferencedEntity() == this.entity).toList();
remove.forEach(fk -> e.getEntity().dropForeignKey(fk));
});
}
}
public void isKey() {
isKey(true);
}
public void isKey(boolean key) {
var k = new KeyConstraint(entity, fields);
if (key) {
entity.addKey(k);
} else {
entity.dropKey(k);
}
}
public void isIndex() {
isIndex(true);
}
public void isIndex(boolean index) {
var i = new IndexConstraint(entity, fields);
if (index) {
entity.addIndex(i);
} else {
entity.dropIndex(i);
}
}
}

View File

@@ -1,37 +0,0 @@
package jef.model;
import jef.model.annotations.processors.AnnotationProcessor;
import jef.model.annotations.processors.ForeignKeyProcessor;
import jef.model.annotations.processors.GeneratedProcessor;
import jef.model.annotations.processors.IndexProcessor;
import jef.model.annotations.processors.KeyProcessor;
import jef.model.annotations.processors.NotNullProcessor;
import jef.model.annotations.processors.UniqueProcessor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Builder
@AllArgsConstructor
public class ModelBuilderOptions {
private List<AnnotationProcessor> annotationProcessors;
private DbContextOptions contextOptions;
public ModelBuilderOptions() {
this.annotationProcessors = new ArrayList<>(List.of(
NotNullProcessor.INSTANCE,
UniqueProcessor.INSTANCE,
IndexProcessor.INSTANCE,
KeyProcessor.INSTANCE,
ForeignKeyProcessor.INSTANCE,
GeneratedProcessor.INSTANCE
));
this.contextOptions = new DbContextOptions(null);//TODO
}
}

View File

@@ -1,21 +0,0 @@
package jef.model.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Generated {
Type value() default Type.IDENTITY;
enum Type {
NONE,//no value is generated
IDENTITY,//value is generated on insert
// COMPUTED,//value is generated on insert and update
;
}
}

View File

@@ -1,27 +0,0 @@
package jef.model.annotations.processors;
import jef.model.DbEntityBuilder;
import jef.model.DbFieldBuilder;
import jef.model.ModelBuilder;
import jef.model.annotations.Generated;
import jef.serializable.SerializableObject;
public class GeneratedProcessor implements AnnotationProcessor {
public static final GeneratedProcessor INSTANCE = new GeneratedProcessor();
@Override
public void apply(ModelBuilder mb) {
for (DbEntityBuilder<? extends SerializableObject> entity : mb.entities()) {
for (DbFieldBuilder<?> field : entity.fields()) {
if (field.getField().getField() == null) {
continue;
}
var annotation = field.getField().getField().getAnnotation(Generated.class);
if (annotation == null) {
continue;
}
field.generated(annotation.value());
}
}
}
}

View File

@@ -1,14 +0,0 @@
package jef.platform;
import jef.platform.base.SqlTypeMapper;
import jef.platform.base.DatabaseOptions;
import jef.platform.base.migration.MigrationApplier;
import jef.platform.base.migration.MigrationOperationTranslator;
import java.sql.Connection;
public interface SqlPlatform {
MigrationOperationTranslator getTranslator();
SqlTypeMapper getTypeMapper();
MigrationApplier getMigrationApplier(Connection connection, DatabaseOptions options);
}

View File

@@ -1,25 +0,0 @@
package jef.platform.base;
import jef.MigrationException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Connection;
@Getter
@AllArgsConstructor
public abstract class Database {
// private final DatabaseConfiguration config;
protected final Connection connection;
protected final DatabaseOptions options;
public abstract void migrate() throws MigrationException;
// public ResultSet executeRaw(String s) throws SQLException {
// try (var stmt = connection.prepareStatement(s)) {
// try (var res = stmt.executeQuery()) {
// return res;
// }
// }
// }
}

View File

@@ -1,48 +0,0 @@
package jef.platform.base;
import lombok.Getter;
import java.net.MalformedURLException;
import java.util.regex.Pattern;
@Getter
public class DatabaseOptions {
protected final String url;
protected final String user;
protected final String password;
protected final String migrationsPackage;
protected final String migrationsTableName;//TODO verify sql injection
protected final String host;//TODO get rid of this
protected final String database;//TODO get rid of this
public DatabaseOptions(String url, String user, String password, String migrationsPackage, String migrationsTableName) {
this.url = url;
this.user = user;
this.password = password;
host = extractHost(url);
database = extractDatabase(url);
this.migrationsPackage = migrationsPackage;
this.migrationsTableName = migrationsTableName == null || migrationsTableName.isBlank() ? "__jef_migration_log" : migrationsTableName;
}
private static String extractHost(String url) {
var pattern = Pattern.compile("^.*?//(.*?)/.*$");
var matcher = pattern.matcher(url);
if (!matcher.matches()) {
throw new RuntimeException(new MalformedURLException("Could not extract host for url: " + url));
}
return matcher.group(1);
}
private static String extractDatabase(String url) {
var pattern = Pattern.compile("^.*?//.*?/(.*?)([?/].*)?$");
var matcher = pattern.matcher(url);
if (!matcher.matches()) {
throw new RuntimeException(new MalformedURLException("Could not extract database for url: " + url));
}
return matcher.group(1);
}
}

View File

@@ -1,21 +0,0 @@
package jef.platform.base;
import java.util.Optional;
public class SqlTypeMapper {
public Optional<String> map(String typeName) {
if (typeName == null) {
return Optional.empty();
}
return Optional.ofNullable(switch (typeName) {
case "java.lang.String" -> "VARCHAR(255)";//TODO add length and precision as param
case "int" -> "INT(11)";
case "float" -> "FLOAT";
case "double" -> "DOUBLE";
case "boolean" -> "INT(11)";
case "short" -> "INT(11)";
case "long" -> "BIGINT";
default -> null;
});
}
}

View File

@@ -1,32 +0,0 @@
package jef.platform.base.migration;
import jef.model.migration.Migration;
import jef.model.migration.MigrationBuilder;
import jef.model.migration.operation.AddFieldOperation;
import jef.platform.base.SqlTypeMapper;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import java.util.List;
@AllArgsConstructor
@EqualsAndHashCode//needed for test
public class M00010101000000_MigrationsLogTableMigration implements Migration {
private final SqlTypeMapper typeMapper;
private final String tableName;
@Override
public void up(MigrationBuilder migrationBuilder) {//TODO add timestamp later
migrationBuilder.addTable(tableName, List.of(
new AddFieldOperation.Builder(tableName, "migration").notNull(true).sqlType(typeMapper.map(String.class.getName()).orElseThrow()),
new AddFieldOperation.Builder(tableName, "version").notNull(true).sqlType(typeMapper.map(String.class.getName()).orElseThrow())
), null);
migrationBuilder.addUniqueKey("U_" + tableName + "_migration", tableName, List.of("migration"));
}
@Override
public void down(MigrationBuilder migrationBuilder) {
migrationBuilder.dropConstraint("U_" + tableName + "_migration", tableName);
migrationBuilder.dropTable(tableName);
}
}

View File

@@ -1,131 +0,0 @@
package jef.platform.base.migration;
import jef.MigrationException;
import jef.model.migration.Migration;
import jef.model.migration.MigrationBuilder;
import jef.model.migration.operation.MigrationOperation;
import jef.platform.SqlPlatform;
import jef.platform.base.DatabaseOptions;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@AllArgsConstructor
@Getter
public abstract class MigrationApplier {
protected final Connection connection;
protected final DatabaseOptions options;
protected final SqlPlatform sqlPlatform;
public void migrate() throws MigrationException {
var migrations = new ArrayList<>(findMigrations(options.getMigrationsPackage())); //TODO find all migrations, support multiple db contexts
try {
ensureMigrationsTableExists();
getAppliedMigrations().forEach(a -> migrations.removeIf(m -> m.getClass().getSimpleName().equals(a)));
for (Migration migration : migrations) {
applyMigration(migration);
}
} catch (SQLException e) {
throw new MigrationException("Error while applying migrations: " + e.getLocalizedMessage(), e);
}
}
protected void applyMigration(Migration m) throws MigrationException {//TODO verify transactions work
var mb = new MigrationBuilder();
m.up(mb);
var operations = mb.getOperations().stream().map(MigrationOperation.Builder::build).toList();
try {
connection.setAutoCommit(false);
try {
for (MigrationOperation op : operations) {
applyMigrationOperation(op);
}
insertMigrationLog(m);
connection.commit();
} catch (SQLException e) {
connection.rollback();
throw e;
} finally {
connection.setAutoCommit(true);
}
} catch (SQLException e) {
throw new MigrationException("Failed to apply migration '" + m.getClass().getSimpleName() + "' to the database: " + e.getLocalizedMessage(), e);
}
}
protected void applyMigrationOperation(MigrationOperation operation) throws SQLException {
PreparedStatement stmt;
try {
stmt = sqlPlatform.getTranslator().translate(connection, operation);//TODO may return an object containing the prep stmt and a string rep of the query
} catch (SQLException e) {
throw new SQLException("Failed to build query: " + e.getLocalizedMessage(), e);
}
try {
stmt.execute();
} catch (SQLException e) {
throw new SQLException("Failed to execute query: " + stmt, e);
} finally {
try {
stmt.close();
} catch (SQLException ignored) {
}
}
}
protected List<Migration> findMigrations(String packageName) throws MigrationException {//TODO rewrite this function after adding annotations to migrations (@Context, @Name, ...), also get rid of the packageName restristrict and just read the classfiles with asm
try (var is = getClass().getClassLoader().getResourceAsStream(packageName.replace(".", "/"));
var reader = new BufferedReader(new InputStreamReader(is))) {
return reader.lines()
.filter(line -> line.endsWith(".class"))
.map(line -> line.substring(0, line.length() - ".class".length()))
.map(line -> {
try {
var cls = getClass().getClassLoader().loadClass(packageName + (packageName.isEmpty() ? "" : ".") + line);
if (!Migration.class.isAssignableFrom(cls)) {
return null;
}
return (Migration) cls.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
} catch (Exception e) {
if (e instanceof RuntimeException re) {
e = re;
}
throw new MigrationException("Error while looking up migrations in package '" + packageName + "'", e);
}
}
protected void ensureMigrationsTableExists() throws MigrationException {
try {
if (!migrationsTableExists()) {
createMigrationsTable();
}
} catch (SQLException e) {
throw new MigrationException("Failed to create migrations log table", e);
}
}
protected abstract boolean migrationsTableExists() throws SQLException;
protected void createMigrationsTable() throws MigrationException {
applyMigration(new M00010101000000_MigrationsLogTableMigration(sqlPlatform.getTypeMapper(), options.getMigrationsTableName()));
}
protected abstract List<String> getAppliedMigrations() throws SQLException;//TODO dbset to load later
protected abstract void insertMigrationLog(Migration m) throws SQLException;//TODO dbset to insert later
}

View File

@@ -1,11 +0,0 @@
package jef.platform.base.migration;
import jef.model.migration.operation.MigrationOperation;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public interface MigrationOperationTranslator {
PreparedStatement translate(Connection connection, MigrationOperation op) throws SQLException;
}

View File

@@ -1,14 +0,0 @@
package jef.platform.dummy;
import jef.MigrationException;
import jef.platform.base.Database;
public class DummyDatabase extends Database {
public DummyDatabase() {
super(null, null);
}
@Override
public void migrate() throws MigrationException {
}
}

View File

@@ -1,30 +0,0 @@
package jef.platform.dummy;
import jef.platform.SqlPlatform;
import jef.platform.base.DatabaseOptions;
import jef.platform.base.SqlTypeMapper;
import jef.platform.base.migration.MigrationApplier;
import jef.platform.base.migration.MigrationOperationTranslator;
import jef.platform.dummy.migration.DummyMigrationApplier;
import jef.platform.dummy.migration.DummyMigrationOperationTranslator;
import java.sql.Connection;
public class DummyPlatform implements SqlPlatform {
public static final DummyPlatform INSTANCE = new DummyPlatform();
@Override
public MigrationOperationTranslator getTranslator() {
return DummyMigrationOperationTranslator.INSTANCE;
}
@Override
public SqlTypeMapper getTypeMapper() {
return DummyTypeMapper.INSTANCE;
}
@Override
public MigrationApplier getMigrationApplier(Connection connection, DatabaseOptions options) {
return DummyMigrationApplier.INSTANCE;
}
}

View File

@@ -1,17 +0,0 @@
package jef.platform.dummy;
import jef.platform.base.SqlTypeMapper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.Optional;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DummyTypeMapper extends SqlTypeMapper {
public static final DummyTypeMapper INSTANCE = new DummyTypeMapper();
@Override
public Optional<String> map(String typeName) {
return Optional.ofNullable(typeName);
}
}

View File

@@ -1,43 +0,0 @@
package jef.platform.dummy.migration;
import jef.MigrationException;
import jef.model.migration.Migration;
import jef.platform.base.migration.MigrationApplier;
import java.sql.SQLException;
import java.util.List;
public class DummyMigrationApplier extends MigrationApplier {
public static final DummyMigrationApplier INSTANCE = new DummyMigrationApplier();
private DummyMigrationApplier() {
super(null, null, null);
}
@Override
public void migrate() throws MigrationException {
}
@Override
protected void applyMigration(Migration m) throws MigrationException {
}
@Override
protected boolean migrationsTableExists() throws SQLException {
return false;
}
@Override
protected void createMigrationsTable() throws MigrationException {
}
@Override
protected List<String> getAppliedMigrations() throws SQLException {
return List.of();
}
@Override
protected void insertMigrationLog(Migration m) throws SQLException {
}
}

View File

@@ -1,20 +0,0 @@
package jef.platform.dummy.migration;
import jef.model.migration.operation.MigrationOperation;
import jef.platform.base.migration.MigrationOperationTranslator;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DummyMigrationOperationTranslator implements MigrationOperationTranslator {
public static final DummyMigrationOperationTranslator INSTANCE = new DummyMigrationOperationTranslator();
@Override
public PreparedStatement translate(Connection connection, MigrationOperation op) throws SQLException {
return null;
}
}

View File

@@ -1,11 +0,0 @@
package jef.util;
public class Log {
public static void debug(String s) {
System.out.println(s);//TODO implement logging
}
private Log() {
}
}

View File

@@ -1,47 +0,0 @@
package jef.util;
import java.util.Optional;
public abstract class Util {
public static <T> Optional<T> tryGet(ThrowableSupplier<T> s) {
try {
return Optional.ofNullable(s.get());
} catch (Throwable t) {
return Optional.empty();
}
}
@FunctionalInterface
public interface ThrowableSupplier<T> {
T get() throws Throwable;
}
@FunctionalInterface
public interface ThrowableFunction<T, R> {
R apply(T t) throws Throwable;
}
@FunctionalInterface
public interface ThrowableBiFunction<T, U, R> {
R apply(T t, U u) throws Throwable;
}
public interface Equality<T> {
boolean check(T t1, T t2);
}
public interface NullableEquality<T> {
default boolean check(T t1, T t2) {
if (t1 == t2) {
return true;
}
if (t1 != null) {
return compare(t1, t2);
} else {
return compare(t2, t1);
}
}
boolean compare(/*NonNull*/T t1, T t2);
}
}

View File

@@ -1,39 +0,0 @@
package jef.model;
import jef.model.annotations.Id;
import jef.serializable.SerializableObject;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class EntityDefaultConstructorCheckerTest {
@Test
public void test0ArgConstructor() {
var mb = new ModelBuilder();
EntityDefaultConstructorChecker.checkEntity(mb.entity(TestClass0Arg.class));
assertTrue(true);
}
@Test
public void testNon0ArgConstructor() {
var mb = new ModelBuilder();
var ex = assertThrows(ModelException.class, () -> EntityDefaultConstructorChecker.checkEntity(mb.entity(TestClassNon0Arg.class)));
assertEquals("Class 'TestClassNon0Arg' does not have a default constructor!", ex.getMessage());
}
@NoArgsConstructor
public static class TestClass0Arg extends SerializableObject {
@Id
public int i = 1;
}
@AllArgsConstructor
public static class TestClassNon0Arg extends SerializableObject {
@Id
public int i = 1;
}
}

View File

@@ -1,45 +0,0 @@
package jef.model;
import jef.DbSet;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
class EntityInitializerCollectionAnnotationClassInheritTest {
@Test
public void test() {
assertEquals("Field TestClass2::nested is missing the Clazz annotation",
assertThrowsExactly(ModelException.class, () -> ModelBuilder.from(Ctx.class)).getMessage());
}
public static class Ctx extends DbContext {
@Clazz(TestClass2.class)
private DbSet<TestClass2> objects2;
@Clazz(TestClass.class)
private DbSet<TestClass> objects;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i2 = 1;
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -1,46 +0,0 @@
package jef.model;
import jef.DbSet;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
class EntityInitializerCollectionAnnotationMissingTest {
@Test
public void test() {
assertEquals("Field TestClass2::nested has a class in its Clazz annotation that does not inherit from SerializableObject: int",
assertThrowsExactly(ModelException.class, () -> ModelBuilder.from(Ctx.class)).getMessage());
}
public static class Ctx extends DbContext {
@Clazz(TestClass2.class)
private DbSet<TestClass2> objects2;
@Clazz(TestClass.class)
private DbSet<TestClass> objects;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i2 = 1;
@Clazz(int.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -1,34 +0,0 @@
package jef.model;
import jef.DbSet;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
class EntityInitializerDbSetAnnotationClassInheritTest {
@Test
public void test() {
assertEquals("DbSet objects1 has a class in its Clazz annotation that does not inherit from SerializableObject: int",
assertThrowsExactly(ModelException.class, () -> ModelBuilder.from(Ctx.class)).getMessage());
}
public static class Ctx extends DbContext {
@Clazz(int.class)
private DbSet<TestClass> objects1;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -1,61 +0,0 @@
package jef.model;
import jef.DbSet;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class KeyBuilderPrimaryDropRelatedForeignKeyDropTest {
@Test
public void test() {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntity(TestClass3.class).getForeignKeys().size());
assertEquals(1, mb.getEntity(TestClass2.class).getForeignKeys().size());
assertEquals(0, mb.getEntity(TestClass.class).getForeignKeys().size());
mb.entity(TestClass.class).hasOne(e -> e.i).isPrimaryKey(false);
assertEquals(1, mb.getEntity(TestClass3.class).getForeignKeys().size());
assertEquals(0, mb.getEntity(TestClass2.class).getForeignKeys().size());
assertEquals(0, mb.getEntity(TestClass.class).getForeignKeys().size());
}
public static class Ctx extends DbContext {
@Clazz(TestClass.class)
private DbSet<TestClass> objects1;
@Clazz(TestClass2.class)
private DbSet<TestClass2> objects2;
@Clazz(TestClass3.class)
private DbSet<TestClass2> objects3;
}
@Getter
public static class TestClass3 extends SerializableObject {
@Id
public int i3 = 1;
private TestClass2 nested2;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i2 = 1;
private TestClass nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -1,74 +0,0 @@
package jef.model.annotations.processors;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.ForeignKey;
import jef.model.annotations.Id;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ForeignKeyProcessorExposeViaMethodTest {
@Test
public void test() {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(2, mb.getEntities().size());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertEquals("objects2", mb.getEntity(TestClass2.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass2.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
//fields ------------------------
//TestClass
assertEquals(3, mb.getEntity(TestClass.class).getFields().size());
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("test") && e.isModelField() && !e.isDatabaseField()).count());
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField") && e.isModelField() && e.isDatabaseField()).count());
//TestClass2
assertEquals(1, mb.getEntity(TestClass2.class).getFields().size());
assertEquals(1, mb.getEntity(TestClass2.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
// /fields ------------------------
//keys ------------------------
assertEquals(1, mb.getEntity(TestClass.class).getForeignKeys().size());
assertEquals(mb.getEntity(TestClass.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getEntity());
assertEquals(mb.getEntity(TestClass2.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedEntity());
assertEquals(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField")).toList(),
mb.getEntity(TestClass.class).getForeignKeys().get(0).getFields());
assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedFields());
// /keys ------------------------
//exposing fields
assertEquals(mb.getEntity(TestClass.class).getField("test"), mb.getEntity(TestClass.class).getField("testField").getExposingForeignKeyOf());
}
public static class Ctx extends DbContext {
@Clazz(TestClass.class)
private DbSet<TestClass> objects1;
@Clazz(TestClass2.class)
private DbSet<TestClass2> objects2;
}
@Getter
public static class TestClass extends SerializableObject {
public int i = 1;
private TestClass2 test;
@ForeignKey(getterOrField = "getTest")
public int testField;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
}
}

View File

@@ -1,48 +0,0 @@
package jef.model.annotations.processors;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.ModelException;
import jef.model.annotations.Clazz;
import jef.model.annotations.ForeignKey;
import jef.model.annotations.Id;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
class ForeignKeyProcessorInvalidFieldTypeTest {
@Test
public void test() {
assertEquals("The getter/field in TestClass::test is not a SerializableObject (via @ForeignKey in TestClass::testFk)",
assertThrowsExactly(ModelException.class, () -> ModelBuilder.from(Ctx.class)).getMessage());
}
public static class Ctx extends DbContext {
@Clazz(TestClass.class)
private DbSet<TestClass> objects1;
@Clazz(TestClass2.class)
private DbSet<TestClass2> objects2;
}
@Getter
public static class TestClass extends SerializableObject {
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
private TestClass2 test;
@ForeignKey(getterOrField = "l")
public int testFk;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
}
}

View File

@@ -1,399 +0,0 @@
package jef.platform.base.migration;
import jef.MigrationException;
import jef.model.migration.Migration;
import jef.model.migration.MigrationBuilder;
import jef.model.migration.operation.MigrationOperation;
import jef.platform.SqlPlatform;
import jef.platform.base.DatabaseOptions;
import jef.platform.base.SqlTypeMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.withSettings;
class MigrationApplierTest {
private String packageName;
private DatabaseOptions options;
private Connection connection;
private SqlPlatform platform;
private MigrationApplier applier;
private Object[] allMocks;
@BeforeEach
void setup() {
//setup
packageName = "package";
options = mock(DatabaseOptions.class);
doReturn(packageName).when(options).getMigrationsPackage();
connection = mock(Connection.class);
platform = mock(SqlPlatform.class);
applier = mock(MigrationApplier.class, withSettings().useConstructor(connection, options, platform).defaultAnswer(CALLS_REAL_METHODS)); //spy
allMocks = List.of(options, connection, platform, applier).toArray();
}
@Test
void migrate_success() throws MigrationException, SQLException {
interface Migration1 extends Migration {
}
interface Migration2 extends Migration {
}
var migration1 = mock(Migration1.class);
var migration2 = mock(Migration2.class);
doReturn(List.of(migration1, migration2)).when(applier).findMigrations(packageName);
doNothing().when(applier).ensureMigrationsTableExists();
doReturn(List.of(migration1.getClass().getSimpleName())).when(applier).getAppliedMigrations();
doNothing().when(applier).applyMigration(any(Migration.class));
//test
applier.migrate();
//assert
verify(applier, times(1)).migrate();
verify(options, times(1)).getMigrationsPackage();
verify(applier, times(1)).findMigrations(packageName);
verify(applier, times(1)).ensureMigrationsTableExists();
verify(applier, times(1)).getAppliedMigrations();
verify(applier, times(1)).applyMigration(migration2);
verifyNoMoreInteractions(migration1, migration2);
verifyNoMoreInteractions(allMocks);
}
@Test
void migrate_findMigrationsThrowsMigrationException() throws MigrationException {
//setup
var exception = new MigrationException("reason");
doThrow(exception).when(applier).findMigrations(packageName);
//test
var ex = assertThrows(MigrationException.class, () -> applier.migrate());
//assert
assertSame(exception, ex);
verify(applier, times(1)).migrate();
verify(options, times(1)).getMigrationsPackage();
verify(applier, times(1)).findMigrations(packageName);
verifyNoMoreInteractions(allMocks);
}
@Test
void migrate_ensureMigrationsTableExistsThrowsMigrationException() throws MigrationException {
//setup
doReturn(List.of()).when(applier).findMigrations(packageName);
var exception = new MigrationException("reason");
doThrow(exception).when(applier).ensureMigrationsTableExists();
//test
var ex = assertThrows(MigrationException.class, () -> applier.migrate());
//assert
assertSame(exception, ex);
verify(applier, times(1)).migrate();
verify(options, times(1)).getMigrationsPackage();
verify(applier, times(1)).findMigrations(packageName);
verify(applier, times(1)).ensureMigrationsTableExists();
verifyNoMoreInteractions(allMocks);
}
@Test
void migrate_getAppliedMigrationsThrowsSqlException() throws MigrationException, SQLException {
//setup
doReturn(List.of()).when(applier).findMigrations(packageName);
doNothing().when(applier).ensureMigrationsTableExists();
var exception = new SQLException("reason");
doThrow(exception).when(applier).getAppliedMigrations();
//test
var ex = assertThrows(MigrationException.class, () -> applier.migrate());
//assert
assertEquals("Error while applying migrations: reason", ex.getMessage());
assertSame(exception, ex.getCause());
verify(applier, times(1)).migrate();
verify(options, times(1)).getMigrationsPackage();
verify(applier, times(1)).findMigrations(packageName);
verify(applier, times(1)).ensureMigrationsTableExists();
verify(applier, times(1)).getAppliedMigrations();
verifyNoMoreInteractions(allMocks);
}
@Test
void migrate_applyMigrationThrowsMigrationException() throws MigrationException, SQLException {
//setup
var migration = mock(Migration.class);
doReturn(List.of(migration)).when(applier).findMigrations(packageName);
doNothing().when(applier).ensureMigrationsTableExists();
doReturn(List.of()).when(applier).getAppliedMigrations();
var exception = new MigrationException("reason");
doThrow(exception).when(applier).applyMigration(migration);
//test
var ex = assertThrows(MigrationException.class, () -> applier.migrate());
//assert
assertSame(exception, ex);
verify(applier, times(1)).migrate();
verify(options, times(1)).getMigrationsPackage();
verify(applier, times(1)).findMigrations(packageName);
verify(applier, times(1)).ensureMigrationsTableExists();
verify(applier, times(1)).getAppliedMigrations();
verify(applier, times(1)).applyMigration(migration);
verifyNoMoreInteractions(migration);
verifyNoMoreInteractions(allMocks);
}
@Test
void applyMigration_success() throws SQLException, MigrationException {
//setup
var migration = mock(Migration.class);
var operationBuilder = new MigrationOperation.Builder[]{null};
var operation = new MigrationOperation[]{null};
doAnswer(invok -> {
operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of(), null);
operation[0] = operationBuilder[0].build();
return null;
}).when(migration).up(any(MigrationBuilder.class));
doNothing().when(applier).applyMigrationOperation(argThat(e -> e.equals(operation[0])));//eq(operation[0]) does not work as var would have to be set already and isn't
//test
applier.applyMigration(migration);
//assert
verify(applier, times(1)).applyMigration(migration);
verify(migration, times(1)).up(any(MigrationBuilder.class));
verify(connection, times(1)).setAutoCommit(false);
verify(applier, times(1)).applyMigrationOperation(operation[0]);
verify(applier, times(1)).insertMigrationLog(migration);
verify(connection, times(1)).commit();
verify(connection, times(1)).setAutoCommit(true);
verifyNoMoreInteractions(migration);
verifyNoMoreInteractions(allMocks);
}
@Test
void applyMigration_applyMigrationOperationThrowsSqlException() throws SQLException, MigrationException {
//setup
var migration = mock(Migration.class);
var operationBuilder = new MigrationOperation.Builder[]{null};
var operation = new MigrationOperation[]{null};
doAnswer(invok -> {
operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of(), null);
operation[0] = operationBuilder[0].build();
return null;
}).when(migration).up(any(MigrationBuilder.class));
var sqlException = new SQLException("reason");
doThrow(sqlException).when(applier).applyMigrationOperation(argThat(e -> e.equals(operation[0])));//eq(operation[0]) does not work as var would have to be set already and isn't
//test
var thrown = assertThrows(MigrationException.class, () -> applier.applyMigration(migration));
//assert
assertTrue(thrown.getMessage().matches("Failed to apply migration '(.*?)' to the database: reason"));
assertSame(sqlException, thrown.getCause());
verify(applier, times(1)).applyMigration(migration);
verify(migration, times(1)).up(any(MigrationBuilder.class));
verify(connection, times(1)).setAutoCommit(false);
verify(applier, times(1)).applyMigrationOperation(operation[0]);
verify(connection, times(1)).rollback();
verify(connection, times(1)).setAutoCommit(true);
verifyNoMoreInteractions(migration);
verifyNoMoreInteractions(allMocks);
}
@Test
void applyMigration_insertMigrationLogThrowsSqlException() throws SQLException, MigrationException {
//setup
var migration = mock(Migration.class);
var operationBuilder = new MigrationOperation.Builder[]{null};
var operation = new MigrationOperation[]{null};
doAnswer(invok -> {
operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of(), null);
operation[0] = operationBuilder[0].build();
return null;
}).when(migration).up(any(MigrationBuilder.class));
var sqlException = new SQLException("reason");
doNothing().when(applier).applyMigrationOperation(argThat(e -> e.equals(operation[0])));//eq(operation[0]) does not work as var would have to be set already and isn't
doThrow(sqlException).when(applier).insertMigrationLog(migration);
//test
var thrown = assertThrows(MigrationException.class, () -> applier.applyMigration(migration));
//assert
assertTrue(thrown.getMessage().matches("Failed to apply migration '(.*?)' to the database: reason"));
assertSame(sqlException, thrown.getCause());
verify(applier, times(1)).applyMigration(migration);
verify(migration, times(1)).up(any(MigrationBuilder.class));
verify(connection, times(1)).setAutoCommit(false);
verify(applier, times(1)).applyMigrationOperation(operation[0]);
verify(applier, times(1)).insertMigrationLog(migration);
verify(connection, times(1)).rollback();
verify(connection, times(1)).setAutoCommit(true);
verifyNoMoreInteractions(migration);
verifyNoMoreInteractions(allMocks);
}
@Test
void applyMigrationOperation_success() throws SQLException {
//setup
var translator = mock(MigrationOperationTranslator.class);
doReturn(translator).when(platform).getTranslator();
var operation = mock(MigrationOperation.class);
var stmt = mock(PreparedStatement.class);
doReturn(stmt).when(translator).translate(connection, operation);
doReturn("query").when(stmt).toString();
//test
applier.applyMigrationOperation(operation);
//assert
verify(applier, times(1)).applyMigrationOperation(operation);
verify(platform, times(1)).getTranslator();
verify(translator, times(1)).translate(connection, operation);
verify(stmt, times(1)).execute();
verify(stmt, times(1)).close();
verifyNoMoreInteractions(translator, operation, stmt);
verifyNoMoreInteractions(allMocks);
}
@Test
void applyMigrationOperation_translateThrowsSQLException() throws SQLException {
//setup
var translator = mock(MigrationOperationTranslator.class);
doReturn(translator).when(platform).getTranslator();
var operation = mock(MigrationOperation.class);
var stmt = mock(PreparedStatement.class);
var sqlException = new SQLException("reason");
doThrow(sqlException).when(translator).translate(connection, operation);
//test
var thrown = assertThrows(SQLException.class, () -> applier.applyMigrationOperation(operation));
//assert
assertEquals("Failed to build query: reason", thrown.getMessage());
assertSame(sqlException, thrown.getCause());
verify(applier, times(1)).applyMigrationOperation(operation);
verify(platform, times(1)).getTranslator();
verify(translator, times(1)).translate(connection, operation);
verifyNoMoreInteractions(translator, operation, stmt);
verifyNoMoreInteractions(allMocks);
}
@Test
void applyMigrationOperation_stmtExecuteThrowsSQLException() throws SQLException {
//setup
var translator = mock(MigrationOperationTranslator.class);
doReturn(translator).when(platform).getTranslator();
var operation = mock(MigrationOperation.class);
var stmt = mock(PreparedStatement.class);
doReturn(stmt).when(translator).translate(connection, operation);
doReturn("query").when(stmt).toString();
var sqlException = new SQLException("reason");
doThrow(sqlException).when(stmt).execute();
//test
var thrown = assertThrows(SQLException.class, () -> applier.applyMigrationOperation(operation));
//assert
assertEquals("Failed to execute query: query", thrown.getMessage());
assertSame(sqlException, thrown.getCause());
verify(applier, times(1)).applyMigrationOperation(operation);
verify(platform, times(1)).getTranslator();
verify(translator, times(1)).translate(connection, operation);
verify(stmt, times(1)).execute();
verify(stmt, times(1)).close();
verifyNoMoreInteractions(translator, operation, stmt);
verifyNoMoreInteractions(allMocks);
}
@Test
void findMigrations() {
//setup
//test
//assert
}//TODO after function rewrite
@Test
void createMigrationsTable_success() throws MigrationException {
//setup
var mapper = mock(SqlTypeMapper.class);
doReturn(mapper).when(platform).getTypeMapper();
var migrationTableName = "migrationtable";
doReturn(migrationTableName).when(options).getMigrationsTableName();
doNothing().when(applier).applyMigration(new M00010101000000_MigrationsLogTableMigration(mapper, migrationTableName));
//test
applier.createMigrationsTable();
//assert
verify(applier, times(1)).createMigrationsTable();
verify(platform, times(1)).getTypeMapper();
verify(options, times(1)).getMigrationsTableName();
verify(applier, times(1)).applyMigration(new M00010101000000_MigrationsLogTableMigration(mapper, migrationTableName));
verifyNoMoreInteractions(allMocks);
}
@Test
void createMigrationsTable_applyMigrationThrowsMigrationException() throws MigrationException {
//setup
var mapper = mock(SqlTypeMapper.class);
doReturn(mapper).when(platform).getTypeMapper();
var migrationTableName = "migrationtable";
doReturn(migrationTableName).when(options).getMigrationsTableName();
var exception = new MigrationException("reason");
doThrow(exception).when(applier).applyMigration(new M00010101000000_MigrationsLogTableMigration(mapper, migrationTableName));
//test
var thrown = assertThrows(MigrationException.class, () -> applier.createMigrationsTable());
//assert
assertSame(exception, thrown);
verify(applier, times(1)).createMigrationsTable();
verify(platform, times(1)).getTypeMapper();
verify(options, times(1)).getMigrationsTableName();
verify(applier, times(1)).applyMigration(new M00010101000000_MigrationsLogTableMigration(mapper, migrationTableName));
verifyNoMoreInteractions(allMocks);
}
}

View File

@@ -1,12 +0,0 @@
package jef.platform.dummy;
import jef.MigrationException;
import org.junit.jupiter.api.Test;
class DummyDatabaseTest {
@Test
void migrate() throws MigrationException {
new DummyDatabase().migrate();
}
}

View File

@@ -1,29 +0,0 @@
package jef.platform.dummy;
import jef.platform.dummy.migration.DummyMigrationApplier;
import jef.platform.dummy.migration.DummyMigrationOperationTranslator;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertSame;
class DummyPlatformTest {
@Test
void getTranslator() {
//test
var translator = new DummyPlatform().getTranslator();
//assert
assertSame(DummyMigrationOperationTranslator.INSTANCE, translator);
}
@Test
void getTypeMapper() {
assertSame(DummyTypeMapper.INSTANCE, new DummyPlatform().getTypeMapper());
}
@Test
void getMigrationApplier() {
assertSame(DummyMigrationApplier.INSTANCE, DummyPlatform.INSTANCE.getMigrationApplier(null, null));
}
}

View File

@@ -1,24 +0,0 @@
package jef.platform.dummy;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
class DummyTypeMapperTest {
@Test
void map() {
//setup
var type = UUID.randomUUID().toString();
//test
Optional<String> opt = DummyTypeMapper.INSTANCE.map(type);
//assert
assertTrue(opt.isPresent());
assertSame(type, opt.orElseThrow());
}
}

View File

@@ -1,64 +0,0 @@
package jef.platform.dummy.migration;
import jef.MigrationException;
import jef.model.migration.Migration;
import org.junit.jupiter.api.Test;
import java.sql.SQLException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.mockito.Mockito.mock;
class DummyMigrationApplierTest {
@Test
void migrate() throws MigrationException {
DummyMigrationApplier.INSTANCE.migrate();
}
@Test
void applyMigration() throws MigrationException {
//setup
Migration migration = mock(Migration.class);
//test
DummyMigrationApplier.INSTANCE.applyMigration(migration);
}
@Test
void findMigrations() throws MigrationException {
var foundMethodOverride = new Throwable();
try {
DummyMigrationApplier.class.getDeclaredMethod("findMigrations", String.class);//throws NoSuchMethodException, i.e. not overriden
throw foundMethodOverride;
} catch (Throwable t) {
assertNotSame(foundMethodOverride, t);
}
}
@Test
void migrationsTableExists() throws SQLException {
assertFalse(DummyMigrationApplier.INSTANCE.migrationsTableExists());
}
@Test
void createMigrationsTable() throws MigrationException {
DummyMigrationApplier.INSTANCE.createMigrationsTable();
}
@Test
void getAppliedMigrations() throws SQLException {
assertEquals(0, DummyMigrationApplier.INSTANCE.getAppliedMigrations().size());
}
@Test
void insertMigrationLog() throws SQLException {
//setup
Migration migration = mock(Migration.class);
//test
DummyMigrationApplier.INSTANCE.insertMigrationLog(migration);
}
}

View File

@@ -1,14 +0,0 @@
package jef.platform.dummy.migration;
import org.junit.jupiter.api.Test;
import java.sql.SQLException;
import static org.junit.jupiter.api.Assertions.assertNull;
class DummyMigrationOperationTranslatorTest {
@Test
void translate() throws SQLException {
assertNull(DummyMigrationOperationTranslator.INSTANCE.translate(null, null));
}
}

View File

@@ -1,210 +0,0 @@
package jef.query;
import jef.expressions.AndExpression;
import jef.expressions.BinaryExpression;
import jef.expressions.ConstantExpression;
import jef.expressions.FieldExpression;
import jef.expressions.IntermediateFieldExpression;
import jef.expressions.LimitExpression;
import jef.expressions.NullExpression;
import jef.expressions.OrExpression;
import jef.expressions.OrderExpression;
import jef.expressions.ParameterExpression;
import jef.expressions.SelectExpression;
import jef.expressions.TableExpression;
import jef.expressions.TernaryExpression;
import jef.expressions.UnaryExpression;
import jef.expressions.WhereExpression;
import jef.platform.base.query.QueryBuilder;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
class QueryBuilderTest {
@Test
void visitAnd() {
var v = new QueryBuilder();
v.visitAnd(new AndExpression(ConstantExpression.V0, ConstantExpression.V1));
Assertions.assertEquals("0 AND 1", v.getQuery());
}
@Test
void visitBinary() {
var v = new QueryBuilder();
v.visitBinary(new BinaryExpression(ConstantExpression.V0, ConstantExpression.V1, BinaryExpression.Operator.EQ));
Assertions.assertEquals("0 = 1", v.getQuery());
}
@Test
void visitConstant() {
var v = new QueryBuilder();
v.visitConstant(ConstantExpression.V0);
Assertions.assertEquals("0", v.getQuery());
}
@Test
void visitField() {
var v = new QueryBuilder();
v.visitField(new FieldExpression("schema", "table", "name", null));
Assertions.assertEquals("`schema`.`table`.`name`", v.getQuery());
v = new QueryBuilder();
v.visitField(new FieldExpression(null, "table", "name", null));
Assertions.assertEquals("`table`.`name`", v.getQuery());
v = new QueryBuilder();
v.visitField(new FieldExpression("schema", null, "name", null));
Assertions.assertEquals("`name`", v.getQuery());
v = new QueryBuilder();
v.visitField(new FieldExpression(null, null, "name", null));
Assertions.assertEquals("`name`", v.getQuery());
}
@Test
void visitIntermediateField() {
var v = new QueryBuilder();
v.visitIntermediateField(new IntermediateFieldExpression("value", null));
Assertions.assertEquals("value", v.getQuery());
}
@Test
void visitLimit() {
var v = new QueryBuilder();
v.visitLimit(new LimitExpression(ConstantExpression.V0, 3L, 5L));
Assertions.assertEquals("0 OFFSET 3 LIMIT 5", v.getQuery());
}
@Test
void visitNull() {
var v = new QueryBuilder();
v.visitNull(NullExpression.INSTANCE);
Assertions.assertEquals("NULL", v.getQuery());
}
@Test
void visitOr() {
var v = new QueryBuilder();
v.visitOr(new OrExpression(ConstantExpression.V0, ConstantExpression.V1));
Assertions.assertEquals("0 OR 1", v.getQuery());
}
@Test
void visitOrder() {
//multiple sorts
var v = new QueryBuilder();
v.visitOrder(new OrderExpression(ConstantExpression.V0, List.of(
new OrderExpression.Sort(new FieldExpression(null, null, "name1", null), OrderExpression.SortDirection.ASCENDING),
new OrderExpression.Sort(new FieldExpression(null, null, "name2", null), OrderExpression.SortDirection.DESCENDING))));
Assertions.assertEquals("0 ORDER BY `name1` ASC, `name2` DESC", v.getQuery());
//with schema and table
v = new QueryBuilder();
v.visitOrder(new OrderExpression(ConstantExpression.V0, List.of(
new OrderExpression.Sort(new FieldExpression("schema", "table", "name", null), OrderExpression.SortDirection.ASCENDING))));
Assertions.assertEquals("0 ORDER BY `schema`.`table`.`name` ASC", v.getQuery());
//with table
v = new QueryBuilder();
v.visitOrder(new OrderExpression(ConstantExpression.V0, List.of(
new OrderExpression.Sort(new FieldExpression(null, "table", "name", null), OrderExpression.SortDirection.ASCENDING))));
Assertions.assertEquals("0 ORDER BY `table`.`name` ASC", v.getQuery());
//with schema
v = new QueryBuilder();
v.visitOrder(new OrderExpression(ConstantExpression.V0, List.of(
new OrderExpression.Sort(new FieldExpression("schema", null, "name", null), OrderExpression.SortDirection.ASCENDING))));
Assertions.assertEquals("0 ORDER BY `name` ASC", v.getQuery());
//without schema and table
v = new QueryBuilder();
v.visitOrder(new OrderExpression(ConstantExpression.V0, List.of(
new OrderExpression.Sort(new FieldExpression(null, null, "name", null), OrderExpression.SortDirection.ASCENDING))));
Assertions.assertEquals("0 ORDER BY `name` ASC", v.getQuery());
}
@Test
void visitParameter() {
//isInput
var v = new QueryBuilder();
v.visitParameter(new ParameterExpression(0, null, true, null));
Assertions.assertEquals("param #0", v.getQuery());
//value == null
v = new QueryBuilder();
v.visitParameter(new ParameterExpression(0, null, false, null));
Assertions.assertEquals("NULL", v.getQuery());
//value == null
v = new QueryBuilder();
v.visitParameter(new ParameterExpression(0, Arrays.asList(null, "value"), false, null));
Assertions.assertEquals("(NULL, value)", v.getQuery());
//value == null
var v2 = new QueryBuilder();
Assertions.assertThrows(UnsupportedOperationException.class, () -> v2.visitParameter(new ParameterExpression(0, "value", false, null)));
}
@Test
void visitSelect() {
//select from non table expr
var v = new QueryBuilder();
v.visitSelect(new SelectExpression(List.of(
new FieldExpression(null, null, "name", null),
new FieldExpression(null, null, "name2", null)
), ConstantExpression.V0, "alias"));
Assertions.assertEquals("SELECT `name`, `name2` FROM (0) alias", v.getQuery());
v = new QueryBuilder();
v.visitSelect(new SelectExpression(List.of(
new FieldExpression(null, null, "name", null),
new FieldExpression(null, null, "name2", null)
), new TableExpression("table"), "alias"));
Assertions.assertEquals("SELECT `name`, `name2` FROM `table` alias", v.getQuery());
}
@Test
void visitTable() {
var v = new QueryBuilder();
v.visitTable(new TableExpression("table"));
Assertions.assertEquals("`table`", v.getQuery());
}
@Test
void visitTernary() {
var v = new QueryBuilder();
v.visitTernary(new TernaryExpression(ConstantExpression.V0, ConstantExpression.V1, ConstantExpression.V2));
Assertions.assertEquals("IF(0, 1, 2)", v.getQuery());
}
@Test
void visitUnary() {
var v = new QueryBuilder();
v.visitUnary(new UnaryExpression(NullExpression.INSTANCE, UnaryExpression.Operator.NOT));
Assertions.assertEquals("NOT NULL", v.getQuery());
v = new QueryBuilder();
v.visitUnary(new UnaryExpression(new BinaryExpression(ConstantExpression.V0, ConstantExpression.V1, BinaryExpression.Operator.EQ), UnaryExpression.Operator.NOT));
Assertions.assertEquals("NOT (0 = 1)", v.getQuery());
}
@Test
void visitWhere() {
var v = new QueryBuilder();
v.visitWhere(new WhereExpression(ConstantExpression.V0, ConstantExpression.V1));
Assertions.assertEquals("0 WHERE 1", v.getQuery());
}
@Getter
public static class TestClass extends SerializableObject {
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -1,32 +0,0 @@
package jef.util;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class CheckTest {
@Test
void notNull_paramNotNull() {
//setup
var obj = new Object();
//test
var ret = Check.notNull(obj, "obj") ;
//assert
assertEquals(obj, ret);
}
@Test
void notNull_paramNull() {
//setup
var varName = UUID.randomUUID().toString();
//test & assert
var ex = assertThrows(IllegalArgumentException.class, () -> Check.notNull(null, varName));
assertEquals(varName + " must be not null", ex.getMessage());
}
}

View File

@@ -1,34 +0,0 @@
package jef.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class UtilTest {
@Test
void tryGet_NoException() {
//setup
var object = new Object();
//test
var ret = Util.tryGet(() -> object);
//assert
assertTrue(ret.isPresent());
assertEquals(object, ret.orElseThrow());
}
@Test
void tryGet_Exception() {
//test
var ret = Util.tryGet(() -> {
throw new Exception();
});
//assert
assertFalse(ret.isPresent());
}
}

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jef</artifactId>
<groupId>jef</groupId>
<version>0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>migration-creator</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>jef</groupId>
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>jef</groupId>
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,127 +0,0 @@
package jef.model.migration.creator;
import jef.model.DbContext;
import jef.model.DbEntity;
import jef.model.DbEntityBuilder;
import jef.model.DbField;
import jef.model.ModelBuilder;
import jef.model.annotations.Generated;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint;
import jef.model.constraints.PrimaryKeyConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import jef.platform.base.SqlTypeMapper;
import jef.serializable.SerializableObject;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@RequiredArgsConstructor
public class ModelBuilderGenerator {
private final ModelBuilder mb;
private final String className;
private final String packageName;
private final SqlTypeMapper sqlTypeMapper;
private final Set<Class<?>> imports = new HashSet<>();
@Getter
private String java = null;
public ModelBuilderGenerator generate() {
if (java == null) {
java = generateModelBuilderJava();
}
return this;
}
private String generateModelBuilderJava() {
var indent = " ";
imports.add(DbContext.class);
imports.add(ModelBuilder.class);
imports.add(DbEntityBuilder.class);
var java = ""
+ "public class " + className + " extends DbContext {\n"
+ " @Override\n"
+ " public void onModelCreate(ModelBuilder mb) {\n";
for (DbEntity<? extends SerializableObject> entity : mb.getEntities()) {
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .name(\"" + entity.getName() + "\");\n";
for (DbField<?> field : entity.getFields()) {
if (field.getGenerated() != Generated.Type.NONE) {
imports.add(Generated.class);
}
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .field(\"" + field.getName() + "\", \"" + field.getTypeName() + "\")"
+ "\n" + indent + " .sqlType(" + getSqlType(field) + ")"
+ (field.isNotNull() ? "\n" + indent + " .isNotNull()" : "")
+ (field.getGenerated() != Generated.Type.NONE ? "\n" + indent + " .generated(Generated.Type." + field.getGenerated() + ")" : "")
+ "\n" + indent + " .isDatabaseField(" + field.isDatabaseField() + ")"
+ "\n" + indent + " .isModelField(" + field.isModelField() + ");\n";
}
if (entity.getPrimaryKey() != null) {
imports.add(List.class);
imports.add(PrimaryKeyConstraint.class);
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .hasOne(" + entity.getPrimaryKey().getFields().stream().map(f -> "\"" + f.getName() + "\"").collect(Collectors.joining(", ")) + ")\n"
+ indent + " .isPrimaryKey();\n";
}
java += "\n";
}
for (DbEntity<? extends SerializableObject> entity : mb.getEntities()) {
if (entity.getForeignKeys().isEmpty() && entity.getForeignKeys().isEmpty() && entity.getForeignKeys().isEmpty() && entity.getForeignKeys().isEmpty()) {
continue;
}
for (ForeignKeyConstraint foreignKey : entity.getForeignKeys()) {
imports.add(ForeignKeyConstraint.class);
//TODO hasOne/hasMany/withOne/WithMany
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .hasOne(" + foreignKey.getFields().stream().map(f -> "\"" + f.getName() + "\"").collect(Collectors.joining(", " + indent)) + ")\n"
+ indent + " .withOne(\"" + foreignKey.getReferencedEntity().getTypeName() + "\", " + foreignKey.getReferencedFields().stream().map(f -> "\"" + f.getName() + "\"").collect(Collectors.joining(", ")) + ")\n"
+ indent + " .onUpdate(ForeignKeyConstraint.Action." + foreignKey.getOnUpdate().name() + ")\n"
+ indent + " .onDelete(ForeignKeyConstraint.Action." + foreignKey.getOnDelete().name() + ");\n";
}
for (UniqueKeyConstraint uniqueKey : entity.getUniqueKeys()) {
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .hasOne(" + uniqueKey.getFields().stream().map(f -> "\"" + f.getName() + "\"").collect(Collectors.joining(", ")) + ")\n"
+ indent + " .isUnique();\n";
}
for (KeyConstraint key : entity.getKeys()) {
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .hasOne(" + key.getFields().stream().map(f -> "\"" + f.getName() + "\"").collect(Collectors.joining(", ")) + ")\n"
+ indent + " .isKey();\n";
}
for (IndexConstraint index : entity.getIndexes()) {
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .hasOne(" + index.getFields().stream().map(f -> "\"" + f.getName() + "\"").collect(Collectors.joining(", ")) + ")\n"
+ indent + " .isIndex();\n";
}
}
java += " }\n"
+ "}\n";
//imports
var normalImports = imports.stream().filter(e -> !e.getName().startsWith("java")).sorted(Comparator.comparing(Class::getName, String.CASE_INSENSITIVE_ORDER)).toList();
var javaImports = imports.stream().filter(e -> e.getName().startsWith("java")).sorted(Comparator.comparing(Class::getName, String.CASE_INSENSITIVE_ORDER)).toList();
var normalImportsString = normalImports.stream().map(e -> "import " + e.getName().replace("$", ".") + ";").collect(Collectors.joining("\n"));
var javaImportsString = javaImports.stream().map(e -> "import " + e.getName().replace("$", ".") + ";").collect(Collectors.joining("\n"));
//finalize
java = (packageName != null ? "package " + packageName + ";\n\n" : "")
+ normalImportsString + (normalImportsString.length() > 0 ? "\n\n" : "")
+ javaImportsString + (javaImportsString.length() > 0 ? "\n\n" : "")
+ java;
return java;
}
private String getSqlType(DbField<?> f) {
return Optional.ofNullable(f.getSqlType()).or(() -> sqlTypeMapper.map(f.getTypeName())).map(e -> "\"" + e + "\"").orElse(null);
}
}

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jef</artifactId>
<groupId>jef</groupId>
<version>0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mysql</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>jef</groupId>
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>jef</groupId>
<artifactId>migration-creator</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,20 +0,0 @@
package jef.platform.mysql;
import jef.MigrationException;
import jef.platform.base.Database;
import jef.platform.base.DatabaseOptions;
import java.sql.Connection;
public class MysqlDatabase extends Database {
private final MysqlPlatform platform;
public MysqlDatabase(Connection connection, DatabaseOptions options, MysqlPlatform platform) {
super(connection, options);
this.platform = platform;
}
@Override
public void migrate() throws MigrationException {
platform.getMigrationApplier(connection, options).migrate();
}
}

View File

@@ -1,27 +0,0 @@
package jef.platform.mysql;
import jef.platform.SqlPlatform;
import jef.platform.base.DatabaseOptions;
import jef.platform.base.SqlTypeMapper;
import jef.platform.base.migration.MigrationApplier;
import jef.platform.mysql.migration.MysqlMigrationApplier;
import jef.platform.mysql.migration.MysqlMigrationOperationTranslator;
import java.sql.Connection;
public class MysqlPlatform implements SqlPlatform {
@Override
public MysqlMigrationOperationTranslator getTranslator() {
return new MysqlMigrationOperationTranslator();
}
@Override
public SqlTypeMapper getTypeMapper() {
return new MysqlTypeMapper();
}
@Override
public MigrationApplier getMigrationApplier(Connection connection, DatabaseOptions options) {
return new MysqlMigrationApplier(connection, options, this);
}
}

View File

@@ -1,7 +0,0 @@
package jef.platform.mysql;
import jef.platform.base.SqlTypeMapper;
public class MysqlTypeMapper extends SqlTypeMapper {
}

View File

@@ -1,57 +0,0 @@
package jef.platform.mysql.migration;
import jef.model.migration.Migration;
import jef.platform.SqlPlatform;
import jef.platform.base.DatabaseOptions;
import jef.platform.base.migration.MigrationApplier;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class MysqlMigrationApplier extends MigrationApplier {
public MysqlMigrationApplier(Connection connection, DatabaseOptions options, SqlPlatform sqlPlatform) {
super(connection, options, sqlPlatform);
}
@Override
protected void insertMigrationLog(Migration m) throws SQLException {
try (var stmt = connection.prepareStatement("INSERT INTO `" + options.getMigrationsTableName() + "` (`migration`, `version`) VALUES (?, ?)")) {//TODO configurable log table name
stmt.setString(1, m.getClass().getSimpleName());
stmt.setString(2, "0.1"); //TODO insert actual library version
stmt.executeUpdate();
}
}
@Override
protected boolean migrationsTableExists() throws SQLException {
try (var stmt = connection.prepareStatement("SHOW TABLES");
var res = stmt.executeQuery()) {
while (res.next()) {
if (res.getString(1).equals(options.getMigrationsTableName())) {
return true;
}
}
}
return false;
}
@Override
protected List<String> getAppliedMigrations() throws SQLException {
var ret = new ArrayList<String>();
if (!migrationsTableExists()) {
return ret;
}
try (var stmt = connection.prepareStatement("SELECT `migration` FROM `" + options.getMigrationsTableName() + "`");
var res = stmt.executeQuery()) {
while (res.next()) {
if (!res.getString(1).equals(options.getMigrationsTableName())) {
ret.add(res.getString("migration"));
}
}
}
return ret;
}
}

View File

@@ -1,163 +0,0 @@
package jef.platform.mysql.migration;
import jef.model.annotations.Generated;
import jef.model.migration.operation.AddFieldOperation;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddIndexOperation;
import jef.model.migration.operation.AddKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.AddTableOperation;
import jef.model.migration.operation.AddUniqueKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.DropFieldOperation;
import jef.model.migration.operation.DropTableOperation;
import jef.model.migration.operation.MigrationOperation;
import jef.model.migration.operation.RenameFieldOperation;
import jef.model.migration.operation.RenameTableOperation;
import jef.model.migration.operation.UpdateFieldOperation;
import jef.platform.base.migration.MigrationOperationTranslator;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class MysqlMigrationOperationTranslator implements MigrationOperationTranslator {
private static final Map<Class<? extends MigrationOperation>, Mapper<MigrationOperation>> translators;
static {
var map = new HashMap<Class<? extends MigrationOperation>, Mapper<MigrationOperation>>();
map.put(AddFieldOperation.class, MysqlMigrationOperationTranslator::translateAddFieldOperation);
map.put(AddForeignKeyOperation.class, MysqlMigrationOperationTranslator::translateAddForeignKeyOperation);
map.put(AddIndexOperation.class, MysqlMigrationOperationTranslator::translateAddIndexOperation);
map.put(AddKeyOperation.class, MysqlMigrationOperationTranslator::translateAddKeyOperation);
map.put(AddPrimaryKeyOperation.class, MysqlMigrationOperationTranslator::translateAddPrimaryKeyOperation);
map.put(AddTableOperation.class, MysqlMigrationOperationTranslator::translateAddTableOperation);
map.put(AddUniqueKeyOperation.class, MysqlMigrationOperationTranslator::translateAddUniqueKeyOperation);
map.put(DropConstraintOperation.class, MysqlMigrationOperationTranslator::translateDropConstraintOperation);
map.put(DropFieldOperation.class, MysqlMigrationOperationTranslator::translateDropFieldOperation);
map.put(DropTableOperation.class, MysqlMigrationOperationTranslator::translateDropTableOperation);
map.put(RenameFieldOperation.class, MysqlMigrationOperationTranslator::translateRenameFieldOperation);
map.put(RenameTableOperation.class, MysqlMigrationOperationTranslator::translateRenameTableOperation);
map.put(UpdateFieldOperation.class, MysqlMigrationOperationTranslator::translateUpdateFieldOperation);
translators = Collections.unmodifiableMap(map);
}
public static PreparedStatement translateS(Connection connection, MigrationOperation operation) throws SQLException {
return Optional.ofNullable(translators.get(operation.getClass())).orElseThrow().apply(connection, operation);
}
public PreparedStatement translate(Connection connection, MigrationOperation operation) throws SQLException {
return translateS(connection, operation);
}
private static PreparedStatement translateAddFieldOperation(Connection connection, MigrationOperation operation) throws SQLException {
AddFieldOperation op = (AddFieldOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " ADD COLUMN `" + op.getField() + "` "
+ op.getSqlType() + (op.isNotNull() ? " NOT NULL" : "") //TODO add after field specification
+ (op.getGenerated() == Generated.Type.IDENTITY ? "AUTO_INCREMENT " : "")
// + (op.getGenerated() == Generated.Type.COMPUTED ? "AUTO_INCREMENT " : "")//TODO
+ " LAST");
}
private static PreparedStatement translateAddForeignKeyOperation(Connection connection, MigrationOperation operation) throws SQLException {
AddForeignKeyOperation op = (AddForeignKeyOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " ADD CONSTRAINT `" + op.getName() + "`"
+ " FOREIGN KEY (" + op.getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")"
+ " REFERENCES `" + op.getReferencedTable() + "`(" + op.getReferencedFields().stream()
.map(e -> "`" + e + "`")
.collect(Collectors.joining(", ")) + ")");
}
private static PreparedStatement translateAddIndexOperation(Connection connection, MigrationOperation operation) throws SQLException {
AddIndexOperation op = (AddIndexOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " ADD CONSTRAINT `" + op.getName() + "`"
+ " INDEX (" + op.getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")");
}
private static PreparedStatement translateAddKeyOperation(Connection connection, MigrationOperation operation) throws SQLException {
AddKeyOperation op = (AddKeyOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " ADD CONSTRAINT `" + op.getName() + "`"
+ " KEY (" + op.getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")");
}
private static PreparedStatement translateAddPrimaryKeyOperation(Connection connection, MigrationOperation operation) throws SQLException {
AddPrimaryKeyOperation op = (AddPrimaryKeyOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " ADD " + (!op.getName().equals("PRIMARY") ? "CONSTRAINT `" + op.getName() + "`" : "")
+ " PRIMARY KEY (" + op.getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")");
// + " ADD CONSTRAINT `" + op.getName() + "`"
// + " PRIMARY KEY (" + op.getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")");
}
private static PreparedStatement translateAddTableOperation(Connection connection, MigrationOperation operation) throws SQLException {
AddTableOperation op = (AddTableOperation) operation;
return connection.prepareStatement("CREATE TABLE `" + op.getTable() + "` ("
+ op.getFields().stream().map(e -> {
var f = e.build();
return "`" + f.getField() + "` " + f.getSqlType()
+ (f.isNotNull() ? " NOT NULL" : "")
+ (f.getGenerated() == Generated.Type.IDENTITY ? " AUTO_INCREMENT" : "");
// + (op.getGenerated() == Generated.Type.COMPUTED ? "AUTO_INCREMENT " : "")//TODO
}).collect(Collectors.joining(", "))
+ (op.getPrimaryKey() != null
? ", PRIMARY KEY (" + op.getPrimaryKey().build().getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")"
: "")
+ ")"); //TODO default collocation from database config or operation, field collation, constraints
}
private static PreparedStatement translateAddUniqueKeyOperation(Connection connection, MigrationOperation operation) throws SQLException {
AddUniqueKeyOperation op = (AddUniqueKeyOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " ADD CONSTRAINT `" + op.getName() + "`"
+ " UNIQUE (" + op.getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")");
}
private static PreparedStatement translateDropConstraintOperation(Connection connection, MigrationOperation operation) throws SQLException {
DropConstraintOperation op = (DropConstraintOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " DROP CONSTRAINT `" + op.getName() + "`");
}
private static PreparedStatement translateDropFieldOperation(Connection connection, MigrationOperation operation) throws SQLException {
DropFieldOperation op = (DropFieldOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " DROP COLUMN `" + op.getField() + "`");
}
private static PreparedStatement translateDropTableOperation(Connection connection, MigrationOperation operation) throws SQLException {
DropTableOperation op = (DropTableOperation) operation;
return connection.prepareStatement("DROP TABLE `" + op.getTable() + "`");
}
private static PreparedStatement translateRenameFieldOperation(Connection connection, MigrationOperation operation) throws SQLException {
RenameFieldOperation op = (RenameFieldOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " RENAME COLUMN `" + op.getOldName() + "` `" + op.getNewName() + "`");
}
private static PreparedStatement translateRenameTableOperation(Connection connection, MigrationOperation operation) throws SQLException {
RenameTableOperation op = (RenameTableOperation) operation;
return connection.prepareStatement("RENAME TABLE `" + op.getOldName() + "` `" + op.getNewName() + "`");
}
private static PreparedStatement translateUpdateFieldOperation(Connection connection, MigrationOperation operation) throws SQLException {
UpdateFieldOperation op = (UpdateFieldOperation) operation;
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " ALTER COLUMN `" + op.getField() + "` `" + op.getNewName() + "`"
+ op.getSqlType() + (op.isNotNull() ? " NOT NULL" : ""));
}
@FunctionalInterface
private interface Mapper<T extends MigrationOperation> {
PreparedStatement apply(Connection connection, T operation) throws SQLException;
}
}

View File

@@ -1,7 +0,0 @@
package jef.platform.mysql.query;
import jef.platform.base.query.QueryBuilder;
public class MysqlQueryBuilder extends QueryBuilder {
}

View File

@@ -1,169 +0,0 @@
package jef.platform.mysql.migration;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.DbContextOptions;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.ForeignKey;
import jef.model.migration.creator.MigrationCreator;
import jef.platform.base.Database;
import jef.platform.base.DatabaseOptions;
import jef.platform.mysql.MysqlDatabase;
import jef.platform.mysql.MysqlPlatform;
import jef.serializable.SerializableObject;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MysqlMigrationTest {
private static final String GENERATE_MIGRATIONS_FOLDER = "target/test-generated-migrations/";
private static final String GENERATE_MIGRATIONS_FOLDER_SRC = GENERATE_MIGRATIONS_FOLDER + "src/";
private static final String GENERATE_MIGRATIONS_FOLDER_TARGET = GENERATE_MIGRATIONS_FOLDER + "target/";
private static final String PACKAGE_NAME = MysqlMigrationTest.class.getSimpleName();
@Test
public void test() throws Exception {
clearMigrationFolders();
generateInitialMigration();
compileInitialMigration();
Class.forName("com.mysql.cj.jdbc.Driver").getDeclaredConstructor().newInstance();
var dboptions = new DatabaseOptions("jdbc:mysql://localhost/test", "test", "password", PACKAGE_NAME, null);
var ctxoptions = new DbContextOptions(dboptions);
var conn = DriverManager.getConnection(dboptions.getUrl(), dboptions.getUser(), dboptions.getPassword());
var sqlPlatform = new MysqlPlatform();
var db = new MysqlDatabase(conn, dboptions, sqlPlatform);
var ctx = new Ctx(db, ctxoptions);
ctx.getDatabase().migrate();
var result = ctx.getCompanies().filter(e -> e.getName().equals("foobar")).toString();
System.out.println(result);
}
private void clearMigrationFolders() {
delRecursive(new File(GENERATE_MIGRATIONS_FOLDER_SRC + PACKAGE_NAME));
delRecursive(new File(GENERATE_MIGRATIONS_FOLDER_TARGET + PACKAGE_NAME));
}
private void delRecursive(File f) {
if (f.isDirectory()) {
Arrays.stream(Objects.requireNonNull(f.listFiles()))
.filter(e -> !List.of(".", "..").contains(e.getName()))
.forEach(this::delRecursive);
}
f.delete();
}
private void generateInitialMigration() {
try {
var sqlPlatform = new MysqlPlatform();
var from = new ModelBuilder();
var to = ModelBuilder.from(Ctx.class);
var result = new MigrationCreator(sqlPlatform, from, to, "Initial", PACKAGE_NAME, null).createMigration();
var dir = Path.of(GENERATE_MIGRATIONS_FOLDER_SRC, PACKAGE_NAME).toFile();
dir.mkdirs();
var fileOptions = new OpenOption[]{StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE};
Files.writeString(Path.of(dir.toString(), result.getMigrationClassName() + ".java"), result.getMigration(), fileOptions);
Files.writeString(Path.of(dir.toString(), result.getMigrationSnapshotClassName() + ".java"), result.getMigrationSnapshot(), fileOptions);
Files.writeString(Path.of(dir.toString(), result.getCurrentSnapshotClassName() + ".java"), result.getCurrentSnapshot(), fileOptions);
} catch (AssertionError | RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException("Error while compiling generated class", t);
}
}
private void compileInitialMigration() {
try {
var javaHome = System.getProperty("java.home");
var isWindows = System.getProperty("os.name").toLowerCase(Locale.ROOT).equals("win");
var javac = new File(javaHome, "bin/javac" + (isWindows ? ".exe" : ""));
var params = new ArrayList<>(List.of(
javac.getAbsolutePath(),
"-cp", "target/classes" + File.pathSeparator + "target/test-classes" + File.pathSeparator + "../core/target/classes",
"-encoding", "UTF8",
"-g", //debug symbols
"-d", GENERATE_MIGRATIONS_FOLDER_TARGET //target
));
Arrays.stream(new File(GENERATE_MIGRATIONS_FOLDER_SRC + PACKAGE_NAME).listFiles(File::isFile)).map(File::getPath).forEach(params::add);
var process = new ProcessBuilder()
.command(params.toArray(String[]::new))
.inheritIO()
.start();
process.waitFor(10, TimeUnit.SECONDS);
var exitCode = process.exitValue();
assertEquals(0, exitCode, "Initial migration compilation failed");
} catch (AssertionError | RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException("Error while compiling generated class", t);
}
}
@Getter
public static class Ctx extends DbContext {
@Clazz(Company.class)
private DbSet<Company> companies;
@Clazz(Employee.class)
private DbSet<Employee> employees;
public Ctx() {
}
public Ctx(Database database, DbContextOptions options) {
super(database, options);
}
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
public static class Company extends SerializableObject {
private int id;
private String name;
private Employee ceo;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode(callSuper = false)
public static class Employee extends SerializableObject {
private int id;
private String name;
private Employee boss;
@ForeignKey(getterOrField = "boss")
private int bossId;
private Company company;
@ForeignKey(getterOrField = "company")
private int companyId;
}
}

33
pom.xml
View File

@@ -7,13 +7,6 @@
<groupId>jef</groupId>
<artifactId>jef</artifactId>
<version>0.1</version>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>cli</module>
<module>mysql</module>
<module>migration-creator</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -101,22 +94,12 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<!-- <plugin> &lt;!&ndash; for inserting values in plugin.yml &ndash;&gt;-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-resources-plugin</artifactId>-->
<!-- <version>2.7</version>-->
<!-- <configuration>-->
<!-- <encoding>${project.build.sourceEncoding}</encoding>-->
<!-- </configuration>-->
<!-- </plugin>-->
<plugin> <!-- for tests which generate, compile, and execute migrations -->
<plugin> <!-- for inserting values in plugin.yml -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${project.basedir}/target/test-generated-migrations/target</additionalClasspathElement>
</additionalClasspathElements>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
@@ -163,14 +146,6 @@
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>javax.persistence</groupId>-->
<!-- <artifactId>javax.persistence-api</artifactId>-->

View File

@@ -1,6 +1,6 @@
package jef.asm;
public class AsmParseException extends RuntimeException {
public class AsmParseException extends Exception {
public AsmParseException() {
}

View File

@@ -0,0 +1,66 @@
package jef.asm;
import jef.expressions.Expression;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializablePredicate;
import lombok.Getter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.util.stream.IntStream;
@Getter
public class AsmParser {
private final Serializable lambda;
public AsmParser(SerializablePredicate<?> predicate) {
this.lambda = predicate;
}
public AsmParser(SerializableFunction<?, ?> function) {
this.lambda = function;
}
public Expression parse() throws AsmParseException {
try {
return parseExpression();
} catch (Exception e) {
throw new AsmParseException("PredicateParser: failed to parse expression: " + e.getLocalizedMessage(), e);
}
}
private Expression parseExpression() throws Exception {
var cls = lambda.getClass();
var loader = cls.getClassLoader();
InputStream is;
// System.out.println(cls);
// System.out.println(cls.getName());
if (cls.getName().contains("$$Lambda$")) {
// System.out.println(cls.getName().split("\\$\\$")[0].replace(".", "/") + ".class");
is = loader.getResourceAsStream(cls.getName().split("\\$\\$")[0].replace(".", "/") + ".class");
} else {
// System.out.println(cls.getName().replace(".", "/") + ".class");
is = loader.getResourceAsStream(cls.getName().replace(".", "/") + ".class");
}
var x = cls.getDeclaredMethod("writeReplace");
// System.out.println(x);
x.setAccessible(true);
var serlambda = (SerializedLambda) x.invoke(lambda);
Object[] args = IntStream.range(0, serlambda.getCapturedArgCount()).mapToObj(serlambda::getCapturedArg).toArray();
// System.out.println(serlambda);
var lambdaname = serlambda.getImplMethodName();
// System.out.println(lambdaname);
var expr = new Expression[1];
var cr = new ClassReader(is);
var visiter = new FilterClassVisitor(Opcodes.ASM9, lambdaname, args, e -> expr[0] = e);
cr.accept(visiter, 0);
return expr[0];
}
}

View File

@@ -0,0 +1,32 @@
package jef.asm;
import jef.expressions.Expression;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import java.util.function.Consumer;
class FilterClassVisitor extends ClassVisitor {
private final int api;
private final String lambdaname;
private final Consumer<Expression> queryConsumer;
private final Object[] args;
protected FilterClassVisitor(int api, String lambdaname, Object[] args, Consumer<Expression> queryConsumer) {
super(api);
this.api = api;
this.lambdaname = lambdaname;
this.args = args;
this.queryConsumer = queryConsumer;
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (!name.equals(lambdaname)) {
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
System.out.println("found method name: " + name);
return new FilterMethodVisitor(api, descriptor, args, queryConsumer);
}
}

View File

@@ -3,7 +3,6 @@ package jef.asm;
import jef.expressions.BinaryExpression;
import jef.expressions.ConstantExpression;
import jef.expressions.Expression;
import jef.expressions.FunctionExpression;
import jef.expressions.IntermediateFieldExpression;
import jef.expressions.NullExpression;
import jef.expressions.ParameterExpression;
@@ -23,7 +22,6 @@ import org.objectweb.asm.Type;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@@ -31,6 +29,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -38,29 +37,21 @@ class FilterMethodVisitor extends MethodVisitor {
private Stack<Expression> varStack = new Stack<>();
private final String[] parameterClasses;
private final Object[] args;
private final Consumer<Expression> exprConsumer;
private Expression lambdaExpr;
private final Set<Field> accessedMembers = new HashSet<>();
protected FilterMethodVisitor(int api, String className, String descriptor, Object[] args) {
protected FilterMethodVisitor(int api, String descriptor, Object[] args, Consumer<Expression> exprConsumer) {
super(api);
this.args = args;
this.exprConsumer = exprConsumer;
//parameters
var types = Type.getMethodType(descriptor).getArgumentTypes();
if (types.length == 0) {// class method //TODO this is dirty, as only works for parameterless class methods
this.args = new Object[]{null};
parameterClasses = new String[]{className};
} else {
this.args = args;
parameterClasses = new String[types.length];
for (int i = 0; i < types.length; i++) {
parameterClasses[i] = types[i].getClassName();
}
}
}
public AsmParseResult getResult() {
return new AsmParseResult(lambdaExpr, accessedMembers);
}
@Override
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
@@ -76,11 +67,6 @@ class FilterMethodVisitor extends MethodVisitor {
var v = varStack.pop();
if (v instanceof ParameterExpression p) {
if (p.isInput()) {
try {
accessedMembers.add(Class.forName(owner.replace("/", ".")).getDeclaredField(name));
} catch (NoSuchFieldException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
varStack.push(new IntermediateFieldExpression(name, descriptor));
} else {
throw new RuntimeException("field insn: unsupported GETFIELD expression");
@@ -115,22 +101,8 @@ class FilterMethodVisitor extends MethodVisitor {
var element = varStack.pop();
var collection = varStack.pop();
varStack.push(new BinaryExpression(element, collection, BinaryExpression.Operator.IN));
} else if (name.equals("equals")
&& owner.equals("java/lang/String")) {
var value = varStack.pop();
var variable = varStack.pop();
varStack.push(new BinaryExpression(variable, value, BinaryExpression.Operator.EQ));
} else if (name.equals("equalsIgnoreCase")
&& owner.equals("java/lang/String")) {
var value = varStack.pop();
var variable = varStack.pop();
varStack.push(new BinaryExpression(new FunctionExpression(FunctionExpression.TOLOWER, List.of(variable)),
new FunctionExpression(FunctionExpression.TOLOWER, List.of(value)),
BinaryExpression.Operator.EQ));
} else if (descriptor.startsWith("()")) {
var method = Class.forName(owner.replace("/", ".")).getDeclaredMethod(name);
var res = new OptimizedAsmParser(method).parse();
var field = res.getAccessedFields().stream().findFirst();
} else if (name.startsWith("get") && name.length() > 3 && descriptor.startsWith("()")) { //hacky getter support, TODO replace this with proper getter eval later
var field = findField(owner, name);
if (field.isPresent()) {
var v = varStack.pop();
if (v instanceof ParameterExpression p) {
@@ -149,9 +121,6 @@ class FilterMethodVisitor extends MethodVisitor {
throw new RuntimeException("method insn: unsupported function " + name + " in " + owner);
}
} catch (Exception e) {
if (e instanceof RuntimeException re) {
throw re;
}
throw new RuntimeException("method insn: ", e);
}
} else {
@@ -417,6 +386,7 @@ class FilterMethodVisitor extends MethodVisitor {
System.out.println("end");
super.visitEnd();
exprConsumer.accept(lambdaExpr);
}
@Override

View File

@@ -1,13 +1,20 @@
package jef.asm;
import jef.expressions.Expression;
import jef.expressions.modifier.ExpressionOptimizerBottomUp;
import jef.expressions.modifier.IConst0Fixer;
import jef.expressions.modifier.TableAliasInjector;
import jef.expressions.modifier.TernaryRewriter;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializablePredicate;
import lombok.Getter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import java.lang.reflect.Method;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.util.stream.IntStream;
@Getter
public class OptimizedAsmParser extends AsmParser{
@@ -20,13 +27,8 @@ public class OptimizedAsmParser extends AsmParser {
super(function);
}
public OptimizedAsmParser(Method method) {
super(method);
}
public AsmParseResult parse() throws AsmParseException {
var result = super.parse();
var expr = result.getExpression();
public Expression parse() throws AsmParseException {
var expr = super.parse();
System.out.println(expr);
expr = new TernaryRewriter().modify(expr);
@@ -35,6 +37,6 @@ public class OptimizedAsmParser extends AsmParser {
System.out.println(expr);
expr = new ExpressionOptimizerBottomUp().modify(expr);
return new AsmParseResult(expr, result.getAccessedFields());
return expr;
}
}

View File

@@ -5,6 +5,7 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Getter

View File

@@ -25,7 +25,6 @@ public class BinaryExpression implements Expression {
case EQ, NE, IS -> Priority.EQUALITY;
case LT, LE, GT, GE -> Priority.RELATIONAL;
case IN -> Priority.RELATIONAL; //or equality?
//TODO +-, */, %, ...
default -> throw new IllegalStateException();
};
}

View File

@@ -0,0 +1,34 @@
package jef.expressions;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class ConstantExpression implements Expression {
public static final jef.expressions.ConstantExpression V0 = new jef.expressions.ConstantExpression(0);
public static final jef.expressions.ConstantExpression V1 = new jef.expressions.ConstantExpression(1);
public static final jef.expressions.ConstantExpression V2 = new jef.expressions.ConstantExpression(2);
public static final jef.expressions.ConstantExpression V3 = new jef.expressions.ConstantExpression(3);
public static final jef.expressions.ConstantExpression V4 = new jef.expressions.ConstantExpression(4);
public static final jef.expressions.ConstantExpression V5 = new jef.expressions.ConstantExpression(5);
protected final Object value;
@Override
public Type getType() {
return Type.CONSTANT;
}
@Override
public Priority getPriority() {
return Priority.CONSTANT;
}
@Override
public String toString() {
return value.toString();
}
}

View File

@@ -13,7 +13,6 @@ public interface Expression {
BINARY,
CONSTANT,
FIELD,
FUNCTION,
INTERMEDIATE_FIELD,
LIMIT,
NULL,
@@ -43,7 +42,7 @@ public interface Expression {
BIT_SHIFT(10), // <<, >>
ADD_SUB(13), // +, -
MUL_DIV_MOD(14), // *, /, %
UNARY_PRE(15), // +, -, !, ~, --i, ++i, function calls
UNARY_PRE(15), // +, -, !, ~, --i, ++i
UNARY_POST(16), // i++, i--
CONSTANT(17), // constant values
;

View File

@@ -0,0 +1,38 @@
package jef.expressions;
import jef.expressions.selectable.DatabaseSelectAllExpression;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
@Getter
@AllArgsConstructor
public class IncludeExpression implements Expression {
private final Expression expr;
private final List<Include> includes;
@Override
public Type getType() {
return Type.INCLUDE;
}
@Override
public Priority getPriority() {
return Priority.UNDEFINED;
}
@Override
public String toString() {
var ret = new SelectExpression(List.of(new DatabaseSelectAllExpression()), expr, "").toString();
ret += " LEFT JOIN "
return ret;
}
@Getter
@AllArgsConstructor
public static class Include {
private final FieldExpression expr;
}
}

View File

@@ -4,6 +4,7 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;
@Getter
@@ -32,7 +33,7 @@ public class ParameterExpression extends ConstantExpression implements Expressio
} else if (this.value == null) {
return "null";
} else if (this.value instanceof Collection) {
return "(" + ((Collection<?>) this.value).stream().map(e -> e == null ? "NULL" : (e instanceof String ? "\"" + e + "\"" : e.toString())).collect(Collectors.joining(", ")) + ")";
return "(" + ((Collection<?>) this.value).stream().map(e -> e == null ? "NULL" : Objects.toString(e)).collect(Collectors.joining(", ")) + ")";
}
return value.toString();
}

View File

@@ -25,6 +25,6 @@ public class TableExpression extends ConstantExpression {
@Override
public String toString() {
return "`" + name + "`";
return "`" + super.toString() + "`";
}
}

View File

@@ -5,7 +5,6 @@ import jef.expressions.BinaryExpression;
import jef.expressions.ConstantExpression;
import jef.expressions.Expression;
import jef.expressions.FieldExpression;
import jef.expressions.FunctionExpression;
import jef.expressions.IntermediateFieldExpression;
import jef.expressions.LimitExpression;
import jef.expressions.NullExpression;
@@ -29,7 +28,6 @@ public abstract class ExpressionModifier {
case BINARY -> modifyBinary((BinaryExpression) expr);
case CONSTANT -> modifyConstant((ConstantExpression) expr);
case FIELD -> modifyField((FieldExpression) expr);
case FUNCTION -> modifyFunction((FunctionExpression) expr);
case INTERMEDIATE_FIELD -> modifyIntermediateField((IntermediateFieldExpression) expr);
case LIMIT -> modifyLimit((LimitExpression) expr);
case NULL -> modifyNull((NullExpression) expr);
@@ -65,10 +63,6 @@ public abstract class ExpressionModifier {
return expr;
}
public Expression modifyFunction(FunctionExpression expr) {
return new FunctionExpression(expr.getFunction(), expr.getParameters().stream().map(this::modify).toList());
}
public Expression modifyIntermediateField(IntermediateFieldExpression expr) {
return expr;
}

View File

@@ -9,13 +9,13 @@ public class DatabaseSelectAllExpression implements Expression, SelectableExpres
public static final DatabaseSelectAllExpression INSTANCE = new DatabaseSelectAllExpression();
@Override
public Type getType() {
return Type.CONSTANT;
public Expression.Type getType() {
return Expression.Type.CONSTANT;
}
@Override
public Priority getPriority() {
return Priority.UNDEFINED;
public Expression.Priority getPriority() {
return Expression.Priority.UNDEFINED;
}
@Override

View File

@@ -4,7 +4,6 @@ import jef.expressions.AndExpression;
import jef.expressions.BinaryExpression;
import jef.expressions.ConstantExpression;
import jef.expressions.FieldExpression;
import jef.expressions.FunctionExpression;
import jef.expressions.IntermediateFieldExpression;
import jef.expressions.LimitExpression;
import jef.expressions.NullExpression;
@@ -57,11 +56,6 @@ public class DebugExpressionVisitor extends ExpressionVisitor {
System.out.println(i() + expr.toString());
}
@Override
public void visitFunction(FunctionExpression expr) {
System.out.println(i() + expr.toString());
}
@Override
public void visitIntermediateField(IntermediateFieldExpression expr) {
System.out.println(i() + expr.toString());

Some files were not shown because too many files have changed in this diff Show More