Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
716e2934cc | |||
2626ba7ffd | |||
b28d9e0645 | |||
edbb75d292 | |||
6b215cc337 | |||
2ec91e3dcf | |||
e8f3b45c68 | |||
3567912fd9 | |||
32e8e2deb8 | |||
f44e19f6d5 | |||
bd8e3b284b | |||
a5d7837c7d | |||
06a716dc00 | |||
7a168bbb05 | |||
71af157e3a | |||
fd45d8af02 | |||
f2d5f203c6 | |||
0c2465df58 | |||
caebbc59a0 | |||
3119173d87 | |||
0742ab40d7 | |||
edc88bdb9a | |||
0f75a1c261 | |||
1b17ed8d79 | |||
98016830dd | |||
ba15dfc9d4 |
@ -2,7 +2,7 @@ name: Publish Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches: [master, develop]
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
@ -58,14 +58,13 @@ jobs:
|
||||
|
||||
# Publish to Maven
|
||||
- name: Publish to Maven
|
||||
run: |
|
||||
chmod +x ./scripts/deploy.sh
|
||||
./scripts/deploy.sh
|
||||
run: mvn deploy -Pgen-javadocs -B -Dstyle.color=always --update-snapshots -T6C -e
|
||||
|
||||
# Generate changelog
|
||||
- name: Generate Changelog
|
||||
id: changelog
|
||||
uses: https://github.com/mathiasvr/command-output@v2.0.0
|
||||
if: ${{ github.ref.action == 'master' }}
|
||||
with:
|
||||
run: |
|
||||
npx conventional-changelog-cli -p angular -i CHANGELOG.md -s --skip-unstable
|
||||
@ -76,6 +75,7 @@ jobs:
|
||||
# so we can use it later
|
||||
- name: Extract Maven project version
|
||||
shell: bash -l {0}
|
||||
if: ${{ github.ref.action == 'master' }}
|
||||
run: |
|
||||
RELEASE_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
|
||||
echo "Project version: $RELEASE_VERSION"
|
||||
@ -85,6 +85,7 @@ jobs:
|
||||
- name: Publish Release
|
||||
id: publish-release
|
||||
uses: https://git.rainnny.club/Rainnny/release-action@main
|
||||
if: ${{ github.ref.action == 'master' }}
|
||||
with:
|
||||
api_key: ${{ secrets.RELEASE_ACCESS_TOKEN }}
|
||||
tag: Feather-${{ env.RELEASE_VERSION }}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,6 +18,7 @@ cmake-build-*/
|
||||
out/
|
||||
build/
|
||||
work/
|
||||
target/
|
||||
.idea_modules/
|
||||
atlassian-ide-plugin.xml
|
||||
com_crashlytics_export_strings.xml
|
||||
|
@ -91,9 +91,6 @@
|
||||
<!-- See: https://checkstyle.org/checks/javadoc/javadocmissingwhitespaceafterasterisk.html#JavadocMissingWhitespaceAfterAsterisk -->
|
||||
<module name="JavadocMissingWhitespaceAfterAsterisk" />
|
||||
|
||||
<!-- See: https://checkstyle.org/checks/javadoc/javadocparagraph.html#JavadocParagraph -->
|
||||
<module name="JavadocParagraph" />
|
||||
|
||||
<!-- See: https://checkstyle.org/checks/javadoc/javadocstyle.html#JavadocStyle -->
|
||||
<module name="JavadocStyle" />
|
||||
|
||||
@ -215,11 +212,6 @@
|
||||
<!-- See: https://checkstyle.org/checks/javadoc/singlelinejavadoc.html#SingleLineJavadoc -->
|
||||
<module name="SingleLineJavadoc" />
|
||||
|
||||
<!-- See: https://checkstyle.org/checks/javadoc/requireemptylinebeforeblocktaggroup.html#RequireEmptyLineBeforeBlockTagGroup -->
|
||||
<module name="RequireEmptyLineBeforeBlockTagGroup">
|
||||
<property name="violateExecutionOnNonTightHtml" value="true" />
|
||||
</module>
|
||||
|
||||
<!-- See: https://checkstyle.org/checks/whitespace/singlespaceseparator.html#SingleSpaceSeparator -->
|
||||
<module name="SingleSpaceSeparator" />
|
||||
|
||||
|
49
pom.xml
49
pom.xml
@ -7,7 +7,7 @@
|
||||
<!--Project Details-->
|
||||
<groupId>me.braydon</groupId>
|
||||
<artifactId>Feather</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>1.0.1-dev</version>
|
||||
|
||||
<properties>
|
||||
<java.version>8</java.version>
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!--Used for compiling the source code with the proper Java version-->
|
||||
<!-- Used for compiling the source code with the proper Java version -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
@ -27,13 +27,13 @@
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
|
||||
<!--Enable incremental builds, this is reversed due to-->
|
||||
<!--a bug as seen in https://issues.apache.org/jira/browse/MCOMPILER-209-->
|
||||
<!-- Enable incremental builds, this is reversed due to -->
|
||||
<!-- a bug as seen in https://issues.apache.org/jira/browse/MCOMPILER-209 -->
|
||||
<useIncrementalCompilation>false</useIncrementalCompilation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!--Handles shading of dependencies in the final output jar-->
|
||||
<!-- Handles shading of dependencies in the final output jar -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
@ -70,7 +70,7 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--Used for generating a git properties file during build-->
|
||||
<!-- Used for generating a git properties file during build -->
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
@ -168,6 +168,13 @@
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>rainnny-repo-public</id>
|
||||
<url>https://maven.rainnny.club/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<!-- Depends -->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -176,31 +183,43 @@
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>32.1.3-jre</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-common</artifactId>
|
||||
<version>4.1.104.Final</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Databases -->
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongodb-driver-sync</artifactId>
|
||||
<version>4.11.1</version>
|
||||
<scope>compile</scope>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
<version>6.3.0.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
<version>7.0.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>4.0.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Switch dir
|
||||
cd "$(dirname "$0")/.." || exit
|
||||
|
||||
# Deploy
|
||||
mvn deploy -Pgen-javadocs -B -Dstyle.color=always --update-snapshots -T6C -e
|
20
src/main/java/me/braydon/feather/annotation/RawData.java
Normal file
20
src/main/java/me/braydon/feather/annotation/RawData.java
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
|
||||
*
|
||||
* For inquiries, please contact braydonrainnny@gmail.com
|
||||
*/
|
||||
package me.braydon.feather.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* {@link Field}'s tagged with this annotation
|
||||
* will be set to the raw data from the document
|
||||
* it is in.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented @Inherited
|
||||
public @interface RawData { }
|
@ -7,16 +7,17 @@ package me.braydon.feather.data;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.ToString;
|
||||
import me.braydon.feather.FeatherSettings;
|
||||
import me.braydon.feather.annotation.Field;
|
||||
import me.braydon.feather.annotation.Id;
|
||||
import me.braydon.feather.annotation.RawData;
|
||||
import me.braydon.feather.annotation.Serializable;
|
||||
import me.braydon.feather.common.FieldUtils;
|
||||
import me.braydon.feather.common.Tuple;
|
||||
import me.braydon.feather.database.IDatabase;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@ -29,10 +30,9 @@ import java.util.UUID;
|
||||
* document is universal between all {@link IDatabase}'s.
|
||||
*
|
||||
* @author Braydon
|
||||
* @param <V> the type of value this document holds
|
||||
*/
|
||||
@ThreadSafe @Getter @ToString
|
||||
public class Document<V> {
|
||||
@Getter @ToString
|
||||
public class Document {
|
||||
/**
|
||||
* The key to use for the id field.
|
||||
*/
|
||||
@ -51,15 +51,20 @@ public class Document<V> {
|
||||
* a tuple that contains the Java field, as well as
|
||||
* the field value.
|
||||
* </p>
|
||||
*
|
||||
* @see V for value type
|
||||
*/
|
||||
private final Map<String, Tuple<java.lang.reflect.Field, V>> mappedData = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||
private final Map<String, Tuple<java.lang.reflect.Field, Object>> mappedData = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||
|
||||
@SneakyThrows
|
||||
public Document(@NonNull Object element) {
|
||||
Class<?> clazz = element.getClass(); // Get the element class
|
||||
String idKey = null; // The key for the id field
|
||||
java.lang.reflect.Field rawDataField = null; // The raw data field if defined
|
||||
for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {
|
||||
// Raw data field, save it for later
|
||||
if (field.isAnnotationPresent(RawData.class)) {
|
||||
rawDataField = field;
|
||||
continue;
|
||||
}
|
||||
// Field is missing the @Field annotation, skip it
|
||||
if (!field.isAnnotationPresent(Field.class)) {
|
||||
continue;
|
||||
@ -72,24 +77,24 @@ public class Document<V> {
|
||||
idKey = key;
|
||||
}
|
||||
Class<?> fieldType = field.getType(); // The type of the field
|
||||
try {
|
||||
Object value = field.get(element); // The value of the field
|
||||
|
||||
if (field.isAnnotationPresent(Serializable.class)) { // Serialize the field if @Serializable is present
|
||||
value = FeatherSettings.getGson().toJson(field.get(element));
|
||||
} else if (fieldType == UUID.class) { // Convert UUIDs into strings
|
||||
value = value.toString();
|
||||
}
|
||||
|
||||
mappedData.put(key, new Tuple<>(field, (V) value)); // Store in our map
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
Object value = field.get(element); // The value of the field
|
||||
|
||||
if (field.isAnnotationPresent(Serializable.class)) { // Serialize the field if @Serializable is present
|
||||
value = FeatherSettings.getGson().toJson(field.get(element));
|
||||
} else if (fieldType == UUID.class) { // Convert UUIDs into strings
|
||||
value = value.toString();
|
||||
}
|
||||
|
||||
mappedData.put(key, new Tuple<>(field, value)); // Store in our map
|
||||
}
|
||||
assert idKey != null; // We need an id key
|
||||
if (rawDataField != null) { // We have a raw data field, set it
|
||||
rawDataField.setAccessible(true); // Make our field accessible
|
||||
rawDataField.set(element, toMappedData()); // Set the raw data field to our mapped document
|
||||
}
|
||||
this.idKey = idKey; // Set our id key
|
||||
|
||||
Tuple<java.lang.reflect.Field, V> key = mappedData.get(idKey); // Get the id from the data map
|
||||
Tuple<java.lang.reflect.Field, Object> key = mappedData.get(idKey); // Get the id from the data map
|
||||
if (key == null) { // The element is missing an id field
|
||||
throw new IllegalArgumentException("No @Id annotated field found in " + clazz.getSimpleName());
|
||||
}
|
||||
@ -103,9 +108,9 @@ public class Document<V> {
|
||||
* @see #mappedData for stored data
|
||||
*/
|
||||
@NonNull
|
||||
public Map<String, V> toMappedData() {
|
||||
Map<String, V> mappedData = new LinkedHashMap<>(); // The mapped data
|
||||
for (Map.Entry<String, Tuple<java.lang.reflect.Field, V>> entry : this.mappedData.entrySet()) {
|
||||
public Map<String, Object> toMappedData() {
|
||||
Map<String, Object> mappedData = new LinkedHashMap<>(); // The mapped data
|
||||
for (Map.Entry<String, Tuple<java.lang.reflect.Field, Object>> entry : this.mappedData.entrySet()) {
|
||||
mappedData.put(entry.getKey(), entry.getValue().getRight());
|
||||
}
|
||||
return mappedData;
|
||||
|
@ -29,6 +29,7 @@ public interface IDatabase<B, C> extends Closeable {
|
||||
*
|
||||
* @param credentials the optional credentials to use
|
||||
* @throws IllegalStateException if already connected
|
||||
* @see C for credentials
|
||||
*/
|
||||
void connect(C credentials) throws IllegalStateException;
|
||||
|
||||
|
@ -10,6 +10,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.braydon.feather.FeatherSettings;
|
||||
import me.braydon.feather.annotation.RawData;
|
||||
import me.braydon.feather.annotation.Serializable;
|
||||
import me.braydon.feather.common.FieldUtils;
|
||||
|
||||
@ -21,7 +22,7 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A repository belonging to a {@link IDatabase}.
|
||||
* A repository belonging to an {@link IDatabase}.
|
||||
*
|
||||
* @author Braydon
|
||||
* @param <D> the database this repository uses
|
||||
@ -120,8 +121,18 @@ public abstract class Repository<D extends IDatabase<?, ?>, ID, E> {
|
||||
Constructor<? extends E> constructor = entityClass.getConstructor(); // Get the no args constructor
|
||||
E entity = constructor.newInstance(); // Create the entity
|
||||
|
||||
// Get the field tagged with @Id
|
||||
// Get the field tagged with @Field
|
||||
java.lang.reflect.Field rawDataField = null; // The raw data field if defined
|
||||
for (Field field : entityClass.getDeclaredFields()) {
|
||||
// Raw data field, save it for later
|
||||
if (field.isAnnotationPresent(RawData.class)) {
|
||||
rawDataField = field;
|
||||
continue;
|
||||
}
|
||||
// Not the field we're looking for
|
||||
if (!field.isAnnotationPresent(me.braydon.feather.annotation.Field.class)) {
|
||||
continue;
|
||||
}
|
||||
String key = FieldUtils.extractKey(field); // The key of the database field
|
||||
Class<?> type = field.getType(); // The type of the field
|
||||
Object value = mappedData.get(key); // The value of the field
|
||||
@ -137,6 +148,10 @@ public abstract class Repository<D extends IDatabase<?, ?>, ID, E> {
|
||||
field.setAccessible(true);
|
||||
field.set(entity, value);
|
||||
}
|
||||
if (rawDataField != null) { // We have a raw data field, set it
|
||||
rawDataField.setAccessible(true); // Make our field accessible
|
||||
rawDataField.set(entity, mappedData); // Set the raw data field to our mapped document
|
||||
}
|
||||
return entity;
|
||||
} catch (NoSuchMethodException ex) { // We need our no args constructor
|
||||
throw new IllegalStateException("Entity " + entityClass.getName() + " is missing no args constructor");
|
||||
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
|
||||
*
|
||||
* For inquiries, please contact braydonrainnny@gmail.com
|
||||
*/
|
||||
package me.braydon.feather.database.impl.mariadb;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import lombok.NonNull;
|
||||
import me.braydon.feather.database.IDatabase;
|
||||
|
||||
/**
|
||||
* The {@link IDatabase} implementation for MariaDB (and other MySQL servers).
|
||||
*
|
||||
* @author Braydon
|
||||
* @see HikariDataSource for the bootstrap class
|
||||
* @see MariaDBAuthorization for the credentials class
|
||||
* @see <a href="https://github.com/brettwooldridge/HikariCP">HikariCP Official GitHub</a>
|
||||
*/
|
||||
public class MariaDB implements IDatabase<HikariDataSource, MariaDBAuthorization> {
|
||||
/**
|
||||
* The current {@link HikariDataSource} instance.
|
||||
*/
|
||||
private HikariDataSource dataSource;
|
||||
|
||||
/**
|
||||
* Get the name of this database.
|
||||
*
|
||||
* @return the database name
|
||||
*/
|
||||
@Override @NonNull
|
||||
public String getName() {
|
||||
return "MariaDB";
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a connection to this database.
|
||||
*
|
||||
* @param credentials the optional credentials to use
|
||||
* @throws IllegalArgumentException if no credentials are provided
|
||||
* @throws IllegalStateException if already connected
|
||||
* @see MariaDBAuthorization for credentials
|
||||
*/
|
||||
@Override
|
||||
public void connect(MariaDBAuthorization credentials) throws IllegalArgumentException, IllegalStateException {
|
||||
// if (credentials == null) { // We need valid credentials
|
||||
// throw new IllegalArgumentException("No credentials defined");
|
||||
// }
|
||||
// if (isConnected()) { // Already connected
|
||||
// throw new IllegalStateException("Already connected");
|
||||
// }
|
||||
// if (dataSource != null) { // We have a data source, close it first
|
||||
// dataSource.close();
|
||||
// }
|
||||
// HikariConfig config = credentials.getHikariConfig(); // Get the custom config
|
||||
// if (config == null) { // No custom config, make a new one
|
||||
// config = new HikariConfig();
|
||||
// }
|
||||
// config.setJdbcUrl(credentials.getJdbcUrl()); // Set the JDBC connection URL
|
||||
// config.setUsername(credentials.getUsername()); // Set the username
|
||||
// config.setPassword(credentials.getPassword()); // Set the password
|
||||
// dataSource = new HikariDataSource(config); // Create a new data source
|
||||
throw new UnsupportedOperationException(); // Not impl yet
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this database is connected.
|
||||
*
|
||||
* @return the database connection state
|
||||
*/
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return dataSource != null && (dataSource.isRunning() && !dataSource.isClosed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latency to this database.
|
||||
*
|
||||
* @return the latency, -1 if not connected
|
||||
*/
|
||||
@Override
|
||||
public long getLatency() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bootstrap class
|
||||
* instance for this database.
|
||||
*
|
||||
* @return the bootstrap class instance, null if none
|
||||
* @see HikariDataSource for bootstrap class
|
||||
*/
|
||||
@Override
|
||||
public HikariDataSource getBootstrap() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this stream and releases any system resources associated
|
||||
* with it. If the stream is already closed then invoking this
|
||||
* method has no effect.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (dataSource != null) {
|
||||
dataSource.close();
|
||||
}
|
||||
dataSource = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
|
||||
*
|
||||
* For inquiries, please contact braydonrainnny@gmail.com
|
||||
*/
|
||||
package me.braydon.feather.database.impl.mariadb;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* Authorization for a {@link MariaDB} database.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@RequiredArgsConstructor @AllArgsConstructor @Getter @ToString
|
||||
public final class MariaDBAuthorization {
|
||||
/**
|
||||
* The JDBC connection URL for the database.
|
||||
*/
|
||||
@NonNull private final String jdbcUrl;
|
||||
|
||||
/**
|
||||
* The username to use for authenticating with the database.
|
||||
*/
|
||||
@NonNull private final String username;
|
||||
|
||||
/**
|
||||
* The password to use for authenticating with the database.
|
||||
*/
|
||||
@NonNull private final String password;
|
||||
|
||||
/**
|
||||
* The optional {@link HikariConfig} to use.
|
||||
*/
|
||||
private HikariConfig hikariConfig;
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
|
||||
*
|
||||
* For inquiries, please contact braydonrainnny@gmail.com
|
||||
*/
|
||||
package me.braydon.feather.database.impl.mariadb;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.feather.database.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@link MariaDB} {@link Repository} implementation.
|
||||
*
|
||||
* @author Braydon
|
||||
* @param <ID> the identifier for type for entities
|
||||
* @param <E> the entity type this repository stores
|
||||
*/
|
||||
public class MariaDBRepository<ID, E> extends Repository<MariaDB, ID, E> {
|
||||
public MariaDBRepository(@NonNull MariaDB database, @NonNull Class<? extends E> entityClass) {
|
||||
super(database, entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity with the given id.
|
||||
*
|
||||
* @param id the entity id
|
||||
* @return the entity with the id, null if none
|
||||
* @see ID for id
|
||||
* @see E for entity
|
||||
*/
|
||||
@Override
|
||||
public E find(@NonNull ID id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all entities within this repository.
|
||||
*
|
||||
* @return the entities
|
||||
* @see E for entity
|
||||
*/
|
||||
@Override
|
||||
public List<E> findAll() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the given entities.
|
||||
*
|
||||
* @param entities the entities to save
|
||||
* @see E for entity
|
||||
*/
|
||||
@Override
|
||||
public void saveAll(@NonNull E... entities) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of stored entities.
|
||||
*
|
||||
* @return the amount of stored entities
|
||||
* @see E for entity
|
||||
*/
|
||||
@Override
|
||||
public long count() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the entity with the given id.
|
||||
*
|
||||
* @param id the entity id to drop
|
||||
* @see ID for id
|
||||
* @see E for entity
|
||||
*/
|
||||
@Override
|
||||
public void dropById(@NonNull ID id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the given entity.
|
||||
*
|
||||
* @param entity the entity to drop
|
||||
* @see E for entity
|
||||
*/
|
||||
@Override
|
||||
public void drop(@NonNull E entity) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
|
||||
*
|
||||
* For inquiries, please contact braydonrainnny@gmail.com
|
||||
*/
|
||||
package me.braydon.feather.database.impl.mariadb.queries;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Singular;
|
||||
import me.braydon.feather.database.impl.mariadb.MariaDB;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A builder for {@link MariaDB} update queries.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Builder @Getter
|
||||
public class UpdateQuery {
|
||||
/**
|
||||
* The table to execute the update in.
|
||||
*/
|
||||
@NonNull private String table;
|
||||
|
||||
/**
|
||||
* The mapped values (by column) for this query.
|
||||
*/
|
||||
@NonNull @Singular private Map<String, Object> values;
|
||||
|
||||
/**
|
||||
* The where clause for this query.
|
||||
*/
|
||||
@NonNull private WhereClause whereClause;
|
||||
|
||||
/**
|
||||
* Build this query.
|
||||
*
|
||||
* @return the built query
|
||||
*/
|
||||
@Override @NonNull
|
||||
public String toString() {
|
||||
// Build the values
|
||||
StringBuilder valuesBuilder = new StringBuilder();
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
valuesBuilder.append("`").append(entry.getKey()).append("`")
|
||||
.append("='").append(entry.getValue()).append("', ");
|
||||
}
|
||||
String values = valuesBuilder.toString(); // The built values string
|
||||
values = values.substring(0, values.length() - 2); // Remove the trailing comma
|
||||
|
||||
// Build the query
|
||||
return String.format("UPDATE `%s` SET %s WHERE %s;",
|
||||
table, values, whereClause
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The where clause for this query.
|
||||
*/
|
||||
@Builder @Getter
|
||||
public static class WhereClause {
|
||||
/**
|
||||
* The column to target.
|
||||
*/
|
||||
@NonNull private String column;
|
||||
|
||||
/**
|
||||
* The value of the column to target.
|
||||
*/
|
||||
@NonNull private Object value;
|
||||
|
||||
@Override @NonNull
|
||||
public String toString() {
|
||||
return column + "='" + value + "'";
|
||||
}
|
||||
}
|
||||
}
|
@ -49,6 +49,7 @@ public class MongoDB implements IDatabase<MongoClient, ConnectionString> {
|
||||
* @param credentials the optional credentials to use
|
||||
* @throws IllegalArgumentException if no credentials or database name is provided
|
||||
* @throws IllegalStateException if already connected
|
||||
* @see ConnectionString for credentials
|
||||
*/
|
||||
@Override
|
||||
public void connect(ConnectionString credentials) throws IllegalArgumentException, IllegalStateException {
|
||||
@ -91,8 +92,8 @@ public class MongoDB implements IDatabase<MongoClient, ConnectionString> {
|
||||
}
|
||||
// Return ping
|
||||
long before = System.currentTimeMillis();
|
||||
database.runCommand(new BasicDBObject("ping", "1"));
|
||||
return System.currentTimeMillis() - before;
|
||||
database.runCommand(new BasicDBObject("ping", "1")); // Send a ping command
|
||||
return System.currentTimeMillis() - before; // Return time difference
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,7 +124,7 @@ public class MongoDB implements IDatabase<MongoClient, ConnectionString> {
|
||||
if (!isConnected()) { // Not connected
|
||||
throw new IllegalStateException("Not connected");
|
||||
}
|
||||
return new MongoRepository<>(this, entityClass, database.getCollection(collectionName));
|
||||
return new MongoRepository<>(this, entityClass, collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,16 +12,16 @@ import com.mongodb.client.model.Indexes;
|
||||
import com.mongodb.client.model.UpdateOneModel;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.braydon.feather.common.Tuple;
|
||||
import me.braydon.feather.database.Repository;
|
||||
import me.braydon.feather.database.impl.mongodb.annotation.CustomDocument;
|
||||
import me.braydon.feather.database.impl.mongodb.annotation.Index;
|
||||
import org.bson.Document;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The {@link MongoDB} {@link Repository} implementation.
|
||||
@ -36,6 +36,10 @@ public class MongoRepository<ID, E> extends Repository<MongoDB, ID, E> {
|
||||
*/
|
||||
@NonNull private final MongoCollection<Document> collection;
|
||||
|
||||
public MongoRepository(@NonNull MongoDB database, @NonNull Class<? extends E> entityClass, @NonNull String collectionName) {
|
||||
this(database, entityClass, database.getDatabase().getCollection(collectionName));
|
||||
}
|
||||
|
||||
public MongoRepository(@NonNull MongoDB database, @NonNull Class<? extends E> entityClass, @NonNull MongoCollection<Document> collection) {
|
||||
super(database, entityClass);
|
||||
this.collection = collection;
|
||||
@ -90,17 +94,29 @@ public class MongoRepository<ID, E> extends Repository<MongoDB, ID, E> {
|
||||
* @param entities the entities to save
|
||||
* @see E for entity
|
||||
*/
|
||||
@Override
|
||||
@Override @SneakyThrows
|
||||
public void saveAll(@NonNull E... entities) {
|
||||
List<UpdateOneModel<Document>> updateModels = new ArrayList<>(); // The update models to bulk write
|
||||
|
||||
for (E entity : entities) {
|
||||
me.braydon.feather.data.Document<Object> document = new me.braydon.feather.data.Document<>(entity); // Create a document from the entity
|
||||
me.braydon.feather.data.Document document = new me.braydon.feather.data.Document(entity); // Create a document from the entity
|
||||
Document bsonDocument; // The Bson document to save
|
||||
|
||||
Method customDocumentMethod = Arrays.stream(entity.getClass().getDeclaredMethods())
|
||||
.filter(method -> method.isAnnotationPresent(CustomDocument.class))
|
||||
.findFirst().orElse(null); // Get the @CustomDocument method
|
||||
|
||||
// We have a custom document method
|
||||
if (customDocumentMethod != null && (customDocumentMethod.getReturnType() == Document.class)) {
|
||||
bsonDocument = (Document) customDocumentMethod.invoke(entity); // Get our custom document
|
||||
} else { // Otherwise, use our mapped data
|
||||
bsonDocument = new Document(document.toMappedData());
|
||||
}
|
||||
|
||||
// Add our update model to the list
|
||||
updateModels.add(new UpdateOneModel<>(
|
||||
Filters.eq(document.getIdKey(), document.getKey()),
|
||||
new Document("$set", new Document(document.toMappedData())),
|
||||
new Document("$set", bsonDocument),
|
||||
new UpdateOptions().upsert(true)
|
||||
));
|
||||
|
||||
@ -162,7 +178,7 @@ public class MongoRepository<ID, E> extends Repository<MongoDB, ID, E> {
|
||||
*/
|
||||
@Override
|
||||
public void drop(@NonNull E entity) {
|
||||
me.braydon.feather.data.Document<Object> document = new me.braydon.feather.data.Document<>(entity); // Create a document from the entity
|
||||
me.braydon.feather.data.Document document = new me.braydon.feather.data.Document(entity); // Create a document from the entity
|
||||
collection.deleteOne(new Document(document.getIdKey(), document.getKey())); // Delete the entity
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
|
||||
*
|
||||
* For inquiries, please contact braydonrainnny@gmail.com
|
||||
*/
|
||||
package me.braydon.feather.database.impl.mongodb.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Methods tagged with this annotation will be invoked
|
||||
* when a document is being saved to the database, this
|
||||
* allows is to save a custom document, rather than
|
||||
* parsing the entity and creating one that way.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented @Inherited
|
||||
public @interface CustomDocument { }
|
@ -19,7 +19,7 @@ import me.braydon.feather.database.IDatabase;
|
||||
* @see RedisURI for the credentials class
|
||||
* @see <a href="https://redis.io">Redis Official Site</a>
|
||||
*/
|
||||
public class Redis implements IDatabase<StatefulRedisConnection<String, String>, RedisURI> {
|
||||
public class Redis implements IDatabase<StatefulRedisConnection<String, String>, String> {
|
||||
/**
|
||||
* The current {@link RedisClient} instance.
|
||||
*/
|
||||
@ -43,25 +43,25 @@ public class Redis implements IDatabase<StatefulRedisConnection<String, String>,
|
||||
/**
|
||||
* Initialize a connection to this database.
|
||||
*
|
||||
* @param credentials the optional credentials to use
|
||||
* @param uri the optional credentials to use
|
||||
* @throws IllegalArgumentException if no credentials are provided
|
||||
* @throws IllegalStateException if already connected
|
||||
*/
|
||||
@Override
|
||||
public void connect(RedisURI credentials) throws IllegalArgumentException, IllegalStateException {
|
||||
if (credentials == null) { // We need valid credentials
|
||||
public void connect(String uri) throws IllegalArgumentException, IllegalStateException {
|
||||
if (uri == null) { // We need valid credentials
|
||||
throw new IllegalArgumentException("No credentials defined");
|
||||
}
|
||||
if (isConnected()) { // Already connected
|
||||
throw new IllegalStateException("Already connected");
|
||||
}
|
||||
if (client != null) { // We have a client, close it first
|
||||
client.close();
|
||||
client.shutdown();
|
||||
}
|
||||
if (connection != null) { // We have a connection, close it first
|
||||
connection.close();
|
||||
}
|
||||
client = RedisClient.create(credentials); // Create a new client
|
||||
client = RedisClient.create(uri); // Create a new client
|
||||
connection = client.connect(); // Connect to the Redis server
|
||||
}
|
||||
|
||||
@ -82,7 +82,13 @@ public class Redis implements IDatabase<StatefulRedisConnection<String, String>,
|
||||
*/
|
||||
@Override
|
||||
public long getLatency() {
|
||||
return 0;
|
||||
if (!isConnected()) { // Not connected
|
||||
return -1L;
|
||||
}
|
||||
// Return ping
|
||||
long before = System.currentTimeMillis();
|
||||
connection.sync().ping(); // Send a ping command
|
||||
return System.currentTimeMillis() - before; // Return time difference
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,7 +145,7 @@ public class Redis implements IDatabase<StatefulRedisConnection<String, String>,
|
||||
@Override
|
||||
public void close() {
|
||||
if (client != null) {
|
||||
client.close();
|
||||
client.shutdown();
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
|
@ -7,12 +7,13 @@ package me.braydon.feather.database.impl.redis;
|
||||
|
||||
import io.lettuce.core.api.sync.RedisCommands;
|
||||
import lombok.NonNull;
|
||||
import me.braydon.feather.common.Tuple;
|
||||
import me.braydon.feather.data.Document;
|
||||
import me.braydon.feather.database.Repository;
|
||||
import me.braydon.feather.database.impl.redis.annotation.TTL;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The {@link Redis} {@link Repository} implementation.
|
||||
@ -78,8 +79,24 @@ public class RedisRepository<ID, E> extends Repository<Redis, ID, E> {
|
||||
commands.multi();
|
||||
}
|
||||
for (E entity : entities) { // Set our entities
|
||||
Document<String> document = new Document<>(entity); // Create a document from the entity
|
||||
commands.hmset(keyPrefix + ":" + document.getKey(), document.toMappedData());
|
||||
Document document = new Document(entity); // Create a document from the entity
|
||||
String key = keyPrefix + ":" + document.getKey(); // The key of this entity
|
||||
|
||||
Map<String, String> mappedData = new HashMap<>();
|
||||
for (Map.Entry<String, Tuple<Field, Object>> entry : document.getMappedData().entrySet()) {
|
||||
mappedData.put(entry.getKey(), String.valueOf(entry.getValue().getRight()));
|
||||
}
|
||||
commands.hmset(key, mappedData); // Set the mapped document in the database
|
||||
|
||||
// Handling @TTL annotations
|
||||
Class<?> clazz = entity.getClass(); // The entity class
|
||||
if (!clazz.isAnnotationPresent(TTL.class)) { // Missing @TTL
|
||||
continue;
|
||||
}
|
||||
long ttl = clazz.getAnnotation(TTL.class).value(); // Get the ttl value
|
||||
if (ttl > 0L) { // Value is above zero, set it
|
||||
commands.expire(key, ttl);
|
||||
}
|
||||
}
|
||||
if (multi) { // Execute the commands in bulk
|
||||
commands.exec();
|
||||
@ -117,7 +134,7 @@ public class RedisRepository<ID, E> extends Repository<Redis, ID, E> {
|
||||
*/
|
||||
@Override
|
||||
public void drop(@NonNull E entity) {
|
||||
me.braydon.feather.data.Document<Object> document = new me.braydon.feather.data.Document<>(entity); // Create a document from the entity
|
||||
me.braydon.feather.data.Document document = new me.braydon.feather.data.Document(entity); // Create a document from the entity
|
||||
getDatabase().getBootstrap().sync().del(keyPrefix + ":" + document.getKey());
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Braydon (Rainnny). All rights reserved.
|
||||
*
|
||||
* For inquiries, please contact braydonrainnny@gmail.com
|
||||
*/
|
||||
package me.braydon.feather.database.impl.redis.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Entities tagged with this annotation will
|
||||
* expire after the amount of defined seconds.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented @Inherited
|
||||
public @interface TTL {
|
||||
/**
|
||||
* The time-to-live value.
|
||||
* <p>
|
||||
* If the value is zero or
|
||||
* below, then there will
|
||||
* be no expiration.
|
||||
* <p>
|
||||
* The key this TTL rule is assigned
|
||||
* to will expire in the defined amount
|
||||
* of seconds from when the key was updated.
|
||||
* </p>
|
||||
*
|
||||
* @return the value in seconds
|
||||
*/
|
||||
long value() default -1L;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user