This is an automated email from the ASF dual-hosted git repository.

aadamchik pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new a3cf68b16 CAY-2701 DST-related LocalDateTime issues
a3cf68b16 is described below

commit a3cf68b163d67352ae6661563d15a536cf38e553
Author: Andrus Adamchik <and...@objectstyle.com>
AuthorDate: Thu Jan 30 17:35:37 2025 -0500

    CAY-2701 DST-related LocalDateTime issues
---
 RELEASE-NOTES.txt                                  |   1 +
 .../org/apache/cayenne/dba/mysql/MySQLAdapter.java |  24 +-
 .../cayenne/dba/mysql/MySQLLocalDateTimeType.java  |  53 ++++
 .../cayenne/access/types/DateTimeTypesIT.java      | 330 ++++++++++++---------
 .../testcontainers/MysqlContainerProvider.java     |   4 +-
 pom.xml                                            |   9 +-
 6 files changed, 271 insertions(+), 150 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 21757a41c..ddeee5276 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -18,6 +18,7 @@ CAY-2873 Change Orderings.orderedList() to accept a 
Collection rather than a Lis
 
 Bug Fixes:
 
+CAY-2701 MySQL DST-related LocalDateTime issues
 CAY-2871 QualifierTranslator breaks on a relationship with a compound FK
 CAY-2872 CayenneModeler "Documentation" link is broken
 
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java 
b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
index 4301ee40a..3aec4a1bd 100644
--- a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
+++ b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
@@ -19,16 +19,6 @@
 
 package org.apache.cayenne.dba.mysql;
 
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Types;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor;
 import org.apache.cayenne.access.translator.ParameterBinding;
@@ -49,7 +39,6 @@ import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.DefaultQuotingStrategy;
 import org.apache.cayenne.dba.JdbcAdapter;
-import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.dba.QuotingStrategy;
 import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.di.Inject;
@@ -59,6 +48,16 @@ import org.apache.cayenne.query.Query;
 import org.apache.cayenne.query.SQLAction;
 import org.apache.cayenne.resource.ResourceLocator;
 
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
 /**
  * DbAdapter implementation for <a href="http://www.mysql.com";>MySQL RDBMS</a>.
  * <h3>
@@ -149,6 +148,9 @@ public class MySQLAdapter extends JdbcAdapter {
        protected void configureExtendedTypes(ExtendedTypeMap map) {
                super.configureExtendedTypes(map);
 
+               // TODO: this may need to be made universal across adapters
+               map.registerType(new MySQLLocalDateTimeType());
+
                // must handle CLOBs as strings, otherwise there
                // are problems with NULL clobs that are treated
                // as empty strings... somehow this doesn't happen
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLLocalDateTimeType.java
 
b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLLocalDateTimeType.java
new file mode 100644
index 000000000..a85b3db45
--- /dev/null
+++ 
b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLLocalDateTimeType.java
@@ -0,0 +1,53 @@
+package org.apache.cayenne.dba.mysql;
+
+import org.apache.cayenne.access.types.ExtendedType;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.time.LocalDateTime;
+
+/**
+ * An extended type for DST-safe LocalDateTime handling that bypasses timezone 
resolution by reading and writing
+ * timestamps directly as LocalDateTime.
+ *
+ * @since 5.0
+ */
+// TODO: only used by MySQL now. Test compatibility on other engines, and make 
it a universal approach
+class MySQLLocalDateTimeType implements ExtendedType<LocalDateTime> {
+
+    @Override
+    public String getClassName() {
+        return LocalDateTime.class.getName();
+    }
+
+    @Override
+    public LocalDateTime materializeObject(ResultSet rs, int index, int type) 
throws Exception {
+        return rs.getObject(index, LocalDateTime.class);
+    }
+
+    @Override
+    public LocalDateTime materializeObject(CallableStatement rs, int index, 
int type) throws Exception {
+        return rs.getObject(index, LocalDateTime.class);
+    }
+
+    @Override
+    public void setJdbcObject(
+            PreparedStatement st,
+            LocalDateTime val,
+            int pos,
+            int type,
+            int precision) throws Exception {
+
+        if (val == null) {
+            st.setNull(pos, type);
+        } else {
+            st.setObject(pos, val, type);
+        }
+    }
+
+    @Override
+    public String toString(LocalDateTime value) {
+        return String.valueOf(value);
+    }
+}
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/access/types/DateTimeTypesIT.java 
b/cayenne/src/test/java/org/apache/cayenne/access/types/DateTimeTypesIT.java
index a1769bc00..ab9a4b210 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/types/DateTimeTypesIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/types/DateTimeTypesIT.java
@@ -22,12 +22,15 @@ package org.apache.cayenne.access.types;
 import org.apache.cayenne.access.DataContext;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.query.SQLExec;
+import org.apache.cayenne.query.SQLSelect;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.testdo.datetime.DurationTestEntity;
 import org.apache.cayenne.testdo.datetime.LocalDateTestEntity;
 import org.apache.cayenne.testdo.datetime.LocalDateTimeTestEntity;
 import org.apache.cayenne.testdo.datetime.LocalTimeTestEntity;
 import org.apache.cayenne.testdo.datetime.PeriodTestEntity;
+import org.apache.cayenne.unit.MySQLUnitDbAdapter;
 import org.apache.cayenne.unit.UnitDbAdapter;
 import org.apache.cayenne.unit.di.runtime.CayenneProjects;
 import org.apache.cayenne.unit.di.runtime.RuntimeCase;
@@ -43,170 +46,227 @@ import java.time.LocalTime;
 import java.time.Period;
 import java.time.temporal.ChronoField;
 import java.time.temporal.TemporalField;
+import java.util.TimeZone;
 
 import static org.junit.Assert.*;
 
 @UseCayenneRuntime(CayenneProjects.DATE_TIME_PROJECT)
 public class DateTimeTypesIT extends RuntimeCase {
 
-       @Inject
-       private DataContext context;
+    @Inject
+    private DataContext context;
 
-       @Inject
-       private UnitDbAdapter unitDbAdapter;
+    @Inject
+    private UnitDbAdapter unitDbAdapter;
 
-       @Inject
-       private DBHelper dbHelper;
+    @Inject
+    private DBHelper dbHelper;
 
-       @Before
-       public void before() throws SQLException {
-               dbHelper.deleteAll("LOCAL_DATE_TEST");
-               dbHelper.deleteAll("LOCAL_DATETIME_TEST");
-               dbHelper.deleteAll("LOCAL_TIME_TEST");
-               dbHelper.deleteAll("DURATION_TEST");
-               dbHelper.deleteAll("PERIOD_TEST");
-       }
+    @Before
+    public void before() throws SQLException {
+        dbHelper.deleteAll("LOCAL_DATE_TEST");
+        dbHelper.deleteAll("LOCAL_DATETIME_TEST");
+        dbHelper.deleteAll("LOCAL_TIME_TEST");
+        dbHelper.deleteAll("DURATION_TEST");
+        dbHelper.deleteAll("PERIOD_TEST");
+    }
 
-       @Test
-       public void testLocalDate_Null() {
-               LocalDateTestEntity localDateTestEntity = 
context.newObject(LocalDateTestEntity.class);
-               localDateTestEntity.setDate(null);
+    @Test
+    public void testLocalDate_Null() {
+        LocalDateTestEntity localDateTestEntity = 
context.newObject(LocalDateTestEntity.class);
+        localDateTestEntity.setDate(null);
 
-               context.commitChanges();
+        context.commitChanges();
 
-               LocalDateTestEntity testRead = 
ObjectSelect.query(LocalDateTestEntity.class).selectOne(context);
+        LocalDateTestEntity testRead = 
ObjectSelect.query(LocalDateTestEntity.class).selectOne(context);
 
-               assertNull(testRead.getDate());
-       }
+        assertNull(testRead.getDate());
+    }
 
-       @Test
-       public void testLocalDate() {
-               LocalDateTestEntity localDateTestEntity = 
context.newObject(LocalDateTestEntity.class);
-               LocalDate localDate = LocalDate.now();
-               localDateTestEntity.setDate(localDate);
+    @Test
+    public void testLocalDate() {
+        LocalDateTestEntity localDateTestEntity = 
context.newObject(LocalDateTestEntity.class);
+        LocalDate localDate = LocalDate.now();
+        localDateTestEntity.setDate(localDate);
 
-               context.commitChanges();
+        context.commitChanges();
 
-               LocalDateTestEntity testRead = 
ObjectSelect.query(LocalDateTestEntity.class).selectOne(context);
+        LocalDateTestEntity testRead = 
ObjectSelect.query(LocalDateTestEntity.class).selectOne(context);
 
-               assertNotNull(testRead.getDate());
-               assertEquals(LocalDate.class, testRead.getDate().getClass());
-               assertEquals(localDate, testRead.getDate());
-       }
+        assertNotNull(testRead.getDate());
+        assertEquals(LocalDate.class, testRead.getDate().getClass());
+        assertEquals(localDate, testRead.getDate());
+    }
 
-       @Test
-       public void testLocalTime() {
-               LocalTimeTestEntity localTimeTestEntity = 
context.newObject(LocalTimeTestEntity.class);
-               LocalTime localTime = LocalTime.now();
-               localTimeTestEntity.setTime(localTime);
+    @Test
+    public void testLocalTime() {
+        LocalTimeTestEntity localTimeTestEntity = 
context.newObject(LocalTimeTestEntity.class);
+        LocalTime localTime = LocalTime.now();
+        localTimeTestEntity.setTime(localTime);
 
-               context.commitChanges();
+        context.commitChanges();
 
-               LocalTimeTestEntity testRead = 
ObjectSelect.query(LocalTimeTestEntity.class).selectOne(context);
+        LocalTimeTestEntity testRead = 
ObjectSelect.query(LocalTimeTestEntity.class).selectOne(context);
 
-               TemporalField testValue = unitDbAdapter.supportsPreciseTime()
-                               ? ChronoField.MILLI_OF_DAY
-                               : ChronoField.SECOND_OF_DAY;
+        TemporalField testValue = unitDbAdapter.supportsPreciseTime()
+                ? ChronoField.MILLI_OF_DAY
+                : ChronoField.SECOND_OF_DAY;
 
-               assertNotNull(testRead.getTime());
-               assertEquals(LocalTime.class, testRead.getTime().getClass());
-               assertEquals(localTime.toSecondOfDay(), 
testRead.getTime().toSecondOfDay());
-               assertEquals(localTime.get(testValue), 
testRead.getTime().get(testValue));
-       }
+        assertNotNull(testRead.getTime());
+        assertEquals(LocalTime.class, testRead.getTime().getClass());
+        assertEquals(localTime.toSecondOfDay(), 
testRead.getTime().toSecondOfDay());
+        assertEquals(localTime.get(testValue), 
testRead.getTime().get(testValue));
+    }
 
-       @Test
-       public void testLocalDateTime() {
-               LocalDateTimeTestEntity localDateTimeTestEntity = 
context.newObject(LocalDateTimeTestEntity.class);
-               // round up seconds fraction
-               // reason: on MySQL field should be defined as 
TIMESTAMP(fractionSecondsPrecision) to support it
-               LocalDateTime localDateTime = 
LocalDateTime.now().with(ChronoField.NANO_OF_SECOND, 0);
-               localDateTimeTestEntity.setTimestamp(localDateTime);
+    @Test
+    public void testLocalDateTime() {
+        LocalDateTimeTestEntity localDateTimeTestEntity = 
context.newObject(LocalDateTimeTestEntity.class);
+        // round up seconds fraction
+        // reason: on MySQL field should be defined as 
TIMESTAMP(fractionSecondsPrecision) to support it
+        LocalDateTime localDateTime = 
LocalDateTime.now().with(ChronoField.NANO_OF_SECOND, 0);
+        localDateTimeTestEntity.setTimestamp(localDateTime);
+
+        context.commitChanges();
+
+        LocalDateTimeTestEntity testRead = 
ObjectSelect.query(LocalDateTimeTestEntity.class).selectOne(context);
 
-               context.commitChanges();
+        assertNotNull(testRead.getTimestamp());
+        assertEquals(LocalDateTime.class, testRead.getTimestamp().getClass());
+        assertEquals(localDateTime, testRead.getTimestamp());
 
-               LocalDateTimeTestEntity testRead = 
ObjectSelect.query(LocalDateTimeTestEntity.class).selectOne(context);
+    }
+
+    @Test
+    public void testLocalDateTime_DST_Select() {
 
-               assertNotNull(testRead.getTimestamp());
-               assertEquals(LocalDateTime.class, 
testRead.getTimestamp().getClass());
-               assertEquals(localDateTime, testRead.getTimestamp());
+        if (!(unitDbAdapter instanceof MySQLUnitDbAdapter)) {
+            // skip other DB's for now until we can test across all of them
+            return;
+        }
+
+        // not using TableHelper to avoid PreparedStatement parameter 
interactions for this timestamp
+        SQLExec.query("insert into LOCAL_DATETIME_TEST values (1, '2021-03-14 
02:35:00')").execute(context);
 
-       }
+        // set the TZ where this time doesn't exist because of the DST 
switchover at 2 AM
+        TimeZone defaultTz = TimeZone.getDefault();
+        TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
 
-       @Test
-       public void columnSelectWithLocalDateTime() {
-               // round up seconds fraction
-               // reason: on MySQL field should be defined as 
TIMESTAMP(fractionSecondsPrecision) to support it
-               LocalDateTime localDateTime = 
LocalDateTime.now().with(ChronoField.NANO_OF_SECOND, 0);
+        try {
+            // LocalDateTime is TZ-agnostic, so the timestamp should not be 
rewound to 3 AM just because it happens to
+            // match the DST "gap" in US timezones
+            LocalDateTime value = 
ObjectSelect.query(LocalDateTimeTestEntity.class)
+                    .column(LocalDateTimeTestEntity.TIMESTAMP)
+                    .selectOne(context);
+
+            assertEquals(LocalDateTime.of(2021, 3, 14, 2, 35, 0), value);
+        } finally {
+            TimeZone.setDefault(defaultTz);
+        }
+    }
+
+    @Test
+    public void testLocalDateTime_DST_Insert() {
+
+        if (!(unitDbAdapter instanceof MySQLUnitDbAdapter)) {
+            // skip other DB's for now until we can test across all of them
+            return;
+        }
+
+        // set the TZ where this time doesn't exist because of the DST 
switchover at 2 AM
+        TimeZone defaultTz = TimeZone.getDefault();
+        TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
 
-               LocalDateTimeTestEntity localDateTimeTestEntity = 
context.newObject(LocalDateTimeTestEntity.class);
-               localDateTimeTestEntity.setTimestamp(localDateTime);
+        try {
 
-               context.commitChanges();
-
-               LocalDateTime value = 
ObjectSelect.query(LocalDateTimeTestEntity.class)
-                               .column(LocalDateTimeTestEntity.TIMESTAMP)
-                               .selectOne(context);
-               assertEquals(localDateTime, value);
-
-               LocalDateTime value2 = 
ObjectSelect.query(LocalDateTimeTestEntity.class)
-                               .min(LocalDateTimeTestEntity.TIMESTAMP)
-                               .selectOne(context);
-               assertEquals(localDateTime, value2);
-       }
-
-       @Test
-       public void testDuration() {
-               DurationTestEntity durationTestEntity = 
context.newObject(DurationTestEntity.class);
-               Duration duration = Duration.ofDays(10);
-               durationTestEntity.setDurationBigInt(duration);
-               durationTestEntity.setDurationDecimal(duration);
-               durationTestEntity.setDurationInt(duration);
-               durationTestEntity.setDurationLongVarchar(duration);
-               durationTestEntity.setDurationNumeric(duration);
-               durationTestEntity.setDurationVarchar(duration);
-
-               context.commitChanges();
-
-               DurationTestEntity testRead = 
ObjectSelect.query(DurationTestEntity.class).selectOne(context);
-
-               assertNotNull(testRead.getDurationBigInt());
-               assertEquals(Duration.class, 
testRead.getDurationBigInt().getClass());
-               assertEquals(duration, testRead.getDurationBigInt());
-
-               assertNotNull(testRead.getDurationDecimal());
-               assertEquals(Duration.class, 
testRead.getDurationDecimal().getClass());
-               assertEquals(duration, testRead.getDurationDecimal());
-
-               assertNotNull(testRead.getDurationInt());
-               assertEquals(Duration.class, 
testRead.getDurationInt().getClass());
-               assertEquals(duration, testRead.getDurationInt());
-
-               assertNotNull(testRead.getDurationLongVarchar());
-               assertEquals(Duration.class, 
testRead.getDurationLongVarchar().getClass());
-               assertEquals(duration, testRead.getDurationLongVarchar());
-
-               assertNotNull(testRead.getDurationNumeric());
-               assertEquals(Duration.class, 
testRead.getDurationNumeric().getClass());
-               assertEquals(duration, testRead.getDurationNumeric());
-
-               assertNotNull(testRead.getDurationVarchar());
-               assertEquals(Duration.class, 
testRead.getDurationVarchar().getClass());
-               assertEquals(duration, testRead.getDurationVarchar());
-       }
-
-       @Test
-       public void testPeriod() {
-               PeriodTestEntity periodTestEntity = 
context.newObject(PeriodTestEntity.class);
-               Period period = Period.of(100, 10, 5);
-               periodTestEntity.setPeriodField(period);
-
-               context.commitChanges();
-
-               PeriodTestEntity testRead = 
ObjectSelect.query(PeriodTestEntity.class).selectOne(context);
-
-               assertNotNull(testRead.getPeriodField());
-               assertEquals(Period.class, 
testRead.getPeriodField().getClass());
-               assertEquals(period, testRead.getPeriodField());
-       }
+            LocalDateTimeTestEntity o = 
context.newObject(LocalDateTimeTestEntity.class);
+            o.setTimestamp(LocalDateTime.of(2021, 3, 14, 2, 35, 0));
+            context.commitChanges();
+
+            String val = SQLSelect
+                    .scalarQuery("select CAST(TimestampField as CHAR) from 
LOCAL_DATETIME_TEST", String.class)
+                    .selectOne(context);
+
+            assertEquals("2021-03-14 02:35:00", val);
+        } finally {
+            TimeZone.setDefault(defaultTz);
+        }
+    }
+
+    @Test
+    public void columnSelectWithLocalDateTime() {
+        // round up seconds fraction
+        // reason: on MySQL field should be defined as 
TIMESTAMP(fractionSecondsPrecision) to support it
+        LocalDateTime localDateTime = 
LocalDateTime.now().with(ChronoField.NANO_OF_SECOND, 0);
+
+        LocalDateTimeTestEntity localDateTimeTestEntity = 
context.newObject(LocalDateTimeTestEntity.class);
+        localDateTimeTestEntity.setTimestamp(localDateTime);
+
+        context.commitChanges();
+
+        LocalDateTime value = ObjectSelect.query(LocalDateTimeTestEntity.class)
+                .column(LocalDateTimeTestEntity.TIMESTAMP)
+                .selectOne(context);
+        assertEquals(localDateTime, value);
+
+        LocalDateTime value2 = 
ObjectSelect.query(LocalDateTimeTestEntity.class)
+                .min(LocalDateTimeTestEntity.TIMESTAMP)
+                .selectOne(context);
+        assertEquals(localDateTime, value2);
+    }
+
+    @Test
+    public void testDuration() {
+        DurationTestEntity durationTestEntity = 
context.newObject(DurationTestEntity.class);
+        Duration duration = Duration.ofDays(10);
+        durationTestEntity.setDurationBigInt(duration);
+        durationTestEntity.setDurationDecimal(duration);
+        durationTestEntity.setDurationInt(duration);
+        durationTestEntity.setDurationLongVarchar(duration);
+        durationTestEntity.setDurationNumeric(duration);
+        durationTestEntity.setDurationVarchar(duration);
+
+        context.commitChanges();
+
+        DurationTestEntity testRead = 
ObjectSelect.query(DurationTestEntity.class).selectOne(context);
+
+        assertNotNull(testRead.getDurationBigInt());
+        assertEquals(Duration.class, testRead.getDurationBigInt().getClass());
+        assertEquals(duration, testRead.getDurationBigInt());
+
+        assertNotNull(testRead.getDurationDecimal());
+        assertEquals(Duration.class, testRead.getDurationDecimal().getClass());
+        assertEquals(duration, testRead.getDurationDecimal());
+
+        assertNotNull(testRead.getDurationInt());
+        assertEquals(Duration.class, testRead.getDurationInt().getClass());
+        assertEquals(duration, testRead.getDurationInt());
+
+        assertNotNull(testRead.getDurationLongVarchar());
+        assertEquals(Duration.class, 
testRead.getDurationLongVarchar().getClass());
+        assertEquals(duration, testRead.getDurationLongVarchar());
+
+        assertNotNull(testRead.getDurationNumeric());
+        assertEquals(Duration.class, testRead.getDurationNumeric().getClass());
+        assertEquals(duration, testRead.getDurationNumeric());
+
+        assertNotNull(testRead.getDurationVarchar());
+        assertEquals(Duration.class, testRead.getDurationVarchar().getClass());
+        assertEquals(duration, testRead.getDurationVarchar());
+    }
+
+    @Test
+    public void testPeriod() {
+        PeriodTestEntity periodTestEntity = 
context.newObject(PeriodTestEntity.class);
+        Period period = Period.of(100, 10, 5);
+        periodTestEntity.setPeriodField(period);
+
+        context.commitChanges();
+
+        PeriodTestEntity testRead = 
ObjectSelect.query(PeriodTestEntity.class).selectOne(context);
+
+        assertNotNull(testRead.getPeriodField());
+        assertEquals(Period.class, testRead.getPeriodField().getClass());
+        assertEquals(period, testRead.getPeriodField());
+    }
 
 }
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/unit/testcontainers/MysqlContainerProvider.java
 
b/cayenne/src/test/java/org/apache/cayenne/unit/testcontainers/MysqlContainerProvider.java
index aabfbe6e3..e52c98434 100644
--- 
a/cayenne/src/test/java/org/apache/cayenne/unit/testcontainers/MysqlContainerProvider.java
+++ 
b/cayenne/src/test/java/org/apache/cayenne/unit/testcontainers/MysqlContainerProvider.java
@@ -18,14 +18,14 @@
  ****************************************************************/
 package org.apache.cayenne.unit.testcontainers;
 
-import java.util.Calendar;
-
 import org.apache.cayenne.dba.JdbcAdapter;
 import org.apache.cayenne.dba.mysql.MySQLAdapter;
 import org.testcontainers.containers.JdbcDatabaseContainer;
 import org.testcontainers.containers.MySQLContainer;
 import org.testcontainers.utility.DockerImageName;
 
+import java.util.Calendar;
+
 public class MysqlContainerProvider extends TestContainerProvider {
 
     @Override
diff --git a/pom.xml b/pom.xml
index 3923d68e3..2330a835a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,7 @@
                <slf4j.version>1.7.36</slf4j.version>
                <ant.version>1.10.12</ant.version>
                <testcontainers.version>1.20.1</testcontainers.version>
+               <mysql.connector.version>8.0.29</mysql.connector.version>
 
                <maven.compiler.target>11</maven.compiler.target>
                <maven.compiler.source>11</maven.compiler.source>
@@ -399,6 +400,12 @@
                <version>10.14.2.0</version>
                <scope>test</scope>
                </dependency>
+                       <dependency>
+                               <groupId>mysql</groupId>
+                               <artifactId>mysql-connector-java</artifactId>
+                               <version>${mysql.connector.version}</version>
+                               <scope>test</scope>
+                       </dependency>
                        <dependency>
                                <groupId>org.xmlunit</groupId>
                                <artifactId>xmlunit-matchers</artifactId>
@@ -1031,7 +1038,6 @@
                                <dependency>
                                        <groupId>mysql</groupId>
                                        
<artifactId>mysql-connector-java</artifactId>
-                                       <version>8.0.29</version>
                                        <scope>test</scope>
                                </dependency>
                        </dependencies>
@@ -1048,7 +1054,6 @@
                                <dependency>
                                        <groupId>mysql</groupId>
                                        
<artifactId>mysql-connector-java</artifactId>
-                                       <version>8.0.29</version>
                                        <scope>test</scope>
                                </dependency>
                        </dependencies>

Reply via email to