使用JDBC PreparedStatement插入闰年日期遇ORA-01839错误的解决方法
解决Oracle插入无效日期触发ORA-01839错误的方案
问题核心
插入1980-02-29时触发ORA-01839: date not valid for month specified,原因是Oracle会严格校验日期合法性——即便Java的Date对象能存储该值,Oracle接收时仍会判定为无效(注:1980是闰年,若仍触发错误,大概率是JDBC与Oracle日期格式/时区的兼容性问题,add_months函数可统一处理这类无效日期场景)。
用add_months在PreparedStatement中处理日期
add_months的特性是:当传入无效日期时,会自动将其调整为对应月份的最后一天。无需修改Java层日期处理逻辑,直接在SQL语句中调用该函数即可。
方案1:直接在SQL中处理参数
修改插入SQL,对日期参数使用add_months做自动调整:
// 修改SQL语句,用add_months处理日期参数 String personneString = "INSERT INTO personne (idPersonne, datedenaissance) VALUES (?, CASE WHEN ? IS NOT NULL THEN add_months(?, 0) ELSE NULL END)"; Date annivDate = (anniv == null) ? null : Date.valueOf(anniv); try (PreparedStatement statementPersonne = connection.prepareStatement(personneString)) { statementPersonne.setInt(1, idPersonne); statementPersonne.setDate(2, annivDate); // 用于NULL判断 statementPersonne.setDate(3, annivDate); // 传入add_months处理 statementPersonne.addBatch(); statementPersonne.executeBatch(); } catch (SQLException e) { System.out.println("date = " + annivDate); e.printStackTrace(); }
注:
add_months(?, 0)仅做日期合法性校验与调整,不会改变原有有效日期的值。
方案2:精准定位当月最后一天
如果需要明确将无效日期转为当月最后一天,可结合last_day和trunc函数,逻辑更直观:
String personneString = "INSERT INTO personne (idPersonne, datedenaissance) VALUES (?, CASE WHEN ? IS NOT NULL THEN last_day(trunc(?, 'MM')) ELSE NULL END)"; Date annivDate = (anniv == null) ? null : Date.valueOf(anniv); try (PreparedStatement statementPersonne = connection.prepareStatement(personneString)) { statementPersonne.setInt(1, idPersonne); statementPersonne.setDate(2, annivDate); statementPersonne.setDate(3, annivDate); statementPersonne.addBatch(); statementPersonne.executeBatch(); } catch (SQLException e) { System.out.println("date = " + annivDate); e.printStackTrace(); }
备选方案:Java层预处理日期
若不想修改SQL,可在Java代码中提前校验并调整日期:
Date annivDate = null; if (anniv != null) { LocalDate localDate = LocalDate.parse(anniv); // 检查日期是否合法,不合法则转为当月最后一天 try { LocalDate.of(localDate.getYear(), localDate.getMonth(), localDate.getDayOfMonth()); } catch (DateTimeException e) { localDate = localDate.with(TemporalAdjusters.lastDayOfMonth()); } annivDate = Date.valueOf(localDate); } // 后续PreparedStatement代码保持不变 try (PreparedStatement statementPersonne = connection.prepareStatement(personneString)) { statementPersonne.setInt(1, idPersonne); statementPersonne.setDate(2, annivDate); statementPersonne.addBatch(); statementPersonne.executeBatch(); } catch (SQLException e) { System.out.println("date = " + annivDate); e.printStackTrace(); }
内容的提问来源于stack exchange,提问作者Luis Angel Urena Lopez
相关产品推荐
相关产品推荐

