熟悉Mysql的同學應該都知道,Mysql查詢的boolean結果將輸出為0或者1.
比如:
select 1=1;
其輸出結果為1。
查閱mysql官方文檔僅找到如下描述:
11.10 Using Data Types from Other Database Engines
To facilitate the use of code written for SQL implementations from other vendors, MySQL maps data types as shown in the following table. These mappings make it easier to import table definitions from other database systems into MySQL.
Other Vendor Type MySQL Type BOOL
TINYINT
BOOLEAN
TINYINT
CHARACTER VARYING(
M
)VARCHAR(
M
)FIXED
DECIMAL
FLOAT4
FLOAT
FLOAT8
DOUBLE
INT1
TINYINT
INT2
SMALLINT
INT3
MEDIUMINT
INT4
INT
INT8
BIGINT
LONG VARBINARY
MEDIUMBLOB
LONG VARCHAR
MEDIUMTEXT
LONG
MEDIUMTEXT
MIDDLEINT
MEDIUMINT
NUMERIC
DECIMAL
Other Vendor Type MySQL Type Data type mapping occurs at table creation time, after which the original type specifications are discarded. If you create a table with types used by other vendors and then issue a
DESCRIBE
statement, MySQL reports the table structure using the equivalent MySQL types. For example:tbl_name
mysql> CREATE TABLE t (a BOOL, b FLOAT8, c LONG VARCHAR, d NUMERIC); Query OK, 0 rows affected (0.00 sec) mysql> DESCRIBE t; +-------+---------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+---------------+------+-----+---------+-------+ | a | tinyint(1) | YES | | NULL | | | b | double | YES | | NULL | | | c | mediumtext | YES | | NULL | | | d | decimal(10,0) | YES | | NULL | | +-------+---------------+------+-----+---------+-------+ 4 rows in set (0.01 sec)
我想說的是,今天使用一套中間件對kafka消息進行解析為mysql 語句,其中遇到如下的問題,
- 目標表有一字段設置類型為:tinyint(1)。
- 源表同步消息中接收到相同類型的數據。
- 其中中間件中有如下解析部分:
public void setStatement(PreparedStatement statement, DatabaseType databaseType, boolean timestampChangeToLong) throws SQLException { if (this.value == null) { statement.setNull(this.index, this.sqlType); } else { switch(this.sqlType) { case -15: case -9: case 1: case 12: case 2005: String strVal = String.valueOf(this.value); statement.setString(this.index, strVal); break; case -7: case 16: boolean booleanVal = (Boolean)this.value; //tinyint(1) 類型的表設計字段直接進入該case,由於接收到的消息中的數據為0或者1,直接在該位置報類轉換異常。 statement.setBoolean(this.index, booleanVal); break; case -6: int val2 = (Integer)this.value; statement.setInt(this.index, val2); break; case -5: long longVal = (Long)this.value; statement.setLong(this.index, longVal); break; case 2: this.setStatementDataTypeNumeric(statement); break; case 3: this.setStatementDataTypeDecimal(statement, databaseType, timestampChangeToLong); break; case 4: int val = (Integer)this.value; statement.setInt(this.index, val); break; case 5: int val1 = (Integer)this.value; statement.setInt(this.index, val1); break; case 6: float floatVal = (Float)this.value; statement.setFloat(this.index, floatVal); break; case 8: double doubelVal = (Double)this.value; statement.setDouble(this.index, doubelVal); break; case 91: this.setStatementDataTypeDate(statement, databaseType); break; case 92: Date timeVal = (Date)this.value; Time sqlTime = new Time(timeVal.getTime()); statement.setTime(this.index, sqlTime); break; case 93: this.setStatementDataTypeTimestamp(statement, timestampChangeToLong); break; default: throw new ConsumeException("sqlType " + this.sqlType + " is not support"); } } }
- 怎樣獲取的數字類型呢,代碼如下:
protected Database loadInternal(String database) { Connection connection = null; Database var28; try { connection = this.dataSource.getConnection();//獲取連接 DatabaseMetaData metaData = connection.getMetaData();//獲取元數據 String catalog = null; String[] tableTypes = new String[]{"TABLE"}; String databasePattern = this.databaseSchema != null ? this.databaseSchema : database; ResultSet tablesResultSet = metaData.getTables((String)catalog, databasePattern, "%", tableTypes); Database db = new Database(); db.setName(database); Table tablei; while(tablesResultSet.next()) { String tableName = tablesResultSet.getString("TABLE_NAME"); tablei = new Table(tableName); db.addTable(tablei); } Iterator var27 = db.getTables().iterator(); while(var27.hasNext()) { tablei = (Table)var27.next(); ResultSet columnsResultSet = metaData.getColumns((String)catalog, databasePattern, tablei.getName(), (String)null); while(columnsResultSet.next()) { String columnName = columnsResultSet.getString("COLUMN_NAME"); int sqlType = columnsResultSet.getInt("DATA_TYPE");//此處拿到mysql返回的字段類型 String typeName = columnsResultSet.getString("TYPE_NAME"); int size = columnsResultSet.getInt("COLUMN_SIZE"); boolean nullable = 1 == columnsResultSet.getInt("NULLABLE"); Column column = new Column(); column.setName(columnName); column.setNullable(nullable); column.setSqlType(sqlType); column.setTypeName(typeName); column.setSize(size); tablei.addColumn(column); } } var28 = db; } catch (Exception var25) { throw new RuntimeException("load schema exception", var25); } finally { if (connection != null) { try { connection.close(); } catch (SQLException var24) { ; } } } return var28; }
- 也就是說,獲取字段類型時,字段tinyint(1)的類型被當做boolean類型進行了返回。導致java中Integer類型無法進行強轉。
解決方法:alter talbe change `xxx` `xxx` tinyint(4) ...;即可。修改tinyint數據類型長度,mysql也就不再當做boolean類型進行返回了。
總結:Mysql表結構設計時,要避免設計為tinyint(1)這種類型,以免與boolean類型數據結構進行混淆。引起不必要bug。當然也可以總java代碼中進行修改,修改后的影響,還需另外評估。