Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/de/mineking/javautils/database/Column.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
String name() default "";

boolean key() default false;

boolean unique() default false;

String modifier() default "";
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/de/mineking/javautils/database/DataClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ default T insert() throws ConflictException {
return getTable().insert((T) this);
}

@NotNull
@SuppressWarnings("unchecked")
default boolean update() {
default T update() throws ConflictException {
return getTable().update((T) this);
}

@NotNull
@SuppressWarnings("unchecked")
default T upsert() {
default T upsert() throws ConflictException {
return getTable().upsert((T) this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private class TableBuilder<O, T extends Table<O>> {
public TableBuilder(Class<T> table, Class<O> type, Supplier<O> instance, String name) {
this.table = (T) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[]{table},
new Class<?>[] {table},
new TableImpl<>(DatabaseManager.this, this::getTable, type, instance, name)
);
}
Expand Down
16 changes: 13 additions & 3 deletions src/main/java/de/mineking/javautils/database/Table.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public interface Table<T> {
@NotNull
Map<String, Field> getColumns();

@NotNull
Map<String, Field> getUnique();

@NotNull
Map<String, Field> getKeys();

Expand Down Expand Up @@ -49,15 +52,22 @@ default List<T> selectAll() {
@NotNull
T insert(@NotNull T object) throws ConflictException;

boolean update(@NotNull T object);
@NotNull
T update(@NotNull T object) throws ConflictException;

@NotNull
T upsert(@NotNull T object);
default T upsert(@NotNull T object) throws ConflictException {
try {
return insert(object);
} catch(ConflictException e) {
return update(object);
}
}

int delete(@NotNull Where where);

default int delete(@NotNull T object) {
return delete(Where.of(this, object));
return delete(Where.identify(this, object));
}

default int deleteAll() {
Expand Down
187 changes: 72 additions & 115 deletions src/main/java/de/mineking/javautils/database/TableImpl.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package de.mineking.javautils.database;

import de.mineking.javautils.database.exception.ConflictException;
import org.jdbi.v3.core.result.ResultProducers;
import org.jdbi.v3.core.statement.StatementContext;
import org.jdbi.v3.core.statement.Update;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -85,6 +84,12 @@ public Map<String, Field> getColumns() {
return columns;
}

@NotNull
@Override
public Map<String, Field> getUnique() {
return unique;
}

@NotNull
@Override
public Map<String, Field> getKeys() {
Expand Down Expand Up @@ -145,7 +150,7 @@ public int delete(@NotNull Where where) {
);
}

private boolean execute(@NotNull T object, Update query) {
private boolean execute(@NotNull T object, @NotNull Update query) {
columns.forEach((name, field) -> {
try {
query.bind(name, manager.getArgument(field.getGenericType(), field, field.get(object)));
Expand All @@ -154,134 +159,86 @@ private boolean execute(@NotNull T object, Update query) {
}
});

return query.execute(ResultProducers.returningResults())
.map((rs, ctx) -> {
columns.forEach((name, field) -> {
try {
field.set(object, manager.parse(field.getGenericType(), field, manager.extract(field.getGenericType(), field, name, rs)));
} catch(IllegalAccessException | SQLException e) {
throw new RuntimeException(e);
}
});
return true;
}).findFirst().orElse(false);
return query.execute((statementSupplier, ctx) -> {
var stmt = statementSupplier.get();
var rs = stmt.getResultSet();

if(rs.next()) {
columns.forEach((name, field) -> {
try {
field.set(object, manager.parse(field.getGenericType(), field, manager.extract(field.getGenericType(), field, name, rs)));
} catch(IllegalAccessException | SQLException e) {
throw new RuntimeException(e);
}
});
return true;
} else return false;
});
}

@NotNull
@Override
public T insert(@NotNull T object) throws ConflictException {
var sql = "insert into <name>(<columns>) values(<values>) on conflict do nothing returning *";

var updated = manager.db.withHandle(handle -> {
var query = handle.createUpdate(sql)
.define("name", name)
.define("columns", columns.entrySet().stream()
.filter(e -> {
try {
return !(e.getValue().getAnnotation(Column.class).autoincrement() && ((Number) e.getValue().get(object)).longValue() <= 0);
} catch(IllegalAccessException ex) {
throw new RuntimeException(ex);
}
})
.map(e -> '"' + e.getKey() + '"')
.collect(Collectors.joining(", "))
)
.define("values", columns.entrySet().stream()
.filter(e -> {
try {
return !(e.getValue().getAnnotation(Column.class).autoincrement() && ((Number) e.getValue().get(object)).longValue() <= 0);
} catch(IllegalAccessException ex) {
throw new RuntimeException(ex);
}
})
.map(e -> ":" + e.getKey())
.collect(Collectors.joining(", "))
);

return execute(object, query);
});
var check = Where.detectConflict(this, object, true);
var sql = "insert into <name>(<columns>) (select <values> where not exists (select from <name> <where>)) returning *";

var updated = manager.db.withHandle(handle -> execute(object, handle.createUpdate(sql)
.define("name", name)
.define("columns", columns.entrySet().stream()
.filter(e -> {
try {
return !(e.getValue().getAnnotation(Column.class).autoincrement() && ((Number) e.getValue().get(object)).longValue() <= 0);
} catch(IllegalAccessException ex) {
throw new RuntimeException(ex);
}
})
.map(e -> '"' + e.getKey() + '"')
.collect(Collectors.joining(", "))
)
.define("values", columns.entrySet().stream()
.filter(e -> {
try {
return !(e.getValue().getAnnotation(Column.class).autoincrement() && ((Number) e.getValue().get(object)).longValue() <= 0);
} catch(IllegalAccessException ex) {
throw new RuntimeException(ex);
}
})
.map(e -> ":" + e.getKey())
.collect(Collectors.joining(", "))
)
.define("where", check.format())
.bindMap(check.formatValues(this))
));

if(updated) return object;
else throw new ConflictException();
}

@NotNull
@Override
public boolean update(@NotNull T object) {
var sql = "update <name> set <update> <where>";
var identifier = Where.of(this, object);

if(unique.size() > keys.size()) sql += " and 0 not in (select 0 from <name> <where> and not (<unique>))";
public T update(@NotNull T object) throws ConflictException {
var identifier = Where.identify(this, object);
var unique = Where.detectConflict(this, object, false);

var sql = "update <name> set <update> <where>";
if(this.unique.size() > keys.size()) sql += " and not exists (select from <name> <unique>)";
final var fSql = sql + " returning *";

return manager.db.withHandle(handle -> {
var query = handle.createUpdate(fSql)
.define("name", name)
.define("update", columns.keySet().stream()
.filter(k -> !this.keys.containsKey(k))
.map(k -> '"' + k + "\" = :" + k)
.collect(Collectors.joining(", "))
)
.define("where", identifier.format())
.define("unique", unique.keySet().stream()
.filter(k -> !this.keys.containsKey(k))
.map(k -> '"' + k + "\" = :" + k)
.collect(Collectors.joining(", "))
)
.bindMap(identifier.formatValues(this));

return execute(object, query);
});
}

@NotNull
@Override
public T upsert(@NotNull T object) {
var sql = "insert into <name>(<columns>) values(<values>) ";

if(!unique.isEmpty()) sql += " on conflict(<unique>) do update set <update>";

var fSql = sql; //Because java

manager.db.useHandle(handle -> {
var query = handle.createUpdate(fSql)
.define("name", name)
.define("columns", columns.entrySet().stream()
.filter(e -> {
try {
return !(e.getValue().getAnnotation(Column.class).autoincrement() && ((Number) e.getValue().get(object)).longValue() <= 0);
} catch(IllegalAccessException ex) {
throw new RuntimeException(ex);
}
})
.map(e -> '"' + e.getKey() + '"')
.collect(Collectors.joining(", "))
)
.define("values", columns.entrySet().stream()
.filter(e -> {
try {
return !(e.getValue().getAnnotation(Column.class).autoincrement() && ((Number) e.getValue().get(object)).longValue() <= 0);
} catch(IllegalAccessException ex) {
throw new RuntimeException(ex);
}
})
.map(e -> ":" + e.getKey())
.collect(Collectors.joining(", "))
)
.define("unique", this.unique.keySet().stream()
.map(k -> '"' + k + '"')
.collect(Collectors.joining(", "))
)
.define("update", columns.keySet().stream()
.filter(k -> !this.keys.containsKey(k))
.map(k -> '"' + k + "\" = :" + k)
.collect(Collectors.joining(", "))
);

execute(object, query);
});
var updated = manager.db.withHandle(handle -> execute(object, handle.createUpdate(fSql)
.define("name", name)
.define("update", columns.keySet().stream()
.filter(k -> !this.keys.containsKey(k))
.map(k -> '"' + k + "\" = :" + k)
.collect(Collectors.joining(", "))
)
.define("where", identifier.format())
.define("unique", unique.format())
.bindMap(identifier.formatValues(this))
.bindMap(unique.formatValues(this))
));

return object;
if(updated) return object;
else throw new ConflictException();
}

@Override
Expand Down
21 changes: 19 additions & 2 deletions src/main/java/de/mineking/javautils/database/Where.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import de.mineking.javautils.ID;
import de.mineking.javautils.Pair;
import org.jdbi.v3.core.argument.Argument;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -14,7 +15,7 @@

public interface Where {
@NotNull
static <T> Where of(@NotNull Table<T> table, @NotNull T object) {
static <T> Where identify(@NotNull Table<T> table, @NotNull T object) {
if(table.getKeys().isEmpty()) throw new IllegalArgumentException("Cannot identify object without keys");
return allOf(table.getKeys().entrySet().stream()
.map(e -> {
Expand All @@ -28,6 +29,22 @@ static <T> Where of(@NotNull Table<T> table, @NotNull T object) {
);
}

@NotNull
static <T> Where detectConflict(@NotNull Table<T> table, @NotNull T object, boolean includeKeys) {
if(table.getUnique().isEmpty()) return empty();
return Where.anyOf(table.getUnique().entrySet().stream()
.filter(e -> !e.getValue().getAnnotation(Column.class).key() || includeKeys)
.map(e -> {
try {
return equals(e.getKey(), e.getValue().get(object));
} catch(IllegalAccessException ex) {
throw new RuntimeException(ex);
}
})
.toList()
).and(includeKeys ? empty() : not(identify(table, object)));
}

@NotNull
static Where empty() {
return new Where() {
Expand Down Expand Up @@ -165,7 +182,7 @@ default Where not() {
Map<String, Pair<String, Object>> values();

@SuppressWarnings({"rawtypes", "unchecked"})
default Map<String, Object> formatValues(@NotNull Table<?> table) {
default Map<String, Argument> formatValues(@NotNull Table<?> table) {
return values().entrySet().stream()
.map(e -> {
Field f = table.getColumns().get(e.getValue().key());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ public interface ThrowingConsumer<E extends Throwable, T> {
*/
default ThrowingConsumer<E, T> andThen(ThrowingConsumer<E, ? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
return (T t) -> {
accept(t);
after.accept(t);
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ public interface ThrowingFunction<E extends Throwable, T, R> {
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(ThrowingFunction)
*/
default <V> ThrowingFunction<E, V, R> compose(ThrowingFunction<E, ? super V, ? extends T> before) {
Expand All @@ -39,13 +38,12 @@ default <V> ThrowingFunction<E, V, R> compose(ThrowingFunction<E, ? super V, ? e
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(ThrowingFunction)
*/
default <V> ThrowingFunction<E, T, V> andThen(ThrowingFunction<E, ? super R, ? extends V> after) {
Expand Down
Loading