有一个问题:
数据库表中定义一个字段A,类型int;使用Java 通过Mysql驱动读取的时候,获取的 Java类型就是 Integer;把字段A的类型修改成bit类型,再读取时,获取的Java类型就是Boolean类型,这是怎么做到的呢?
揭秘:
第一步:驱动 读取 数据库 表字段类型
// com.mysql.cj.protocol.a.ColumnDefinitionReader#read
/* read the metadata from the server */
Field[] fields = null;
boolean checkEOF = !this.protocol.getServerSession().isEOFDeprecated();
// Read in the column information
fields = new Field[(int) columnCount];
for (int i = 0; i < columnCount; i++) {
NativePacketPayload fieldPacket = this.protocol.readMessage(null);
// next check is needed for SSPS
if (checkEOF && fieldPacket.isEOFPacket()) {
break;
}
fields[i] = unpackField(fieldPacket,
this.protocol.getServerSession().getCharacterSetMetadata());
}
通过第一步,我们再 表中定义的 字段 类型 就拿到了。 既然拿到了字段类型,那么 就可以 转换成对应的Java类型了。
第二步: 读取
读取某个字段的值时,首先会获取 对应字段的 Field 对象,该对象中定义了 当前字段的类型,根据不同的类型,使用不用的方式来读取数据。
// com.mysql.cj.jdbc.result.ResultSetImpl#getObject(int)
Field field = this.columnDefinition.getFields()[columnIndexMinusOne];
switch (field.getMysqlType()) {
case BIT:
// 代码省略
return field.isSingleBit() ? Boolean.valueOf(getBoolean(columnIndex)) : getBytes(columnIndex);
case BOOLEAN:
return Boolean.valueOf(getBoolean(columnIndex));
case TINYINT:
return Integer.valueOf(getByte(columnIndex));
case TINYINT_UNSIGNED:
case SMALLINT:
case SMALLINT_UNSIGNED:
case MEDIUMINT:
case MEDIUMINT_UNSIGNED:
case INT:
return Integer.valueOf(getInt(columnIndex));
case INT_UNSIGNED:
case BIGINT:
return Long.valueOf(getLong(columnIndex));
case BIGINT_UNSIGNED:
return getBigInteger(columnIndex);
case DECIMAL:
case DECIMAL_UNSIGNED:
String stringVal = getString(columnIndex);
if (stringVal != null) {
// 代码省略
return new BigDecimal(stringVal);
}
return null;
case FLOAT:
case FLOAT_UNSIGNED:
return new Float(getFloat(columnIndex));
case DOUBLE:
case DOUBLE_UNSIGNED:
return new Double(getDouble(columnIndex));
case CHAR:
case ENUM:
case SET:
case VARCHAR:
case TINYTEXT:
return getString(columnIndex);
case TEXT:
case MEDIUMTEXT:
case LONGTEXT:
case JSON:
return getStringForClob(columnIndex);
case GEOMETRY:
return getBytes(columnIndex);
case BINARY:
case VARBINARY:
case TINYBLOB:
case MEDIUMBLOB:
case LONGBLOB:
case BLOB:
if (field.isBinary() || field.isBlob()) {
// 代码省略
return data;
}
return getBytes(columnIndex);
case YEAR:
return this.yearIsDateType ? getDate(columnIndex) : Short.valueOf(getShort(columnIndex));
case DATE:
return getDate(columnIndex);
case TIME:
return getTime(columnIndex);
case TIMESTAMP:
return getTimestamp(columnIndex);
case DATETIME:
return getLocalDateTime(columnIndex);
default:
return getString(columnIndex);
}
如果读取的字段想转成自己指定的类型,那么可以在获取字段数据时指定具体的期望转换的类型就可以了。本来是Boolean类型:
指定为Integer类型读取的对象就是Integer类型
原理总结:
数据库驱动 从 数据库服务 获取到 数据流之后,也就是字节数组,会根据 字段的实际类型,从字节数组中 读取 指定的 字节数,将字节转换成对应的Java对象类型。
有了字节数组,有了起始位置,有了长度,有类型转换工厂。那么 反序列化 之后,就得的我们需要的数据了。
使用:
如果 数据库字段类型 和 Java 定义的 字段类型不一致,只要 类型转换 没问题,程序就没问题。比如: 数据库字段类型是 bit, 读取到Java类型是BigDecimal,一样没问题
字段类型是bit,读取到Java类型是 Date,会报错:
了解这些之后,对使用 数据库字段类型 和 Java 字段类型 就能更好的把握。