Как сохранить 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 ) ;
При сортировке в хронологическом порядке вам потребуется многоуровневая сортировка, сначала сортировка по столбцу целых секунд, а затем сортировка по столбцу доли секунды нанос.