diff --git a/README.md b/README.md
index 6710368..98dfd55 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ Include the Maven artifact:
com.github.collinalpert
java2db
- 5.4.0
+ 5.5.0
```
Or include the [JAR](https://github.com/CollinAlpert/Java2DB/releases/latest) in your project.
@@ -121,6 +121,8 @@ The `BaseService` provides a `createQuery` method which allows you to manually b
Much rather, use the `getSingle` or `getMultiple` methods. `getMultiple` returns an `EntityQuery` object with a preconfigured WHERE condition and then allows you to chain some additional query options. As of the current `EntityQuery` version, WHERE, LIMIT and ORDER BY are supported. With the ORDER BY functionality, there is also the possibility to coalesce multiple columns when ordering. Effectively, the calls `createQuery().where(predicate)` and `getMultiple(predicate)` are the same. The latter is recommended.\
As previously mentioned, to execute the query and retrieve a result, use the `toList`, `toStream`, `toArray` or `toMap` methods.
+As shown in the example above, you can automatically join a table using the `@ForeignKeyEntity` annotation. You also have the option to specify which type of join to use when joining. If you would like only a specific column to be joined, say, the `code` field of the `gender` table, you can additionally specify the `@ForeignKeyPath` annotation.
+
#### Update
Every service class has support for updating a single as well as multiple entities at once on the database.
Check out the different `update` methods provided by your service class.
diff --git a/pom.xml b/pom.xml
index 913578e..221f697 100755
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.github.collinalpert
java2db
- 5.4.0
+ 5.5.0
jar
Java2DB
diff --git a/src/main/java/com/github/collinalpert/java2db/annotations/ForeignKeyEntity.java b/src/main/java/com/github/collinalpert/java2db/annotations/ForeignKeyEntity.java
index 23e4701..7ef224a 100644
--- a/src/main/java/com/github/collinalpert/java2db/annotations/ForeignKeyEntity.java
+++ b/src/main/java/com/github/collinalpert/java2db/annotations/ForeignKeyEntity.java
@@ -23,4 +23,22 @@
@Retention(RetentionPolicy.RUNTIME)
public @interface ForeignKeyEntity {
String value();
+
+ JoinTypes joinType() default JoinTypes.LEFT;
+
+ enum JoinTypes {
+ LEFT("left"),
+ INNER("inner"),
+ RIGHT("right");
+
+ private final String sqlKeyword;
+
+ JoinTypes(String sqlKeyword) {
+ this.sqlKeyword = sqlKeyword;
+ }
+
+ public String getSqlKeyword() {
+ return sqlKeyword;
+ }
+ }
}
diff --git a/src/main/java/com/github/collinalpert/java2db/annotations/ForeignKeyPath.java b/src/main/java/com/github/collinalpert/java2db/annotations/ForeignKeyPath.java
new file mode 100644
index 0000000..64d0bb9
--- /dev/null
+++ b/src/main/java/com/github/collinalpert/java2db/annotations/ForeignKeyPath.java
@@ -0,0 +1,29 @@
+package com.github.collinalpert.java2db.annotations;
+
+import com.github.collinalpert.java2db.entities.BaseEntity;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is used to indicate that only a specific column of a table is supposed to be joined when executing the query.
+ * It has to be used in conjunction with the {@link ForeignKeyEntity} attribute.
+ *
+ * @author Collin Alpert
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ForeignKeyPath {
+
+ /**
+ * @return The name of the column on the table which will be joined.
+ */
+ String value();
+
+ /**
+ * @return The class which represents the table which will be joined.
+ */
+ Class extends BaseEntity> foreignKeyClass();
+}
diff --git a/src/main/java/com/github/collinalpert/java2db/database/ForeignKeyReference.java b/src/main/java/com/github/collinalpert/java2db/database/ForeignKeyReference.java
index c901f50..3594d47 100644
--- a/src/main/java/com/github/collinalpert/java2db/database/ForeignKeyReference.java
+++ b/src/main/java/com/github/collinalpert/java2db/database/ForeignKeyReference.java
@@ -15,21 +15,33 @@ public class ForeignKeyReference extends TableColumnReference {
* The table the foreign key refers to.
*/
private final String foreignKeyTableName;
+
/**
* The name of the column which references the foreign table. Not to be confused with the column in the foreign table.
*/
private final String foreignKeyColumnName;
+
/**
* An alias for the foreign key table name.
*/
- private String foreignKeyAlias;
+ private final String foreignKeyAlias;
+
+ /**
+ * The type of join to use.
+ */
+ private final ForeignKeyEntity.JoinTypes joinType;
- public ForeignKeyReference(String tableName, String alias, Field column, String foreignKeyTableName, String foreignKeyAlias) {
+ public ForeignKeyReference(String tableName, String alias, Field column, String foreignKeyTableName, String foreignKeyAlias, ForeignKeyEntity.JoinTypes joinType) {
super(tableName, alias, column);
this.foreignKeyTableName = foreignKeyTableName;
this.foreignKeyColumnName = column.getAnnotation(ForeignKeyEntity.class).value();
this.foreignKeyAlias = foreignKeyAlias;
+ this.joinType = joinType;
+ }
+
+ public ForeignKeyReference(String tableName, String alias, Field column, String foreignKeyTableName, String foreignKeyAlias) {
+ this(tableName, alias, column, foreignKeyTableName, foreignKeyAlias, ForeignKeyEntity.JoinTypes.LEFT);
}
public String getForeignKeyTableName() {
@@ -43,4 +55,8 @@ public String getForeignKeyColumnName() {
public String getForeignKeyAlias() {
return foreignKeyAlias;
}
+
+ public ForeignKeyEntity.JoinTypes getJoinType() {
+ return joinType;
+ }
}
diff --git a/src/main/java/com/github/collinalpert/java2db/entities/SerializableBaseEntity.java b/src/main/java/com/github/collinalpert/java2db/entities/SerializableBaseEntity.java
index 62048dd..1b6ff45 100644
--- a/src/main/java/com/github/collinalpert/java2db/entities/SerializableBaseEntity.java
+++ b/src/main/java/com/github/collinalpert/java2db/entities/SerializableBaseEntity.java
@@ -10,7 +10,7 @@ public class SerializableBaseEntity extends BaseEntity implements Serializable {
private long id;
public long getId() {
- return id;
+ return this.id;
}
/**
diff --git a/src/main/java/com/github/collinalpert/java2db/mappers/EntityMapper.java b/src/main/java/com/github/collinalpert/java2db/mappers/EntityMapper.java
index fbc315e..7ce80c4 100755
--- a/src/main/java/com/github/collinalpert/java2db/mappers/EntityMapper.java
+++ b/src/main/java/com/github/collinalpert/java2db/mappers/EntityMapper.java
@@ -2,6 +2,7 @@
import com.github.collinalpert.java2db.annotations.ColumnName;
import com.github.collinalpert.java2db.annotations.ForeignKeyEntity;
+import com.github.collinalpert.java2db.annotations.ForeignKeyPath;
import com.github.collinalpert.java2db.contracts.IdentifiableEnum;
import com.github.collinalpert.java2db.entities.BaseEntity;
import com.github.collinalpert.java2db.modules.AnnotationModule;
@@ -194,6 +195,14 @@ private void setFields(ResultSet set, TEntity entit
continue;
}
+ var foreignKeyPathInfo = AnnotationModule.getInstance().getAnnotationInfo(field, ForeignKeyPath.class);
+ if (foreignKeyPathInfo.hasAnnotation()) {
+ var aliasKey = String.join("_", identifier, field.getName());
+ tryAction(() -> field.set(entity, set.getObject(this.aliases.get(aliasKey) + "_" + foreignKeyPathInfo.getAnnotation().value(), field.getType())));
+
+ continue;
+ }
+
if (!BaseEntity.class.isAssignableFrom(field.getType())) {
throw new IllegalArgumentException(String.format("Type %s, which is annotated as a foreign key, does not extend BaseEntity.", field.getType().getSimpleName()));
}
diff --git a/src/main/java/com/github/collinalpert/java2db/modules/FieldModule.java b/src/main/java/com/github/collinalpert/java2db/modules/FieldModule.java
index 84270c1..8a68c56 100644
--- a/src/main/java/com/github/collinalpert/java2db/modules/FieldModule.java
+++ b/src/main/java/com/github/collinalpert/java2db/modules/FieldModule.java
@@ -1,10 +1,12 @@
package com.github.collinalpert.java2db.modules;
import com.github.collinalpert.java2db.annotations.ForeignKeyEntity;
+import com.github.collinalpert.java2db.annotations.ForeignKeyPath;
import com.github.collinalpert.java2db.annotations.Ignore;
import com.github.collinalpert.java2db.database.ForeignKeyReference;
import com.github.collinalpert.java2db.database.TableColumnReference;
import com.github.collinalpert.java2db.entities.BaseEntity;
+import com.github.collinalpert.java2db.utilities.Utilities;
import java.lang.reflect.Field;
import java.util.Arrays;
@@ -94,8 +96,22 @@ private List getColumnReferences(Class extends BaseEntit
}
if (annotationModule.hasAnnotation(field, ForeignKeyEntity.class)) {
+ var foreignKeyPathInfo = annotationModule.getAnnotationInfo(field, ForeignKeyPath.class);
+ if (foreignKeyPathInfo.hasAnnotation()) {
+ var foreignKeyTableName = tableModule.getTableName(foreignKeyPathInfo.getAnnotation().foreignKeyClass());
+ var tempAlias = foreignKeyTableName.substring(0, 1) + ++aliasCounter;
+ var joinType = field.getAnnotation(ForeignKeyEntity.class).joinType();
+ fields.add(new ForeignKeyReference(tableModule.getTableName(instanceClass), alias, field, foreignKeyTableName, tempAlias, joinType));
+
+ var foreignKeyField = Utilities.tryGetValue(() -> foreignKeyPathInfo.getAnnotation().foreignKeyClass().getDeclaredField(foreignKeyPathInfo.getAnnotation().value()));
+ fields.add(new TableColumnReference(foreignKeyTableName, tempAlias, foreignKeyField));
+
+ continue;
+ }
+
var tempAlias = tableModule.getTableName(field.getType()).substring(0, 1) + ++aliasCounter;
- fields.add(new ForeignKeyReference(tableModule.getTableName(instanceClass), alias, field, tableModule.getTableName(field.getType()), tempAlias));
+ var joinType = field.getAnnotation(ForeignKeyEntity.class).joinType();
+ fields.add(new ForeignKeyReference(tableModule.getTableName(instanceClass), alias, field, tableModule.getTableName(field.getType()), tempAlias, joinType));
fields.addAll(getColumnReferences((Class extends BaseEntity>) field.getType(), tempAlias, aliasCounter++));
} else {
fields.add(new TableColumnReference(tableModule.getTableName(instanceClass), alias, field));
diff --git a/src/main/java/com/github/collinalpert/java2db/queries/SingleEntityQuery.java b/src/main/java/com/github/collinalpert/java2db/queries/SingleEntityQuery.java
index 13083a6..36194df 100644
--- a/src/main/java/com/github/collinalpert/java2db/queries/SingleEntityQuery.java
+++ b/src/main/java/com/github/collinalpert/java2db/queries/SingleEntityQuery.java
@@ -117,7 +117,7 @@ public String getQuery() {
for (var column : columns) {
var foreignKey = (ForeignKeyReference) column;
- builder.append(" left join `").append(foreignKey.getForeignKeyTableName()).append("` ").append(foreignKey.getForeignKeyAlias()).append(" on `").append(foreignKey.getAlias()).append("`.`").append(foreignKey.getForeignKeyColumnName()).append("` = `").append(foreignKey.getForeignKeyAlias()).append("`.`id`");
+ builder.append(" ").append(foreignKey.getJoinType().getSqlKeyword()).append(" join `").append(foreignKey.getForeignKeyTableName()).append("` ").append(foreignKey.getForeignKeyAlias()).append(" on `").append(foreignKey.getAlias()).append("`.`").append(foreignKey.getForeignKeyColumnName()).append("` = `").append(foreignKey.getForeignKeyAlias()).append("`.`id`");
}
builder.append(getQueryClauses(tableName));
diff --git a/src/main/java/com/github/collinalpert/java2db/services/BaseService.java b/src/main/java/com/github/collinalpert/java2db/services/BaseService.java
index 5693fdd..4effac9 100755
--- a/src/main/java/com/github/collinalpert/java2db/services/BaseService.java
+++ b/src/main/java/com/github/collinalpert/java2db/services/BaseService.java
@@ -772,13 +772,14 @@ private String getSqlValue(Field entityField, E entityInstance) {
* @param value The value to convert.
* @return The SQL version of a Java value.
*/
+ //TODO add pattern matching when switching to Java 14
private String convertToSql(Object value) {
if (value == null) {
return "null";
}
if (value instanceof String) {
- return "'" + value + "'";
+ return "'" + escapeString((String) value) + "'";
}
if (value instanceof Boolean) {
@@ -804,6 +805,16 @@ private String convertToSql(Object value) {
return value.toString();
}
+ /**
+ * Escapes characters in a String which would break an SQL statement, like a single quote or a backslash.
+ *
+ * @param input The string to escape characters in.
+ * @return The same string but with escaped characters.
+ */
+ private String escapeString(String input) {
+ return input.replace("\\", "\\\\").replace("'", "\\'");
+ }
+
/**
* An overload of the {@link #createPaginationQueries(SqlPredicate, int)} method.
* It creates queries for getting all the values from a table.