数据库说「我做了点处理但没有报错」的信号就是 SQLWarning。这篇看警告挂在哪、能保留多久、什么情况下会被吞掉,以及它们如何让数据被悄悄改写。
drop table if exists 不存在的表之后 stmt.getWarnings():1 条
[1] PSQLWarning SQLState=00000 errorCode=0 消息=table "not_there" does not exist, skipping
select noisy(3)(3 个 NOTICE + WARNING + INFO + LOG + DEBUG)之后 stmt.getWarnings():5 条
[1] PSQLWarning SQLState=00000 errorCode=0 消息=notice 1
[2] PSQLWarning SQLState=00000 errorCode=0 消息=notice 2
[3] PSQLWarning SQLState=00000 errorCode=0 消息=notice 3
[4] PSQLWarning SQLState=01000 errorCode=0 消息=a warning
…
同一时刻 connection.getWarnings():(null,没有警告)
stmt.clearWarnings() 之后:(null,没有警告)
先 select noisy(1) 再 select 1(同一个 Statement)之后 stmt.getWarnings():(null,没有警告)(下一次 execute 会清空)
client_min_messages = warning 之后 select noisy(3):2 条
[1] PSQLWarning SQLState=01000 errorCode=0 消息=a warning
[2] PSQLWarning SQLState=00000 errorCode=0 消息=an info
client_min_messages = debug1:5 条
[1] PSQLWarning SQLState=00000 errorCode=0 消息=notice 1
[2] PSQLWarning SQLState=01000 errorCode=0 消息=a warning
[3] PSQLWarning SQLState=00000 errorCode=0 消息=an info
[4] PSQLWarning SQLState=00000 errorCode=0 消息=a log
…
executeQuery 返回 ResultSet 之后 rs.getWarnings():(null,没有警告);stmt.getWarnings():3 条
[1] PSQLWarning SQLState=00000 errorCode=0 消息=notice 1
[2] PSQLWarning SQLState=01000 errorCode=0 消息=a warning
[3] PSQLWarning SQLState=00000 errorCode=0 消息=an info
PreparedStatement 执行后 ps.getWarnings():4 条
[1] PSQLWarning SQLState=00000 errorCode=0 消息=notice 1
[2] PSQLWarning SQLState=00000 errorCode=0 消息=notice 2
[3] PSQLWarning SQLState=01000 errorCode=0 消息=a warning
[4] PSQLWarning SQLState=00000 errorCode=0 消息=an info
autoCommit=false 下手工 execute("commit") 之后(没有事务时 COMMIT 也会提示):(null,没有警告)
autoCommit=true 下 execute("commit"):1 条
[1] PSQLWarning SQLState=25P01 errorCode=0 消息=there is no transaction in progress
create table if not exists(第二次,表已存在):1 条
[1] PSQLWarning SQLState=42P07 errorCode=0 消息=relation "wtest" already exists, skipping
PG 里超长字符串插入 varchar(3):PSQLException(22001/0): ERROR: value too long for type character varying(3)(PG 报错,不是警告)
警告里 getSQLState / getErrorCode:00000 / 0;类型 org.postgresql.util.PSQLWarning;是否同时是 SQLException true;getNextWarning 链长度 4
已关闭的 Statement 上 getWarnings():PSQLException: This statement has been closed.
连接上的警告:(null,没有警告) 设置 sql_mode = ''(非严格模式)后,会话 sql_mode = “”(空)
非严格模式下插入(超长字符串、tinyint 超范围、非法日期、decimal 超范围)stmt.getWarnings():4 条
[1] SQLWarning SQLState=01000 errorCode=1265 消息=Data truncated for column 'v' at row 1
[2] SQLWarning SQLState=22003 errorCode=1264 消息=Out of range value for column 'n' at row 1
[3] SQLWarning SQLState=22003 errorCode=1264 消息=Out of range value for column 'd' at row 1
[4] SQLWarning SQLState=22003 errorCode=1264 消息=Out of range value for column 'dec_' at row 1
实际存进去的值:v=abc n=127 d=0000-00-00 dec_=999.9(数据被静默截断 / 改写)
紧接着在同一连接上用另一个 Statement 执行 show warnings(没有调用 getWarnings):[Warning 1265 Data truncated for column 'v' at row 1]
再执行一次 show warnings(上一条语句是 show warnings 自己):1 行
该 Statement s 此时 getWarnings():(null,没有警告)
先正常插入、再超长插入后只看最后一条语句的 getWarnings():1 条
[1] SQLWarning SQLState=01000 errorCode=1265 消息=Data truncated for column 'v' at row 1
再来一条正常插入:(null,没有警告)
严格模式下同样的超长字符串:MysqlDataTruncation(22001/1406): Data truncation: Data too long for column 'v' at row 1
insert ignore 主键冲突:1 条
[1] SQLWarning SQLState=23000 errorCode=1062 消息=Duplicate entry '1' for key 'w.PRIMARY';更新计数仍是 0
select 1/0, cast('12abc' as signed), 'a'+1:结果 null / 12 / 1.0;rs.getWarnings():(null,没有警告);stmt.getWarnings():2 条
[1] SQLWarning SQLState=22007 errorCode=1292 消息=Truncated incorrect INTEGER value: '12abc'
[2] SQLWarning SQLState=22007 errorCode=1292 消息=Truncated incorrect DOUBLE value: 'a'
update 把 tinyint 设成 999:3 条
[1] SQLWarning SQLState=22003 errorCode=1264 消息=Out of range value for column 'n' at row 6
[2] SQLWarning SQLState=22003 errorCode=1264 消息=Out of range value for column 'n' at row 7
[3] SQLWarning SQLState=22003 errorCode=1264 消息=Out of range value for column 'n' at row 8
max_error_count = 3 时同样的插入:3 条
[1] SQLWarning SQLState=01000 errorCode=1265 消息=Data truncated for column 'v' at row 1
[2] SQLWarning SQLState=22003 errorCode=1264 消息=Out of range value for column 'n' at row 1
[3] SQLWarning SQLState=22003 errorCode=1264 消息=Out of range value for column 'd' at row 1
drop table if exists 不存在的表:1 条
[1] SQLWarning SQLState=42S02 errorCode=1051 消息=Unknown table 'lab.not_there'
PreparedStatement 插入超长字符串 ps.getWarnings():1 条
[1] SQLWarning SQLState=01000 errorCode=1265 消息=Data truncated for column 'v' at row 1
executeBatch 3 条超长插入:返回 [1, 1, 1];ps.getWarnings():(null,没有警告)
connection.getWarnings():(null,没有警告)
(上面默认参数的连接在非严格模式下只得到警告;下面单独用 jdbcCompliantTruncation=true 重试)
jdbcCompliantTruncation=true + 非严格模式,插入超长字符串:MysqlDataTruncation(22001/1406): Data truncation: Data too long for column 'v' at row 1