有一个问题:
数据库表中定义一个字段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 字段类型 就能更好的把握。