-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
新增filter,用于处理MySQL jdbc升级导致的兼容性问题#5273
新增filter用于处理MySQL jdbc升级导致的兼容性问题#5273
- Loading branch information
Showing
4 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
152 changes: 152 additions & 0 deletions
152
...rc/main/java/com/alibaba/druid/filter/mysql8datetime/MySQL8DateTimeResultSetMetaData.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
package com.alibaba.druid.filter.mysql8datetime; | ||
|
||
import java.sql.ResultSetMetaData; | ||
import java.sql.SQLException; | ||
import java.sql.Timestamp; | ||
import java.time.LocalDateTime; | ||
|
||
/** | ||
* 针对mysql jdbc 8.0.23及以上版本,通过该方法控制将对象类型转换成原来的类型 | ||
* @author lizongbo | ||
* @see <a href="https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/news-8-0-24.html">...</a> | ||
*/ | ||
public class MySQL8DateTimeResultSetMetaData implements ResultSetMetaData { | ||
private ResultSetMetaData resultSetMetaData; | ||
|
||
public MySQL8DateTimeResultSetMetaData(ResultSetMetaData resultSetMetaData) { | ||
super(); | ||
this.resultSetMetaData = resultSetMetaData; | ||
} | ||
|
||
@Override | ||
public <T> T unwrap(Class<T> iface) throws SQLException { | ||
return resultSetMetaData.unwrap(iface); | ||
} | ||
|
||
@Override | ||
public int getColumnCount() throws SQLException { | ||
return resultSetMetaData.getColumnCount(); | ||
} | ||
|
||
@Override | ||
public boolean isAutoIncrement(int column) throws SQLException { | ||
return resultSetMetaData.isAutoIncrement(column); | ||
} | ||
|
||
@Override | ||
public boolean isCaseSensitive(int column) throws SQLException { | ||
return resultSetMetaData.isCaseSensitive(column); | ||
} | ||
|
||
@Override | ||
public boolean isSearchable(int column) throws SQLException { | ||
return resultSetMetaData.isSearchable(column); | ||
} | ||
|
||
@Override | ||
public boolean isWrapperFor(Class<?> iface) throws SQLException { | ||
return resultSetMetaData.isWrapperFor(iface); | ||
} | ||
|
||
@Override | ||
public boolean isCurrency(int column) throws SQLException { | ||
return resultSetMetaData.isCurrency(column); | ||
} | ||
|
||
@Override | ||
public int isNullable(int column) throws SQLException { | ||
return resultSetMetaData.isNullable(column); | ||
} | ||
|
||
@Override | ||
public boolean isSigned(int column) throws SQLException { | ||
return resultSetMetaData.isSigned(column); | ||
} | ||
|
||
@Override | ||
public int getColumnDisplaySize(int column) throws SQLException { | ||
return resultSetMetaData.getColumnDisplaySize(column); | ||
} | ||
|
||
@Override | ||
public String getColumnLabel(int column) throws SQLException { | ||
return resultSetMetaData.getColumnLabel(column); | ||
} | ||
|
||
@Override | ||
public String getColumnName(int column) throws SQLException { | ||
return resultSetMetaData.getColumnName(column); | ||
} | ||
|
||
@Override | ||
public String getSchemaName(int column) throws SQLException { | ||
return resultSetMetaData.getSchemaName(column); | ||
} | ||
|
||
@Override | ||
public int getPrecision(int column) throws SQLException { | ||
return resultSetMetaData.getPrecision(column); | ||
} | ||
|
||
@Override | ||
public int getScale(int column) throws SQLException { | ||
return resultSetMetaData.getScale(column); | ||
} | ||
|
||
@Override | ||
public String getTableName(int column) throws SQLException { | ||
return resultSetMetaData.getTableName(column); | ||
} | ||
|
||
@Override | ||
public String getCatalogName(int column) throws SQLException { | ||
return resultSetMetaData.getCatalogName(column); | ||
} | ||
|
||
@Override | ||
public int getColumnType(int column) throws SQLException { | ||
return resultSetMetaData.getColumnType(column); | ||
} | ||
|
||
@Override | ||
public String getColumnTypeName(int column) throws SQLException { | ||
return resultSetMetaData.getColumnTypeName(column); | ||
} | ||
|
||
@Override | ||
public boolean isReadOnly(int column) throws SQLException { | ||
return resultSetMetaData.isReadOnly(column); | ||
} | ||
|
||
@Override | ||
public boolean isWritable(int column) throws SQLException { | ||
return resultSetMetaData.isWritable(column); | ||
} | ||
|
||
@Override | ||
public boolean isDefinitelyWritable(int column) throws SQLException { | ||
return resultSetMetaData.isDefinitelyWritable(column); | ||
} | ||
|
||
/** | ||
* 针对8.0.24版本开始,如果把mysql DATETIME映射回Timestamp,就需要把javaClass的类型也改回去 | ||
* 相关类在com.mysql.cj.MysqlType 中 | ||
* 旧版本jdbc为 | ||
* DATETIME("DATETIME", Types.TIMESTAMP, Timestamp.class, 0, MysqlType.IS_NOT_DECIMAL, 26L, "[(fsp)]"), | ||
* 8.0.24及以上版本jdbc实现改为 | ||
* DATETIME("DATETIME", Types.TIMESTAMP, LocalDateTime.class, 0, MysqlType.IS_NOT_DECIMAL, 26L, "[(fsp)]"), | ||
* @param column 列的索引位 | ||
* @return | ||
* @see java.sql.ResultSetMetaData#getColumnClassName(int) | ||
* @throws SQLException | ||
*/ | ||
@Override | ||
public String getColumnClassName(int column) throws SQLException { | ||
String className = resultSetMetaData.getColumnClassName(column); | ||
if (LocalDateTime.class.getName().equals(className)) { | ||
return Timestamp.class.getName(); | ||
} | ||
return className; | ||
} | ||
|
||
} |
61 changes: 61 additions & 0 deletions
61
core/src/main/java/com/alibaba/druid/filter/mysql8datetime/MySQL8DateTimeSqlTypeFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package com.alibaba.druid.filter.mysql8datetime; | ||
|
||
import java.sql.SQLException; | ||
import java.sql.Timestamp; | ||
import java.time.LocalDateTime; | ||
|
||
import com.alibaba.druid.filter.FilterAdapter; | ||
import com.alibaba.druid.filter.FilterChain; | ||
import com.alibaba.druid.proxy.jdbc.ResultSetProxy; | ||
|
||
/** | ||
* 针对mysql jdbc 8.0.23及以上版本,如果调用方没有使用orm框架,而是直接调用ResultSet的getObject方法,则针对DATETIME类型的字段,得到的对象从TimeStamp类型变成了LocalDateTime类型,导致调用方出现类型转换异常 | ||
* 通过Filter控制将对象类型转换成原来的类型 | ||
* | ||
* @author lizongbo | ||
* @see <a href="https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/news-8-0-23.html">MySQL 8.0.23 更新说明</a> | ||
*/ | ||
public class MySQL8DateTimeSqlTypeFilter extends FilterAdapter { | ||
|
||
/** | ||
* 针对mysql jdbc 8.0.23及以上版本,通过该方法控制将对象类型转换成原来的类型 | ||
* @param chain | ||
* @param result | ||
* @param columnIndex | ||
* @return | ||
* @see java.sql.ResultSet#getObject(int) | ||
* @throws SQLException | ||
*/ | ||
@Override | ||
public Object resultSet_getObject(FilterChain chain, ResultSetProxy result, int columnIndex) throws SQLException { | ||
return getObjectReplaceLocalDateTime(super.resultSet_getObject(chain, result, columnIndex)); | ||
} | ||
|
||
/** | ||
* 针对mysql jdbc 8.0.23及以上版本,通过该方法控制将对象类型转换成原来的类型 | ||
* @param chain | ||
* @param result | ||
* @param columnLabel | ||
* @return | ||
* @see java.sql.ResultSet#getObject(String) | ||
* @throws SQLException | ||
*/ | ||
@Override | ||
public Object resultSet_getObject(FilterChain chain, ResultSetProxy result, String columnLabel) throws SQLException { | ||
return getObjectReplaceLocalDateTime(super.resultSet_getObject(chain, result, columnLabel)); | ||
} | ||
|
||
/** | ||
* 针对mysql jdbc 8.0.23及以上版本,通过该方法控制将对象类型转换成原来的类型 | ||
* | ||
* @param obj | ||
* @return | ||
*/ | ||
public static Object getObjectReplaceLocalDateTime(Object obj) { | ||
if (!(obj instanceof LocalDateTime)) { | ||
return obj; | ||
} | ||
// 针对升级到了mysql jdbc 8.0.23以上的情况,转换回老的兼容类型 | ||
return Timestamp.valueOf((LocalDateTime) obj); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
core/src/test/java/com/alibaba/druid/bvt/filter/MySQL8DateTimeSqlTypeFilterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.alibaba.druid.bvt.filter; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.sql.Statement; | ||
import java.time.LocalDateTime; | ||
|
||
import com.alibaba.druid.filter.mysql8datetime.MySQL8DateTimeSqlTypeFilter; | ||
import com.alibaba.druid.mock.MockDriver; | ||
import com.alibaba.druid.mock.MockPreparedStatement; | ||
import com.alibaba.druid.mock.MockResultSet; | ||
import com.alibaba.druid.mock.MockStatementBase; | ||
import com.alibaba.druid.pool.DruidDataSource; | ||
import com.alibaba.druid.pool.DruidPooledConnection; | ||
import com.alibaba.druid.util.JdbcUtils; | ||
|
||
import junit.framework.TestCase; | ||
import java.sql.Timestamp; | ||
import org.junit.Assert; | ||
|
||
/** | ||
* lizongbo | ||
*/ | ||
public class MySQL8DateTimeSqlTypeFilterTest extends TestCase { | ||
|
||
private DruidDataSource dataSource; | ||
|
||
protected void setUp() throws Exception { | ||
dataSource = new DruidDataSource(); | ||
|
||
dataSource.setUrl("jdbc:mock:xxx"); | ||
dataSource.setFilters("mysql8DateTime"); | ||
|
||
dataSource.setDriver(new MockDriver() { | ||
public ResultSet createResultSet(MockPreparedStatement stmt) { | ||
return new MyResultSet(stmt); | ||
} | ||
|
||
public ResultSet executeQuery(MockStatementBase stmt, String sql) throws SQLException { | ||
return new MyResultSet(stmt); | ||
} | ||
}); | ||
|
||
dataSource.init(); | ||
} | ||
|
||
protected void tearDown() throws Exception { | ||
JdbcUtils.close(dataSource); | ||
} | ||
|
||
public void test_mysql8datetime() throws Exception { | ||
Assert.assertTrue(dataSource.isInited()); | ||
|
||
MySQL8DateTimeSqlTypeFilter filter = (MySQL8DateTimeSqlTypeFilter) dataSource.getProxyFilters().get(0); | ||
|
||
DruidPooledConnection conn = dataSource.getConnection(); | ||
|
||
final String PARAM_VALUE = "中国"; | ||
PreparedStatement stmt = conn.prepareStatement("select ?"); | ||
stmt.setString(1, PARAM_VALUE); | ||
|
||
ResultSet rs = stmt.executeQuery(); | ||
MyResultSet rawRs = rs.unwrap(MyResultSet.class); | ||
|
||
rs.next(); | ||
Object obj1 = rs.getObject(1); | ||
System.out.println(obj1.getClass() + "|" + obj1); | ||
Assert.assertEquals(Timestamp.class, obj1.getClass()); | ||
Object obj2 = rs.getObject("cc"); | ||
System.out.println(obj2.getClass() + "|" + obj2); | ||
Assert.assertEquals(Timestamp.class, obj2.getClass()); | ||
|
||
rs.close(); | ||
stmt.close(); | ||
|
||
conn.close(); | ||
|
||
} | ||
|
||
public static class MyResultSet extends MockResultSet { | ||
|
||
public MyResultSet(Statement statement) { | ||
super(statement); | ||
} | ||
|
||
@Override | ||
public Object getObject(int index) throws SQLException { | ||
return LocalDateTime.now(); | ||
} | ||
|
||
@Override | ||
public Object getObject(String columnLabel) throws SQLException { | ||
return LocalDateTime.now(); | ||
} | ||
|
||
|
||
} | ||
} |