Skip to content

Commit 2d3dbe7

Browse files
authored
Merge pull request #14 from ZorTik/development
Development
2 parents e9b7a39 + 84ea8f9 commit 2d3dbe7

15 files changed

Lines changed: 295 additions & 24 deletions
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package me.zort.sqllib;
2+
3+
import java.lang.invoke.MethodHandles;
4+
import java.lang.invoke.MethodType;
5+
import java.lang.reflect.Constructor;
6+
import java.lang.reflect.Method;
7+
8+
public abstract class JVM {
9+
10+
public abstract Object invokeDefault(Class<?> declaringClass, Object instance, Method method, Object[] args) throws Throwable;
11+
12+
public static JVM getJVM() {
13+
int ver = jvmVer();
14+
if (ver < 8)
15+
throw new UnsupportedOperationException("Unsupported JVM version: " + ver);
16+
else if (ver == 8)
17+
return new JVM8();
18+
else
19+
return new JVM9();
20+
}
21+
22+
private static int jvmVer() {
23+
String version = System.getProperty("java.version");
24+
if(version.startsWith("1.")) {
25+
version = version.substring(2, 3);
26+
} else {
27+
int dot = version.indexOf(".");
28+
if(dot != -1) {
29+
version = version.substring(0, dot);
30+
}
31+
}
32+
return Integer.parseInt(version);
33+
}
34+
35+
36+
static final class JVM8 extends JVM {
37+
@Override
38+
public Object invokeDefault(Class<?> declaringClass, Object instance, Method method, Object[] args) throws Throwable {
39+
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
40+
constructor.setAccessible(true);
41+
return constructor.newInstance(declaringClass)
42+
.in(declaringClass)
43+
.unreflectSpecial(method, declaringClass)
44+
.bindTo(instance)
45+
.invokeWithArguments(args);
46+
}
47+
}
48+
49+
static final class JVM9 extends JVM {
50+
@Override
51+
public Object invokeDefault(Class<?> declaringClass, Object instance, Method method, Object[] args) throws Throwable {
52+
MethodHandles.Lookup lookup = MethodHandles.lookup();
53+
return lookup.findSpecial(declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()), declaringClass)
54+
.bindTo(instance)
55+
.invokeWithArguments(args);
56+
}
57+
}
58+
59+
}

core/src/main/java/me/zort/sqllib/SQLDatabaseConnection.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public SQLDatabaseConnection(SQLConnectionFactory connectionFactory) {
6666
*/
6767
@ApiStatus.Experimental
6868
public abstract <T> T createGate(Class<T> mappingInterface);
69+
public abstract boolean buildEntitySchema(String tableName, Class<?> entityClass);
6970

7071
/**
7172
* Saves this mapping object into database using upsert query.

core/src/main/java/me/zort/sqllib/SQLDatabaseConnectionImpl.java

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package me.zort.sqllib;
22

33
import com.google.gson.Gson;
4-
import lombok.*;
5-
import me.zort.sqllib.api.*;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Data;
6+
import lombok.Getter;
7+
import lombok.RequiredArgsConstructor;
8+
import me.zort.sqllib.api.ObjectMapper;
9+
import me.zort.sqllib.api.Query;
10+
import me.zort.sqllib.api.StatementFactory;
611
import me.zort.sqllib.api.data.QueryResult;
712
import me.zort.sqllib.api.data.QueryRowsResult;
813
import me.zort.sqllib.api.data.Row;
914
import me.zort.sqllib.api.mapping.StatementMappingFactory;
1015
import me.zort.sqllib.api.mapping.StatementMappingResultAdapter;
1116
import me.zort.sqllib.api.mapping.StatementMappingStrategy;
1217
import me.zort.sqllib.api.options.NamingStrategy;
18+
import me.zort.sqllib.api.repository.SQLTableRepository;
1319
import me.zort.sqllib.internal.Defaults;
1420
import me.zort.sqllib.internal.annotation.JsonField;
1521
import me.zort.sqllib.internal.factory.SQLConnectionFactory;
@@ -28,9 +34,13 @@
2834
import org.jetbrains.annotations.NotNull;
2935
import org.jetbrains.annotations.Nullable;
3036

31-
import java.lang.reflect.*;
37+
import java.lang.reflect.Field;
38+
import java.lang.reflect.Method;
39+
import java.lang.reflect.Modifier;
40+
import java.lang.reflect.Proxy;
3241
import java.sql.*;
3342
import java.util.*;
43+
import java.util.concurrent.CopyOnWriteArrayList;
3444

3545
/**
3646
* Main database client object implementation.
@@ -157,6 +167,9 @@ public final <T> T createGate(Class<T> mappingInterface) {
157167
Objects.requireNonNull(mappingInterface, "Mapping interface cannot be null!");
158168

159169
StatementMappingStrategy<T> statementMapping = mappingFactory.create(mappingInterface, this);
170+
171+
List<Method> pendingMethods = new CopyOnWriteArrayList<>();
172+
160173
return (T) Proxy.newProxyInstance(mappingInterface.getClassLoader(),
161174
new Class[]{mappingInterface}, (proxy, method, args) -> {
162175

@@ -170,10 +183,28 @@ public final <T> T createGate(Class<T> mappingInterface) {
170183
return mappingResultAdapter.adaptResult(method, result);
171184
}
172185

186+
// Default methods are invoked normally.
187+
if (declaringClass.isInterface() && method.isDefault()) {
188+
return JVM.getJVM().invokeDefault(declaringClass, proxy, method, args);
189+
}
190+
173191
throw new UnsupportedOperationException("Method " + method.getName() + " is not supported by this mapping repository!");
174192
});
175193
}
176194

195+
@SuppressWarnings("unchecked, rawtypes")
196+
@ApiStatus.Experimental
197+
public final boolean buildEntitySchema(String tableName, Class<?> entityClass) {
198+
Objects.requireNonNull(entityClass, "Entity class cannot be null!");
199+
200+
SQLTableRepository repository = new SQLTableRepositoryBuilder()
201+
.withConnection(this)
202+
.withTableName(tableName)
203+
.withTypeClass(entityClass)
204+
.build();
205+
return repository.createTable();
206+
}
207+
177208
/**
178209
* Performs new query and returns the result. This result is never null.
179210
* See: {@link QueryRowsResult#isSuccessful()}
@@ -216,6 +247,10 @@ public <T> QueryRowsResult<T> query(Query query, Class<T> typeClass) {
216247
*/
217248
@Override
218249
public QueryRowsResult<Row> query(Query query) {
250+
return doQuery(query, false);
251+
}
252+
253+
private QueryRowsResult<Row> doQuery(Query query, boolean isRetry) {
219254
Objects.requireNonNull(query);
220255

221256
if(!handleAutoReconnect()) {
@@ -241,6 +276,11 @@ public QueryRowsResult<Row> query(Query query) {
241276

242277
return result;
243278
} catch (SQLException e) {
279+
if (!isRetry && e.getMessage().contains("database connection closed")) {
280+
reconnect();
281+
return doQuery(query, true);
282+
}
283+
244284
logSqlError(e);
245285
return new QueryRowsResult<>(false, e.getMessage());
246286
}
@@ -257,13 +297,22 @@ public QueryRowsResult<Row> query(Query query) {
257297
* about success state of the request.
258298
*/
259299
public QueryResult exec(Query query) {
300+
return doExec(query, false);
301+
}
302+
303+
private QueryResult doExec(Query query, boolean isRetry) {
260304
if(!handleAutoReconnect()) {
261305
return new QueryResultImpl(false, "Cannot connect to database!");
262306
}
263307
try(PreparedStatement stmt = buildStatement(query)) {
264308
stmt.execute();
265309
return new QueryResultImpl(true);
266310
} catch (SQLException e) {
311+
if (!isRetry && e.getMessage().contains("database connection closed")) {
312+
reconnect();
313+
return doExec(query, true);
314+
}
315+
267316
logSqlError(e);
268317
return new QueryResultImpl(false, e.getMessage());
269318
}
@@ -349,11 +398,16 @@ protected Pair<String[], UnknownValueWrapper[]> buildDefsVals(Object obj) {
349398
@SuppressWarnings("all")
350399
private boolean handleAutoReconnect() {
351400
if(options.isAutoReconnect() && !isConnected()) {
352-
debug("Trying to make a new connection with the database!");
353-
if(!connect()) {
354-
debug("Cannot make new connection!");
355-
return false;
356-
}
401+
return reconnect();
402+
}
403+
return true;
404+
}
405+
406+
private boolean reconnect() {
407+
debug("Trying to make a new connection with the database!");
408+
if(!connect()) {
409+
debug("Cannot make new connection!");
410+
return false;
357411
}
358412
return true;
359413
}

core/src/main/java/me/zort/sqllib/SQLiteDatabaseConnectionImpl.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.zort.sqllib;
22

3+
import me.zort.sqllib.api.Query;
34
import me.zort.sqllib.api.data.QueryResult;
45
import me.zort.sqllib.api.data.QueryRowsResult;
56
import me.zort.sqllib.api.data.Row;
@@ -132,8 +133,10 @@ public QueryResult upsert(String table, PrimaryKey primaryKey, InsertQuery inser
132133
}
133134

134135
@Override
135-
public UpsertQuery upsert(@Nullable String table) {
136-
throw new IllegalStatementOperationException("Default upsert is not supported by SQLite!");
137-
}
136+
public QueryResult exec(Query query) {
137+
if (query instanceof UpsertQuery && ((UpsertQuery) query).getAssignedSaveObject() != null)
138+
return save(((UpsertQuery) query).getTable(), ((UpsertQuery) query).getAssignedSaveObject());
138139

140+
return super.exec(query);
141+
}
139142
}

core/src/main/java/me/zort/sqllib/internal/query/InsertQuery.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ public QueryDetails buildQueryDetails() {
8585
details.append(" VALUES ");
8686
insertArray(details, values, true);
8787

88+
details.append(buildInnerQuery());
89+
8890
return details;
8991
}
9092

core/src/main/java/me/zort/sqllib/internal/query/QueryDetails.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ protected Pair<String, Object[]> buildStatementDetails() {
6868
Map<Integer, Object> valuesUnsorted = new HashMap<>();
6969

7070
int i = 0;
71+
String queryCloned = query;
7172
for (String placeholder : this.values.keySet()) {
7273
Object value = this.values.get(placeholder);
7374

@@ -76,7 +77,7 @@ protected Pair<String, Object[]> buildStatementDetails() {
7677
if (Util.count(queryStr, placeholder) != 1)
7778
throw new RuntimeException("Placeholder " + placeholder + " is not unique in query " + queryStr);
7879

79-
valuesUnsorted.put(query.indexOf(placeholder), value);
80+
valuesUnsorted.put(queryCloned.indexOf(placeholder), value);
8081
query = query.replaceAll(placeholder, "?");
8182

8283
i++;

core/src/main/java/me/zort/sqllib/internal/query/UpsertQuery.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package me.zort.sqllib.internal.query;
22

3+
import lombok.Getter;
4+
import lombok.Setter;
35
import me.zort.sqllib.SQLDatabaseConnection;
46
import me.zort.sqllib.internal.query.part.SetStatement;
57
import org.jetbrains.annotations.Nullable;
@@ -8,6 +10,10 @@
810

911
public class UpsertQuery extends InsertQuery {
1012

13+
@Setter
14+
@Getter
15+
private Object assignedSaveObject = null; // Can be null, only relevant in SQLite mode.
16+
1117
public UpsertQuery(SQLDatabaseConnection connection) {
1218
super(connection);
1319
}
@@ -54,11 +60,6 @@ public QueryDetails buildQueryDetails() {
5460
return stmt;
5561
}
5662

57-
@Override
58-
public String buildQuery() {
59-
return super.buildQuery();
60-
}
61-
6263
@Override
6364
public UpsertQuery then(String part) {
6465
return (UpsertQuery) super.then(part);

core/src/main/java/me/zort/sqllib/mapping/DefaultStatementMapping.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import lombok.RequiredArgsConstructor;
44
import me.zort.sqllib.SQLDatabaseConnection;
55
import me.zort.sqllib.api.SQLConnection;
6+
import me.zort.sqllib.api.data.QueryRowsResult;
67
import me.zort.sqllib.api.mapping.StatementMappingStrategy;
78
import me.zort.sqllib.api.data.QueryResult;
89
import me.zort.sqllib.internal.query.QueryNode;
910
import me.zort.sqllib.internal.query.QueryNodeRequest;
11+
import me.zort.sqllib.mapping.annotation.Append;
1012
import me.zort.sqllib.mapping.exception.SQLMappingException;
1113
import me.zort.sqllib.util.ParameterPair;
1214
import org.jetbrains.annotations.Nullable;
@@ -54,6 +56,16 @@ public QueryResult executeQuery(Method method, Object[] args, @Nullable Class<?>
5456
}
5557

5658
QueryNode<?> node = wrappedAnnotation.getQueryBuilder().build(connection, queryAnnotation, method, parameters);
59+
60+
if (method.isAnnotationPresent(Append.class)) {
61+
Append append = method.getAnnotation(Append.class);
62+
node.then(new PlaceholderMapper(parameters).assignValues(append.value()));
63+
}
64+
65+
if (mapTo != null && wrappedAnnotation.isProducesResult() && QueryRowsResult.class.isAssignableFrom(mapTo)) {
66+
return ((SQLDatabaseConnection) connection).query(node);
67+
}
68+
5769
if (wrappedAnnotation.isProducesResult() && node instanceof QueryNodeRequest) {
5870
return mapTo != null
5971
? ((SQLDatabaseConnection) connection).query(node, mapTo)

core/src/main/java/me/zort/sqllib/mapping/QueryAnnotation.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
import me.zort.sqllib.api.SQLConnection;
77
import me.zort.sqllib.internal.query.QueryNode;
88
import me.zort.sqllib.mapping.annotation.*;
9-
import me.zort.sqllib.mapping.builder.DeleteQueryBuilder;
10-
import me.zort.sqllib.mapping.builder.InsertQueryBuilder;
11-
import me.zort.sqllib.mapping.builder.SaveQueryBuilder;
12-
import me.zort.sqllib.mapping.builder.SelectQueryBuilder;
9+
import me.zort.sqllib.mapping.builder.*;
1310
import me.zort.sqllib.mapping.exception.SQLMappingException;
1411
import me.zort.sqllib.util.ParameterPair;
1512
import org.jetbrains.annotations.Nullable;
@@ -42,7 +39,8 @@ public class QueryAnnotation {
4239
QUERY_ANNOT.put(Delete.class, new QueryAnnotation(false, new DeleteQueryBuilder()));
4340
QUERY_ANNOT.put(Save.class, new QueryAnnotation(false, new SaveQueryBuilder()));
4441
QUERY_ANNOT.put(Insert.class, new QueryAnnotation(false, new InsertQueryBuilder()));
45-
// TODO: Populate
42+
QUERY_ANNOT.put(Query.class, new QueryAnnotation(true, new NativeQueryBuilder()));
43+
QUERY_ANNOT.put(Exec.class, new QueryAnnotation(false, new NativeQueryBuilder()));
4644
}
4745

4846
@Nullable
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package me.zort.sqllib.mapping.annotation;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target(ElementType.METHOD)
10+
public @interface Append {
11+
12+
String value();
13+
14+
}

0 commit comments

Comments
 (0)