Как сохранить Java Instant в базе данных MySQL

С Java Date Объекты самый простой способ был сохранить их как MySql DateTime объекты (в UTC). С переключателем на Instant этот подход больше не будет работать, потому что MySQL DateTime не предлагает точности для хранения наносекунд. Простое их усечение может привести к неожиданным результатам сравнения между вновь созданными Instant объекты и те, которые читаются из базы данных.

BigDecimal временные метки не кажутся мне элегантным решением: написание запроса выбора в MySql Workbench становится более трудным, потому что вам нужно конвертировать временную метку везде, чтобы сделать ее читабельной, а обработка в Java несколько неуклюжа по сравнению с Instant или даже Long ценности.

Какой лучший способ пойти сюда? Возможно нет varchar, право?

1 ответ

Решение

Обрезать до микросекунд

Очевидно, что мы не можем сжать разрешение наносекунды Instant в разрешении микросекунд типов данных MySQL DateTime а также Timestamp,

Я полагаю, что драйвер JDBC создан для игнорирования наносекунд при получении Instant, усечение значения до микросекунд. Я предлагаю вам попробовать эксперимент, чтобы увидеть, и, возможно, изучить исходный код вашего драйвера, который соответствует JDBC 4.2 и более поздним версиям.

Instant instant = Instant.now().with( ChronoField.NANO_OF_SECOND , 123_456_789L ) ;  //Set the fractional second to a spefic number of nanoseconds.
myPreparedStatement.setObject( … , instant ) ;

…а также…

Instant instant2 = myResultSet.getObject( … , Instant.class ) ;

Тогда сравните.

Boolean result = instant.equals( instant2 ) ;
System.out.println( "instant: " + instant + " equals instant2: = " + instant2 + " is: " + result ) ;

Вы мудро беспокоитесь о значениях, взятых из базы данных, которые не соответствуют исходному значению. Одним из решений, если оно приемлемо для вашей бизнес-задачи, является усечение любых наносекунд до микросекунд в исходных данных. Я рекомендую этот подход в целом.

Instant instant = Instant().now().truncatedTo( ChronoUnit.MICROSECONDS ) ;

В настоящее время такого подхода должно быть достаточно, поскольку в ваших данных вряд ли будут какие-либо наносекунды. Насколько я знаю, современные компьютеры не используют аппаратные часы, способные записывать наносекунды.

Считать с эпохи

Если вы не можете позволить себе потерять какие-либо наносекундные данные, которые могут присутствовать, используйте отсчет времени.

Я обычно рекомендую против отслеживания даты и времени в качестве отсчета от контрольной даты эпохи. Но у вас есть несколько других вариантов хранения значений на основе наносекунд в базе данных, такой как MySQL и Postgres, ограниченных значениями на основе микросекунд.

Хранить пару целых чисел

Вместо того, чтобы использовать чрезвычайно большое количество наносекунд со времен, таких как 1970-01-01T00:00Z, я предлагаю следовать подходу, принятому внутренними Instant класс: используйте пару чисел.

Сохраните количество целых секунд как целое число в вашей базе данных. Во втором столбце запишите в виде целого числа количество наносекунд в долях секунды.

Вы можете легко извлечь / ввести эти цифры из / в Instant объект. Только простой 64-битный long номера участвуют; нет необходимости BigDecimal или же BigInteger, Я полагаю, вы могли бы использовать 32-битный целочисленный столбец как минимум для одного из двух чисел. Но я бы выбрал 64-битные целочисленные типы столбцов для простоты и для прямой совместимости с java.time.Instant классная пара длинных

long seconds = instant.getEpochSecond() ;
long nanos = instant.getNano() ;

…а также…

Instant instant = Instant.ofEpochSecond( seconds , nanos ) ;

При сортировке в хронологическом порядке вам потребуется многоуровневая сортировка, сначала сортировка по столбцу целых секунд, а затем сортировка по столбцу доли секунды нанос.

Другие вопросы по тегам