Skip to content

Commit

Permalink
Added mysql adapter for sqlite and legacy/modern mysql and container …
Browse files Browse the repository at this point in the history
…test
  • Loading branch information
Fabian Phan committed Apr 23, 2024
1 parent 7c00b72 commit 230ffaf
Show file tree
Hide file tree
Showing 25 changed files with 475 additions and 121 deletions.
2 changes: 1 addition & 1 deletion ndatabase-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>ndatabase</artifactId>
<groupId>com.nivixx</groupId>
<version>1.2.1-SNAPSHOT</version>
<version>1.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
6 changes: 3 additions & 3 deletions ndatabase-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>ndatabase</artifactId>
<groupId>com.nivixx</groupId>
<version>1.2.1-SNAPSHOT</version>
<version>1.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand All @@ -20,12 +20,12 @@
<dependency>
<groupId>com.nivixx</groupId>
<artifactId>ndatabase-api</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.nivixx</groupId>
<artifactId>core-platform</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public Repository<K,V> getOrCreateRepository(Class<V> entityType) throws NDataba
// Find configured database type (MYSQL, MongoDB, ...)
Dao<K,V> dao = databaseTypeResolver.getDaoForConfiguredDatabase(nEntity, keyType, entityType);

// Init stuff for target DB if needed
dao.init();

// Create the database/schema structure if doesn't exist
dao.createDatabaseIfNotExist(keyType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ protected Dao(String collectionName,
this.schema = schema;
}

public void init() throws DatabaseCreationException {
// Do nothing by default
}


public abstract void insert(V value) throws NDatabaseException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import com.nivixx.ndatabase.api.exception.*;
import com.nivixx.ndatabase.api.model.NEntity;
import com.nivixx.ndatabase.api.query.NQuery;
import com.nivixx.ndatabase.core.dao.mysql.adapter.MySQLAdapter;
import com.nivixx.ndatabase.core.dao.mysql.adapter.MySQLAdapterResolver;
import com.nivixx.ndatabase.core.expressiontree.SingleNodePath;
import com.nivixx.ndatabase.core.dao.Dao;
import com.nivixx.ndatabase.core.expressiontree.ExpressionTree;
import com.nivixx.ndatabase.core.expressiontree.visitor.SqlExpressionTreeVisitor;
import com.nivixx.ndatabase.core.serialization.encoder.BytesNEntityEncoder;
import com.nivixx.ndatabase.core.serialization.encoder.JsonStringNEntityEncoder;
import com.nivixx.ndatabase.core.serialization.encoder.NEntityEncoder;
import com.nivixx.ndatabase.platforms.coreplatform.logging.DBLogger;

Expand All @@ -26,6 +29,9 @@ public abstract class JdbcDao<K, V extends NEntity<K>> extends Dao<K, V> {
protected final String DATA_IDENTIFIER = "data";
protected final String DATA_KEY_IDENTIFIER = "data_key";
protected NEntityEncoder<V, byte[]> byteObjectSerializer;
protected NEntityEncoder<V, String> jsonStringObjectSerializer;

protected MySQLAdapter mySQLAdapter;


public JdbcDao(String collectionName,
Expand All @@ -38,6 +44,16 @@ public JdbcDao(String collectionName,
super(collectionName, schema, keyType, nEntityType, instantiatedNEntity, dbLogger);
this.pool = connectionPoolManager;
this.byteObjectSerializer = new BytesNEntityEncoder<>();
this.jsonStringObjectSerializer = new JsonStringNEntityEncoder<>();
}

@Override
public void init() throws DatabaseCreationException {
try (Connection connection = pool.getConnection()) {
mySQLAdapter = MySQLAdapterResolver.resolveMySQLAdapter(connection);
} catch (Exception e) {
throw new DatabaseCreationException("Failed to resolve adapter for MySQL", e);
}
}

@Override
Expand All @@ -51,7 +67,7 @@ public void insert(V value) throws NDatabaseException {
"INSERT INTO " + collectionName + "(" + DATA_KEY_IDENTIFIER + "," + DATA_IDENTIFIER + ") VALUES(?,?)"
);
bindKeyToStatement(ps,1, key);
ps.setObject(2, byteObjectSerializer.encode(value));
ps.setString(2, jsonStringObjectSerializer.encode(value));
ps.executeUpdate();
} catch (SQLException e) {
throw new DatabaseException(e);
Expand All @@ -72,10 +88,10 @@ public void upsert(V value) throws NDatabaseException {
ps = connection.prepareStatement(
"INSERT INTO " + collectionName + "(" + DATA_KEY_IDENTIFIER + "," + DATA_IDENTIFIER + ") VALUES(?,?) ON DUPLICATE KEY UPDATE " + DATA_IDENTIFIER + " = ?"
);
byte[] valueBytes = byteObjectSerializer.encode(value);
String valueJson = jsonStringObjectSerializer.encode(value);
bindKeyToStatement(ps,1, key);
ps.setObject(2, valueBytes);
ps.setObject(3, valueBytes);
ps.setObject(2, valueJson);
ps.setObject(3, valueJson);
ps.executeUpdate();
} catch (Exception e) {
throw new DatabaseException(e);
Expand Down Expand Up @@ -133,7 +149,7 @@ public void update(V value) throws NDatabaseException {
collectionName, DATA_IDENTIFIER, DATA_KEY_IDENTIFIER);
ps = connection.prepareStatement(updateQuery);

ps.setObject(1, byteObjectSerializer.encode(value));
ps.setString(1, jsonStringObjectSerializer.encode(value));
bindKeyToStatement(ps,2, key);
if(ps.executeUpdate() <= 0) {
throw new NEntityNotFoundException("There is no value with the key " + key + " in the database for collection " + collectionName);
Expand Down Expand Up @@ -275,13 +291,7 @@ collectionName, columnName, getColumnType(false, fieldType),
}
}

// Index this column if not exist
String createIndexQuery = MessageFormat.format(
"CREATE INDEX IF NOT EXISTS {0}_index ON {1}({2})",
columnName, collectionName, columnName);
try (PreparedStatement ps = connection.prepareStatement(createIndexQuery)) {
ps.execute();
}
mySQLAdapter.createIndexIfNotExist(collectionName, columnName, fieldType, connection);
}

@Override
Expand Down Expand Up @@ -382,14 +392,14 @@ protected String getColumnType(boolean isForKey, Class<?> type) {
}
if(type.isAssignableFrom(String.class)) {
if(isForKey) {
return "VARCHAR(1200)"; // TODO manage max row size
return "VARCHAR(255)"; // max value in mysql for key
}
else {
return "TEXT";
}
}
if(type.isAssignableFrom(Long.class) || type.getName().equals("long")) {
return "LONG";
return "BIGINT";
}
if(type.isAssignableFrom(Integer.class) || type.getName().equals("int")) {
return "INTEGER";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.nivixx.ndatabase.core.dao.mysql;

import com.nivixx.ndatabase.api.exception.DatabaseCreationException;
import com.nivixx.ndatabase.api.model.NEntity;
import com.nivixx.ndatabase.core.dao.jdbc.JdbcDao;
import com.nivixx.ndatabase.core.expressiontree.SingleNodePath;
import com.nivixx.ndatabase.platforms.coreplatform.logging.DBLogger;

import java.util.List;

public class MysqlDao<K, V extends NEntity<K>> extends JdbcDao<K,V> {

public MysqlDao(String collectionName,
Expand All @@ -13,7 +17,7 @@ public MysqlDao(String collectionName,
V instantiatedNEntity,
HikariConnectionPool hikariConnectionPool,
DBLogger dbLogger) {
super(collectionName, schema, keyType, nEntityType, instantiatedNEntity, hikariConnectionPool, dbLogger);
super(collectionName, schema, keyType, nEntityType, instantiatedNEntity, hikariConnectionPool, dbLogger);
}

// Override here if mysql implementation need more specifications
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.nivixx.ndatabase.core.dao.mysql.adapter;

import java.sql.Connection;
import java.sql.SQLException;

public abstract class MySQLAdapter {

public abstract void createIndexIfNotExist(
String tableName, String columnName, Class<?> fieldType, Connection connection) throws SQLException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.nivixx.ndatabase.core.dao.mysql.adapter;

import java.sql.*;

public class MySQLAdapterResolver {

public static MySQLAdapter resolveMySQLAdapter(Connection connection) throws SQLException {

DatabaseMetaData metaData = connection.getMetaData();
String dbName = metaData.getDatabaseProductName();

if (dbName.equalsIgnoreCase("SQLite")) {
return new MySQLAdapter_SQLITE();
}

String version = getMySQLVersion(connection);
boolean isMySQL57OrBelow = isMySQL57OrBelow(version);

if (isMySQL57OrBelow) {
return new MySQLAdapter_MYSQL_5_7();
}

// Add more if needed

return new MySQLAdapter_MYSQL_MODERN();

}


private static String getMySQLVersion(Connection conn) throws SQLException {
String version = null;
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery("SELECT VERSION()");
if (rs.next()) {
version = rs.getString(1);
}
}
return version;
}

private static boolean isMySQL57OrBelow(String version) {
if (version != null && version.matches("^\\d+\\.\\d+\\.\\d+")) {
String[] parts = version.split("\\.");
int major = Integer.parseInt(parts[0]);
int minor = Integer.parseInt(parts[1]);
return major < 6 || (major == 5 && minor <= 7);
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.nivixx.ndatabase.core.dao.mysql.adapter;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.MessageFormat;

public class MySQLAdapter_MARIADB extends MySQLAdapter {

@Override
public void createIndexIfNotExist(String tableName, String columnName, Class<?> fieldType, Connection connection) throws SQLException {

String createIndexQuery = MessageFormat.format(
"CREATE INDEX IF NOT EXISTS {0}_index ON {1}({2})",
columnName, tableName, columnName);
try (PreparedStatement ps = connection.prepareStatement(createIndexQuery)) {
ps.execute();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.nivixx.ndatabase.core.dao.mysql.adapter;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.MessageFormat;

public class MySQLAdapter_MYSQL_5_7 extends MySQLAdapter {

@Override
public void createIndexIfNotExist(String tableName, String columnName, Class<?> fieldType, Connection connection) throws SQLException {
// Check if the index exists
boolean indexExists = indexExists(tableName, columnName, connection);

// If the index doesn't exist, create it
if (!indexExists) {
// In Legacy sql version, we need to specify index length for TEXT
String createIndexQuery =
fieldType.isAssignableFrom(String.class) ?
"CREATE INDEX " + columnName + "_index ON " + tableName + "(" + columnName + "(255))" :
"CREATE INDEX " + columnName + "_index ON " + tableName + "(" + columnName + ")";
try (PreparedStatement ps = connection.prepareStatement(createIndexQuery)) {
ps.executeUpdate();
}
}
}


private boolean indexExists(String tableName, String columnName, Connection connection) throws SQLException {
String indexExistsQuery = "SHOW INDEX FROM " + tableName + " WHERE Key_name = ?";
try (PreparedStatement ps = connection.prepareStatement(indexExistsQuery)) {
ps.setString(1, columnName + "_index");
try (ResultSet rs = ps.executeQuery()) {
return rs.next();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.nivixx.ndatabase.core.dao.mysql.adapter;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.MessageFormat;

public class MySQLAdapter_MYSQL_MODERN extends MySQLAdapter {

@Override
public void createIndexIfNotExist(String tableName, String columnName, Class<?> fieldType, Connection connection) throws SQLException {
// Check if the index exists
boolean indexExists = indexExists(tableName, columnName, connection);

// If the index doesn't exist, create it
if (!indexExists) {
// In Legacy sql version, we need to specify index length for TEXT
String createIndexQuery =
fieldType.isAssignableFrom(String.class) ?
"CREATE INDEX " + columnName + "_index ON " + tableName + "(" + columnName + "(255))" :
"CREATE INDEX " + columnName + "_index ON " + tableName + "(" + columnName + ")";
try (PreparedStatement ps = connection.prepareStatement(createIndexQuery)) {
ps.executeUpdate();
}
}
}


private boolean indexExists(String tableName, String columnName, Connection connection) throws SQLException {
String indexExistsQuery = "SHOW INDEX FROM " + tableName + " WHERE Key_name = ?";
try (PreparedStatement ps = connection.prepareStatement(indexExistsQuery)) {
ps.setString(1, columnName + "_index");
try (ResultSet rs = ps.executeQuery()) {
return rs.next();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.nivixx.ndatabase.core.dao.mysql.adapter;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.MessageFormat;

public class MySQLAdapter_SQLITE extends MySQLAdapter {

@Override
public void createIndexIfNotExist(String tableName, String columnName, Class<?> fieldType, Connection connection) throws SQLException {

String createIndexQuery = MessageFormat.format(
"CREATE INDEX IF NOT EXISTS {0}_index ON {1}({2})",
columnName, tableName, columnName);
try (PreparedStatement ps = connection.prepareStatement(createIndexQuery)) {
ps.execute();
}
}
}
Loading

0 comments on commit 230ffaf

Please sign in to comment.