diff --git a/pom.xml b/pom.xml index 3b923a2f36..3f2edffd3e 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ io.vertigo vertigo-parent - 4.3.3-SNAPSHOT + 4.3.2 vertigo-libs diff --git a/vertigo-database/pom.xml b/vertigo-database/pom.xml index 844a93fe7e..793b5e173a 100644 --- a/vertigo-database/pom.xml +++ b/vertigo-database/pom.xml @@ -23,11 +23,16 @@ ojdbc11 23.8.0.25.04 - - org.postgresql - postgresql - 42.7.7 - + + org.postgresql + postgresql + 42.7.7 + + + org.mariadb.jdbc + mariadb-java-client + 3.3.3 + org.liquibase liquibase-core @@ -36,7 +41,7 @@ io.vertigo vertigo-commons - ${project.version} + ${vertigo.version} pom import @@ -47,10 +52,10 @@ io.vertigo vertigo-commons - ${project.version} + ${vertigo.version} - io.vertigo + io.vertigo vertigo-influxdb-connector ${vertigo.version} true @@ -81,6 +86,12 @@ test + + org.mariadb.jdbc + mariadb-java-client + test + + com.mchange diff --git a/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbDataBase.java b/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbDataBase.java new file mode 100644 index 0000000000..7aeceb4d96 --- /dev/null +++ b/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbDataBase.java @@ -0,0 +1,53 @@ +/* + * vertigo - application development platform + * + * Copyright (C) 2013-2025, Vertigo.io, team@vertigo.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.vertigo.database.impl.sql.vendor.mariadb; + +import io.vertigo.database.impl.sql.vendor.core.SqlVendorMapping; +import io.vertigo.database.sql.vendor.SqlDataBase; +import io.vertigo.database.sql.vendor.SqlDialect; +import io.vertigo.database.sql.vendor.SqlExceptionHandler; +import io.vertigo.database.sql.vendor.SqlMapping; + +/** + * Gestion de la base de données MariaDB. + * Utilise InnoDB comme moteur de stockage pour garantir les transactions ACID. + * + * @author ppinette + */ +public final class MariaDbDataBase implements SqlDataBase { + private final SqlExceptionHandler sqlExceptionHandler = new MariaDbExceptionHandler(); + private final SqlMapping sqlVendorMapping = SqlVendorMapping.createWithBooleanAsBit(); + private final SqlDialect sqlDialect = new MariaDbDialect(); + + /** {@inheritDoc} */ + @Override + public SqlExceptionHandler getSqlExceptionHandler() { + return sqlExceptionHandler; + } + + /** {@inheritDoc} */ + @Override + public SqlMapping getSqlMapping() { + return sqlVendorMapping; + } + + @Override + public SqlDialect getSqlDialect() { + return sqlDialect; + } +} diff --git a/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbDialect.java b/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbDialect.java new file mode 100644 index 0000000000..464ace5a9b --- /dev/null +++ b/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbDialect.java @@ -0,0 +1,86 @@ +/* + * vertigo - application development platform + * + * Copyright (C) 2013-2025, Vertigo.io, team@vertigo.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.vertigo.database.impl.sql.vendor.mariadb; + +import io.vertigo.core.lang.Assertion; +import io.vertigo.core.util.StringUtil; +import io.vertigo.database.sql.vendor.SqlDialect; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Dialecte SQL spécifique à MariaDB. + * Utilise InnoDB comme moteur de stockage et AUTO_INCREMENT pour les clés primaires. + * + * @author ppinette + */ +final class MariaDbDialect implements SqlDialect { + + /** {@inheritDoc} */ + @Override + public String createInsertQuery( + final String idFieldName, + final List dataFieldsName, + final String sequencePrefix, + final String tableName, + final String parameterName) { + Assertion.check() + .isNotBlank(idFieldName) + .isNotNull(dataFieldsName) + .isNotBlank(tableName); + + return new StringBuilder() + .append("insert into ").append(tableName).append(" (") + .append(dataFieldsName + .stream() + .map(StringUtil::camelToConstCase) + .collect(Collectors.joining(", "))) + .append(") values (") + .append(dataFieldsName + .stream() + .map(fieldName -> " #" + parameterName + '.' + fieldName + '#') + .collect(Collectors.joining(", "))) + .append(") ") + .toString(); + } + + /** {@inheritDoc} */ + @Override + public void appendListState(final StringBuilder query, final Integer maxRows, final int skipRows, final String sortFieldName, final boolean sortDesc) { + if (sortFieldName != null) { + query.append(" order by ").append(StringUtil.camelToConstCase(sortFieldName)); + query.append(sortDesc ? " desc" : ""); + } + + if (maxRows != null) { + query.append(" limit ").append(maxRows); + if (skipRows > 0) { + query.append(" offset ").append(skipRows).append(" rows"); + } + } else if (skipRows > 0) { + query.append(" offset ").append(skipRows).append(" rows"); + } + } + + /** {@inheritDoc} */ + @Override + public GenerationMode getGenerationMode() { + return GenerationMode.GENERATED_KEYS; + } +} diff --git a/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbExceptionHandler.java b/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbExceptionHandler.java new file mode 100644 index 0000000000..6d691f237b --- /dev/null +++ b/vertigo-database/src/main/java/io/vertigo/database/impl/sql/vendor/mariadb/MariaDbExceptionHandler.java @@ -0,0 +1,89 @@ +/* + * vertigo - application development platform + * + * Copyright (C) 2013-2025, Vertigo.io, team@vertigo.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.vertigo.database.impl.sql.vendor.mariadb; + +import java.sql.SQLException; + +import io.vertigo.database.impl.sql.vendor.core.AbstractSqlExceptionHandler; + +/** + * Handler des exceptions SQL spécifique à MariaDB. + * Gère les codes d'erreur MySQL/MariaDB et les deadlocks InnoDB. + * + * @author ppinette + */ +final class MariaDbExceptionHandler extends AbstractSqlExceptionHandler { + + /** + * Constructor. + */ + MariaDbExceptionHandler() { + super(); + } + + @Override + public RuntimeException handleSQLException(final SQLException sqle, final String statementInfos) { + final int errCode = sqle.getErrorCode(); + switch (errCode) { + case 1062: + // Violation de contrainte d'unicité (Duplicate entry) + return handleUniqueConstraintSQLException(sqle); + case 1452: + // Violation de contrainte d'intégrité référentielle (Cannot add or update a child row) + return handleForeignConstraintSQLException(sqle); + case 1264: + case 1265: + // Valeur trop grande pour la colonne (Out of range value) + return handleTooLargeValueSqlException(sqle); + default: + if (isUserSQLException(errCode)) { + return handleUserSQLException(sqle); + } + } + return handleOtherSQLException(sqle, statementInfos); + } + + private static boolean isUserSQLException(final int errCode) { + // Codes d'erreur utilisateur dans MySQL/MariaDB + return errCode >= 1000 && errCode < 2000; + } + + /** {@inheritDoc} */ + @Override + protected String extractConstraintName(final String msg) { + // Extraction du nom de contrainte depuis le message d'erreur MariaDB + String constraintName = extractConstraintName(msg, "for key", '\'', '\''); + if (constraintName == null) { + constraintName = extractConstraintName(msg, "constraint", '\'', '\''); + } + return constraintName; + } + + private static String extractConstraintName( + final String msg, + final String constraintName, + final char constraintNameStart, + final char constraintNameEnd) { + final int i1 = msg.indexOf(constraintNameStart, msg.indexOf(constraintName)); + final int i2 = msg.indexOf(constraintNameEnd, i1 + 1); + if (i1 > -1 && i2 > -1 && i2 > i1) { + return msg.substring(i1 + 1, i2).toUpperCase().trim(); + } + return null; + } +} diff --git a/vertigo-database/src/test/java/io/vertigo/database/sql/vendor/mariadb/MariaDbDataBaseManagerTest.java b/vertigo-database/src/test/java/io/vertigo/database/sql/vendor/mariadb/MariaDbDataBaseManagerTest.java new file mode 100644 index 0000000000..d9bb152fc6 --- /dev/null +++ b/vertigo-database/src/test/java/io/vertigo/database/sql/vendor/mariadb/MariaDbDataBaseManagerTest.java @@ -0,0 +1,106 @@ +/* + * vertigo - application development platform + * + * Copyright (C) 2013-2025, Vertigo.io, team@vertigo.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.vertigo.database.sql.vendor.mariadb; + +import io.vertigo.commons.CommonsFeatures; +import io.vertigo.core.node.AutoCloseableNode; +import io.vertigo.core.node.config.BootConfig; +import io.vertigo.core.node.config.NodeConfig; +import io.vertigo.core.param.Param; +import io.vertigo.database.DatabaseFeatures; +import io.vertigo.database.impl.sql.vendor.h2.H2DataBase; +import io.vertigo.database.impl.sql.vendor.mariadb.MariaDbDataBase; +import io.vertigo.database.sql.AbstractSqlManagerTest; +import io.vertigo.database.sql.vendor.SqlDialect; +import io.vertigo.database.sql.vendor.SqlDialect.GenerationMode; + +/** + * Tests d'intégration pour MariaDB avec InnoDB. + * Ces tests peuvent être lents car ils nécessitent une instance MariaDB. + * Utilisez @Disabled pour les désactiver en développement. + * + * @author ppinette + */ +public final class MariaDbDataBaseManagerTest extends AbstractSqlManagerTest { + + private AutoCloseableNode node; + + @Override + public SqlDialect getDialect() { + return new MariaDbDataBase().getSqlDialect(); + } + + @Override + protected NodeConfig buildNodeConfig() { + return NodeConfig.builder() + .withBoot(BootConfig.builder() + .withLocales("fr_FR") + .build()) + .addModule(new CommonsFeatures() + .build()) + .addModule(new DatabaseFeatures() + .withSqlDataBase() + .withC3p0( + Param.of("dataBaseClass", MariaDbDataBase.class.getName()), + Param.of("jdbcDriver", "org.mariadb.jdbc.Driver"), + Param.of("jdbcUrl", "jdbc:mariadb://localhost:3306/test?user=root&password=root"), + Param.of("minPoolSize", "1"), + Param.of("maxPoolSize", "5"), + Param.of("acquireIncrement", "1")) + .withC3p0( + Param.of("name", "secondary"), + Param.of("dataBaseClass", H2DataBase.class.getName()), + Param.of("jdbcDriver", "org.h2.Driver"), + Param.of("jdbcUrl", "jdbc:h2:mem:secondaryDatabase")) + .build()) + .build(); + } + + @Override + protected String createTableMovie() { + return "CREATE TABLE IF NOT EXISTS movie ( " + + "mov_id INT AUTO_INCREMENT PRIMARY KEY, " + + "title VARCHAR(255), " + + "mail VARCHAR(255), " + + "fps DECIMAL(6,3), " + + "income DECIMAL(6,3), " + + "color TINYINT(1), " + + "release_date DATETIME NULL, " + + "release_local_date DATE NULL, " + + "release_instant DATETIME NULL, " + + "icon LONGBLOB" + + ") ENGINE=InnoDB"; + } + + @Override + protected String createSequenceMovie() { + return "CREATE SEQUENCE IF NOT EXISTS seq_movie START WITH 1 INCREMENT BY 20"; + } + + @Override + protected GenerationMode getExpectedGenerationMode() { + return GenerationMode.GENERATED_KEYS; + } + + @Override + protected boolean commitRequiredOnSchemaModification() { + return true; // InnoDB nécessite un commit après DDL + } + + +} diff --git a/vertigo-database/src/test/java/io/vertigo/database/sql/vendor/mariadb/MariaDbDialectTest.java b/vertigo-database/src/test/java/io/vertigo/database/sql/vendor/mariadb/MariaDbDialectTest.java new file mode 100644 index 0000000000..038af6d70b --- /dev/null +++ b/vertigo-database/src/test/java/io/vertigo/database/sql/vendor/mariadb/MariaDbDialectTest.java @@ -0,0 +1,78 @@ +/* + * vertigo - application development platform + * + * Copyright (C) 2013-2025, Vertigo.io, team@vertigo.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.vertigo.database.sql.vendor.mariadb; + +import java.util.Optional; + +import io.vertigo.database.impl.sql.vendor.mariadb.MariaDbDataBase; +import io.vertigo.database.sql.AbstractSqlDialectTest; +import io.vertigo.database.sql.vendor.SqlDialect; + +/** + * Tests unitaires pour le dialecte MariaDB. + * Ces tests sont rapides car ils testent uniquement la logique du dialecte. + * + * @author ppinette + */ +public final class MariaDbDialectTest extends AbstractSqlDialectTest { + + @Override + public SqlDialect getDialect() { + return new MariaDbDataBase().getSqlDialect(); + } + + @Override + public String getExpectedInsertQuery() { + return "insert into MOVIE (TITLE) values ( #dto.title#) "; + } + + @Override + public String getExpectedSelectForUpdateWildCardQuery() { + return " select * from MOVIE where ID = #id# for update "; + } + + @Override + public String getExpectedSelectForUpdateFieldsQuery() { + return " select ID, TITLE from MOVIE where ID = #id# for update "; + } + + @Override + public Optional getExpectedCreatePrimaryKeyQuery() { + return Optional.empty(); + } + + @Override + public String getExpectedAppendMaxRowsQuery() { + return "select * from MOVIE limit 100"; + } + + @Override + public String getExpectedAppendSkipRowsQuery() { + return "select * from MOVIE offset 10 rows"; + } + + @Override + public String getExpectedAppendSortQuery() { + return "select * from MOVIE order by TITLE"; + } + + @Override + public String getExpectedAppendSortDescQuery() { + return "select * from MOVIE order by TITLE desc"; + } +}