LocalDate::format
正如其他人指出的那样,您的行 System.out.println("Date: " + ld); 隐式调用 LocalDate::toString 而不使用您的 DateTimeFormatter 对象。所以你的自定义格式模式永远不会被应用。
在生成文本时应用您的格式化程序。不要调用LocalDate::toString,而是调用LocalDate::format。
String output = ld.format( formatter ) ;
System.out.println( "output: " + output ) ;
20/06/2020
你说:
查询以 JDBC 默认格式 (yyyy-mm-dd) 可视化。
不,没有“JDBC 默认格式”之类的东西。 LocalDate::toString 生成的文本是标准的ISO 8601 格式,YYYY-MM-DD。这与JDBC无关。
您的代码还有其他问题。所以请继续阅读。
使用智能对象,而不是哑字符串
假设您的数据库列的类型类似于 SQL 标准类型 DATE,请使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序将值作为 LocalDate 对象检索。停止思考文本。考虑智能对象,而不是愚蠢的字符串。
LocalDate localDate = myResultSet.getObject( … , LocalDate.class ) ;
同样,将适当的对象发送到数据库,而不仅仅是文本。 Use a prepared statement 和 ? 占位符。
myPreparedStatement.setObject( … , localDate ) ; // Send object to database, to be written into row in table.
将检索与格式化/展示分开
只有稍后您才应该考虑格式化数据以呈现给用户。通常最好将数据的检索与数据的格式/呈现分开。首先将数据收集到对象中,然后生成用于演示的文本。
使用即将推出的记录功能previewed in Java 14 和arriving in Java 15,将数据收集到对象中变得更加容易和简单。构造函数、getter方法、toString、equals&hashCode等都是在后台自动合成的。
record Order ( UUID id , String description , LocalDate whenPlaced ) {} // That is all the code you need. Constructor, getter methods, `toString`, `equals` & `hashCode`, and so on are all synthesized automatically behind the scenes.
以标准ISO 8601 格式生成表示对象内值的文本。请注意,LocalDate 对象没有文本,没有“格式”。这样的对象知道怎么解析文本,知道怎么生成文本,不是本身就是文本。
String output = localDate.toString() ;
让java.time在生成文本的同时自动本地化。
Locale locale = Locale.CANADA_FRENCH ; // or Locale.US, Locale.ITALY, etc.
DateTimeFormatter f =
DateTimeFormatter
.ofLocalizedDate( FormatStyle.MEDIUM )
.withLocale( locale ) ;
String output = localDate.format( f ) ; // Generate text representing the value of this object, while automatically localizing.
或者,如果您坚持,请指定您自己的自定义格式模式。
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/dd/uuuu" ) ;
String output = localDate.format( f ) ;
示例
这是一个完整示例的源代码。
此示例使用H2 Database Engine。我们选择in-memory database 选项,当JVM 关闭时它会消失。
首先,建立一个DataSource对象。
// Establish `DataSource`.
org.h2.jdbcx.JdbcDataSource dataSource = new JdbcDataSource();
// Set `DB_CLOSE_DELAY` to `-1` to keep in-memory database in existence after connection closes.
dataSource.setURL( "jdbc:h2:mem:pstmt_localdate_example_db;DB_CLOSE_DELAY=-1" );
dataSource.setUser( "scott" );
dataSource.setPassword( "tiger" );
在第一次连接时,数据库是隐式创建的。该名称来自我们上面DataSource 的URL 字段。然后我们执行SQL创建表order_。我们使用尾随下划线来避免命名冲突,因为 SQL 标准承诺永远不会以这种方式命名关键字。
顺便说一句,当text blocks feature 到达 Java 15 (previewed in Java 14) 时,在 Java 中嵌入 SQL 代码会容易得多。
// Create database implicitly upon connection, and create first table.
try (
Connection conn = dataSource.getConnection() ;
Statement stmt = conn.createStatement() ;
)
{
String sql =
"DROP TABLE IF EXISTS order_ ; \n "
+
"CREATE TABLE IF NOT EXISTS \n" +
" order_ \n" +
" ( \n" +
" pkey_ UUID NOT NULL DEFAULT RANDOM_UUID() PRIMARY KEY , \n" +
" description_ VARCHAR NOT NULL , \n" +
" when_placed_ DATE NOT NULL \n" +
" ) \n" +
";";
System.out.println( "sql = \n" + sql );
stmt.execute( sql );
}
catch ( SQLException e )
{
e.printStackTrace();
}
我们插入一行。
// Insert row.
try (
Connection conn = dataSource.getConnection() ;
Statement stmt = conn.createStatement() ;
)
{
String sql = "INSERT INTO order_ ( description_ , when_placed_ ) \n";
sql += "VALUES ( ? , ? ) \n";
sql += ";";
System.out.println( "sql = " + sql );
try (
PreparedStatement pstmt = conn.prepareStatement( sql , Statement.RETURN_GENERATED_KEYS ) ;
)
{
pstmt.setString( 1 , "blah" );
pstmt.setObject( 2 , LocalDate.now( ZoneId.of( "America/Montreal" ) ) );
pstmt.executeUpdate();
ResultSet rs = pstmt.getGeneratedKeys();
System.out.println( "INFO - Reporting generated keys." );
while ( rs.next() )
{
UUID uuid = rs.getObject( 1 , UUID.class );
System.out.println( "generated keys: " + uuid );
}
}
}
catch ( SQLException e )
{
e.printStackTrace();
}
将行转储到控制台。如果您进一步查看这段代码,您将看到我们如何使用自动本地化生成文本。我们使用自定义格式化程序生成文本。我建议通常通过硬编码的自定义格式进行本地化。
// Dump all rows.
try (
Connection conn = dataSource.getConnection() ;
Statement stmt = conn.createStatement() ;
)
{
System.out.println( "INFO - Reporting all rows in table `order_`." );
String sql = "SELECT * FROM order_ ; ";
System.out.println( "sql = " + sql );
try ( ResultSet rs = stmt.executeQuery( sql ) ; )
{
while ( rs.next() )
{
UUID pkey = rs.getObject( "pkey_" , UUID.class );
String description = rs.getString( "description_" );
LocalDate whenPlaced = rs.getObject( "when_placed_" , LocalDate.class );
// Dump to console.
System.out.println( "-----------------" );
System.out.println( "pkey = " + pkey );
System.out.println( "description = " + description );
System.out.println( "whenPlaced = " + whenPlaced ); // Standard ISO 8601 format.
// Localized.
Locale locale = new Locale( "fr" , "DZ" ); // French language, Algeria culture.
DateTimeFormatter formatterLocalized = DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).withLocale( locale );
String outputLocalized = whenPlaced.format( formatterLocalized );
System.out.println( "whenPlaced (localized): " + outputLocalized );
// Custom format.
DateTimeFormatter formatterCustom = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );
String outputCustom = whenPlaced.format( formatterCustom );
System.out.println( "whenPlaced (custom-format): " + outputCustom );
System.out.println( "-----------------" );
}
}
}
catch ( SQLException e )
{
e.printStackTrace();
}
运行时。
-----------------
pkey = a4388a40-738b-44bd-b01a-2c487c7e08bf
description = blah
whenPlaced = 2020-06-20
whenPlaced (localized): 20/06/2020
whenPlaced (custom-format): 20/06/2020
-----------------
顺便说一句,养成使用分号来终止 SQL 语句的习惯。在某些情况下你可以避免它的遗漏,但在其他情况下会导致问题。