diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 071c9214776..71b4194dc59 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,4 +80,4 @@ jobs: # step 4 - name: "Test with Maven" run: | - ./mvnw -T 4C clean test -Dspring-boot.version=${{ matrix.springboot }} -Dspring-boot-for-server.version=2.5.14 -Dspring-framework-for-server.version=5.3.20 -Dkotlin-maven-plugin.version=1.7.22 -Dcheckstyle.skip=true -Dlicense.skip=true -e -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn; + ./mvnw -T 4C clean test -Dspring-boot.version=${{ matrix.springboot }} -Dspring-boot-for-server.version=2.7.17 -Dspring-framework-for-server.version=5.3.30 -Dkotlin-maven-plugin.version=1.7.22 -Dcheckstyle.skip=true -Dlicense.skip=true -e -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn; diff --git a/all/pom.xml b/all/pom.xml index 3d5d2696241..c2178c7183b 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -184,6 +184,16 @@ + + io.seata + seata-rm-datasource-mysql5 + ${project.version} + + + io.seata + seata-rm-datasource-mysql8 + ${project.version} + io.seata seata-sqlparser-core diff --git a/bom/pom.xml b/bom/pom.xml index e71dc4c3988..7223960e2af 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -193,6 +193,16 @@ seata-rm-datasource ${project.version} + + io.seata + seata-rm-datasource-mysql5 + ${project.version} + + + io.seata + seata-rm-datasource-mysql8 + ${project.version} + - 2.5.13 - 5.3.20 + 2.7.17 + 5.3.30 5.8.2 diff --git a/changes/en-us/develop.md b/changes/en-us/develop.md index 1b2f57f1de7..5d189721f02 100644 --- a/changes/en-us/develop.md +++ b/changes/en-us/develop.md @@ -10,6 +10,7 @@ Add changes here for all PR submitted to the develop branch. - [[#5991](https://github.com/seata/seata/pull/5991)] fix the issue that the Lua script is not synchronized when the redis sentinel master node is down - [[#6025](https://github.com/seata/seata/pull/6025)] fix the white screen after click the "View Global Lock" button on the transaction info page in the console - [[#6026](https://github.com/seata/seata/pull/6026)] fix incorrect metric report +- [[#6068](https://github.com/seata/seata/pull/6068)] fix the column `TINYINT(1)` in MySql being resolved to `BIT` type ### optimize: - [[#6044](https://github.com/seata/seata/pull/6044)] optimize derivative product check base on mysql @@ -27,6 +28,6 @@ Thanks to these contributors for their code commits. Please report an unintended - [jsbxyyx](https://github.com/jsbxyyx) - [liuqiufeng](https://github.com/liuqiufeng) - [ptyin](https://github.com/ptyin) - +- [wangliang181230](https://github.com/wangliang181230) Also, we receive many valuable issues, questions and advices from our community. Thanks for you all. diff --git a/changes/zh-cn/develop.md b/changes/zh-cn/develop.md index 2eb0a37fbac..1a42b583aa7 100644 --- a/changes/zh-cn/develop.md +++ b/changes/zh-cn/develop.md @@ -10,6 +10,7 @@ - [[#5991](https://github.com/seata/seata/pull/5991)] 修复redis sentinel master node 宕机时,lua脚本未同步的问题 - [[#6025](https://github.com/seata/seata/pull/6025)] 修复控制台点击事务信息页面中的"查看全局锁"按钮之后白屏的问题 - [[#6026](https://github.com/seata/seata/pull/6026)] 修复异常的打点 +- [[#6068](https://github.com/seata/seata/pull/6068)] 修复 “MySql中的 `TINYINT(1)` 类型字段被解析为`BIT`,导致回滚后的值要么是0,要么是1。而不是原来的值。” 的问题。 ### optimize: - [[#6044](https://github.com/seata/seata/pull/6044)] 优化MySQL衍生数据库判断逻辑 @@ -27,5 +28,6 @@ - [jsbxyyx](https://github.com/jsbxyyx) - [liuqiufeng](https://github.com/liuqiufeng) - [ptyin](https://github.com/ptyin) +- [wangliang181230](https://github.com/wangliang181230) 同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。 diff --git a/common/src/main/java/io/seata/common/loader/DependsOnException.java b/common/src/main/java/io/seata/common/loader/DependsOnException.java new file mode 100644 index 00000000000..68a5e1596f9 --- /dev/null +++ b/common/src/main/java/io/seata/common/loader/DependsOnException.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.common.loader; + +/** + * The DependsOn Exception + * + * @author wang.liang + */ +class DependsOnException extends Exception { + + public DependsOnException(String message) { + super(message); + } + + public DependsOnException(String message, Throwable cause) { + super(message, cause); + } + + public DependsOnException(Throwable cause) { + super(cause); + } +} diff --git a/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java b/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java index 69f7189a099..4031f1ed297 100644 --- a/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java +++ b/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java @@ -32,6 +32,7 @@ import io.seata.common.Constants; import io.seata.common.executor.Initialize; import io.seata.common.util.CollectionUtils; +import io.seata.common.util.ReflectionUtil; import io.seata.common.util.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; @@ -339,7 +340,7 @@ private S load(String activateName, Object[] args, ClassLoader loader) * @param activateName the activate name * @param argsType the args type * @param args the args - * @param loader the class loader + * @param loader the class loader * @return the s * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ @@ -350,8 +351,8 @@ private S load(String activateName, Class[] argsType, Object[] args, ClassLoa /** * get all implements - * @param loader the class loader * + * @param loader the class loader * @return list list */ private List loadAll(ClassLoader loader) { @@ -541,7 +542,7 @@ private void loadFile(String dir, ClassLoader loader, List getUnloadedExtensionDefinition(String className, ClassLoader loader) - throws ClassNotFoundException, ClassCastException { + throws ClassNotFoundException, ClassCastException, DependsOnException { //Check whether the definition has been loaded if (!isDefinitionContainsClazz(className, loader)) { Class clazz = Class.forName(className, true, loader); @@ -584,11 +585,17 @@ private ExtensionDefinition getUnloadedExtensionDefinition(String className, String serviceName = null; int priority = 0; Scope scope = Scope.SINGLETON; - LoadLevel loadLevel = clazz.getAnnotation(LoadLevel.class); - if (loadLevel != null) { - serviceName = loadLevel.name(); - priority = loadLevel.order(); - scope = loadLevel.scope(); + try { + LoadLevel loadLevel = clazz.getAnnotation(LoadLevel.class); + if (loadLevel != null) { + serviceName = loadLevel.name(); + priority = loadLevel.order(); + scope = loadLevel.scope(); + + this.verifyDependsOn(loadLevel); + } + } catch (ArrayStoreException | TypeNotPresentException e) { + throw new DependsOnException("Verify depends on classes failed.", e); } ExtensionDefinition result = new ExtensionDefinition<>(serviceName, priority, scope, enhancedServiceClass); classToDefinitionMap.put(clazz, result); @@ -601,6 +608,20 @@ private ExtensionDefinition getUnloadedExtensionDefinition(String className, return null; } + private void verifyDependsOn(LoadLevel loadLevel) throws DependsOnException { + // In java11 and above, a TypeNotPresentException will throw only after obtaining it once, + // if any dependent class does not exist. + @SuppressWarnings("unused") + Class[] dependsOnClasses = loadLevel.dependsOnClasses(); + + // verify depends on class names + for (String className : loadLevel.dependsOnClassNames()) { + if (!ReflectionUtil.existsClass(className)) { + throw new DependsOnException("The dependent class '" + className + "' does not exist"); + } + } + } + private boolean isDefinitionContainsClazz(String className, ClassLoader loader) { for (Map.Entry, ExtensionDefinition> entry : classToDefinitionMap.entrySet()) { if (!entry.getKey().getName().equals(className)) { @@ -654,6 +675,7 @@ private S initInstance(Class implClazz, Class[] argTypes, Object[] args) /** * Helper Class for hold a value. + * * @param */ private static class Holder { diff --git a/common/src/main/java/io/seata/common/loader/LoadLevel.java b/common/src/main/java/io/seata/common/loader/LoadLevel.java index 1c974a6a03f..605cd5e949f 100644 --- a/common/src/main/java/io/seata/common/loader/LoadLevel.java +++ b/common/src/main/java/io/seata/common/loader/LoadLevel.java @@ -48,4 +48,18 @@ * Scope enum. */ Scope scope() default Scope.SINGLETON; + + /** + * Depends on classes. + * + * @return the classes + */ + Class[] dependsOnClasses() default {}; + + /** + * Depends on class names. + * + * @return the class names + */ + String[] dependsOnClassNames() default {}; } diff --git a/common/src/test/java/io/seata/common/loader/AfricaHello.java b/common/src/test/java/io/seata/common/loader/AfricaHello.java new file mode 100644 index 00000000000..20cff71a39d --- /dev/null +++ b/common/src/test/java/io/seata/common/loader/AfricaHello.java @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.common.loader; + +/** + * The type Africa hello. + * + * @author wang.liang + */ +@LoadLevel(name = "AfricaHello", dependsOnClasses = String.class, dependsOnClassNames = "java.lang.Integer") +public class AfricaHello implements Hello3 { +} diff --git a/common/src/test/java/io/seata/common/loader/AustraliaHello.java b/common/src/test/java/io/seata/common/loader/AustraliaHello.java new file mode 100644 index 00000000000..4e71d12d839 --- /dev/null +++ b/common/src/test/java/io/seata/common/loader/AustraliaHello.java @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.common.loader; + +/** + * The type Australia hello. + * + * @author wang.liang + */ +@LoadLevel(name = "AustraliaHello", dependsOnClassNames = "io.seata.Xxxxx") +public class AustraliaHello implements Hello3 { +} diff --git a/common/src/test/java/io/seata/common/loader/EnhancedServiceLoaderTest.java b/common/src/test/java/io/seata/common/loader/EnhancedServiceLoaderTest.java index dd1d6a69c00..8dabaa26fc7 100644 --- a/common/src/test/java/io/seata/common/loader/EnhancedServiceLoaderTest.java +++ b/common/src/test/java/io/seata/common/loader/EnhancedServiceLoaderTest.java @@ -133,4 +133,10 @@ public void classCastExceptionTest() { }); } + @Test + public void testDependsOn() { + List hello3List = EnhancedServiceLoader.loadAll(Hello3.class); + assertThat(hello3List.size()).isEqualTo(1); + assertThat(hello3List.get(0).getClass()).isEqualTo(AfricaHello.class); + } } diff --git a/common/src/test/java/io/seata/common/loader/Hello3.java b/common/src/test/java/io/seata/common/loader/Hello3.java new file mode 100644 index 00000000000..6d015bfffaf --- /dev/null +++ b/common/src/test/java/io/seata/common/loader/Hello3.java @@ -0,0 +1,22 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.common.loader; + +/** + * @author wang.liang + */ +interface Hello3 { +} diff --git a/common/src/test/resources/META-INF/seata/io.seata.common.loader.Hello3 b/common/src/test/resources/META-INF/seata/io.seata.common.loader.Hello3 new file mode 100644 index 00000000000..7898217a5e7 --- /dev/null +++ b/common/src/test/resources/META-INF/seata/io.seata.common.loader.Hello3 @@ -0,0 +1,2 @@ +io.seata.common.loader.AfricaHello +io.seata.common.loader.AustraliaHello \ No newline at end of file diff --git a/core/src/main/java/io/seata/core/context/FastThreadLocalContextCore.java b/core/src/main/java/io/seata/core/context/FastThreadLocalContextCore.java index 7840f8fa804..343f30ad5c4 100644 --- a/core/src/main/java/io/seata/core/context/FastThreadLocalContextCore.java +++ b/core/src/main/java/io/seata/core/context/FastThreadLocalContextCore.java @@ -26,7 +26,7 @@ * * @author ph3636 */ -@LoadLevel(name = "FastThreadLocalContextCore", order = Integer.MIN_VALUE + 1) +@LoadLevel(name = "FastThreadLocalContextCore", order = Integer.MIN_VALUE + 1, dependsOnClasses = FastThreadLocal.class) public class FastThreadLocalContextCore implements ContextCore { private FastThreadLocal> fastThreadLocal = new FastThreadLocal>() { diff --git a/pom.xml b/pom.xml index 44071516399..aa0c5e6c4f8 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> io.seata - seata-build + seata-dependencies ${revision} - ./build/pom.xml + ./dependencies/pom.xml 4.0.0 @@ -52,6 +52,8 @@ integration/brpc rm rm-datasource + rm-datasource-mysql5 + rm-datasource-mysql8 spring spring-aot spring-framework-fake-for-java8 @@ -104,18 +106,6 @@ - - - - io.seata - seata-dependencies - ${project.version} - pom - import - - - - @@ -158,15 +148,13 @@ false false - 5.1.42 - 8.0.27 release-image-based-on-java8 - ${project.version},latest + ${project.version} diff --git a/rm-datasource-mysql5/pom.xml b/rm-datasource-mysql5/pom.xml new file mode 100644 index 00000000000..57bbe1d5a94 --- /dev/null +++ b/rm-datasource-mysql5/pom.xml @@ -0,0 +1,45 @@ + + + + + + io.seata + seata-parent + ${revision} + + 4.0.0 + seata-rm-datasource-mysql5 + jar + seata-rm-datasource-mysql5 ${project.version} + datasource resource manager(mysql5) for Seata built with Maven + + + + ${project.groupId} + seata-rm-datasource + ${project.version} + + + mysql + mysql-connector-java + ${mysql5.version} + provided + + + diff --git a/rm-datasource-mysql5/src/main/java/io/seata/rm/datasource/sql/struct/cache/Mysql5ColumnMetaProcessor.java b/rm-datasource-mysql5/src/main/java/io/seata/rm/datasource/sql/struct/cache/Mysql5ColumnMetaProcessor.java new file mode 100644 index 00000000000..05a0e6f3b6e --- /dev/null +++ b/rm-datasource-mysql5/src/main/java/io/seata/rm/datasource/sql/struct/cache/Mysql5ColumnMetaProcessor.java @@ -0,0 +1,101 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.rm.datasource.sql.struct.cache; + +import com.mysql.jdbc.Field; +import com.mysql.jdbc.MysqlDefs; +import com.mysql.jdbc.ResultSetMetaData; +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.ReflectionUtil; +import io.seata.sqlparser.struct.ColumnMeta; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.lang.reflect.InvocationTargetException; +import java.sql.SQLException; +import java.sql.Types; + +import static io.seata.sqlparser.util.JdbcConstants.MYSQL5; + +/** + * The ColumnMeta processor for mysql5. + * + * @author wang.liang + */ +@LoadLevel(name = MYSQL5, dependsOnClasses = {ResultSetMetaData.class, Field.class}) +public class Mysql5ColumnMetaProcessor implements IColumnMetaProcessor { + + private static final Logger LOGGER = LoggerFactory.getLogger(Mysql5ColumnMetaProcessor.class); + + /** + * @see MysqlDefs#FIELD_TYPE_TINY + */ + private static final int FIELD_TYPE_TINY = 1; + + + @Override + public void process(ColumnMeta columnMeta, int columnIndex, ColumnMetaProcessorContext context) { + this.readRealDataType(columnMeta, columnIndex, context); + } + + + /** + * Read the realDataType for fix issue#6064 + * + * @param columnMeta the column meta + * @param columnIndex the column index + * @param context the context + * @see issue#6064 + * @see ResultSetMetaData#getField(int) + */ + protected void readRealDataType(ColumnMeta columnMeta, int columnIndex, ColumnMetaProcessorContext context) { + if (columnMeta.getRealDataType() != null || columnMeta.getDataType() == Types.TINYINT) { + return; + } + + // unwrap ResultSetMetaData + ResultSetMetaData rsmd; + try { + rsmd = context.getResultSetMetaData().unwrap(ResultSetMetaData.class); + } catch (SQLException ignore) { + return; + } + + // get field + Field field = this.getField(rsmd, columnMeta.getColumnName(), columnIndex); + if (field == null) { + return; + } + + if (field.getMysqlType() == FIELD_TYPE_TINY) { + columnMeta.setRealDataType(Types.TINYINT); + } + } + + @Nullable + private Field getField(ResultSetMetaData rsmd, String columnName, int columnIndex) { + try { + // the columnIndex of the method 'ResultSetMetaData#getField(int columnIndex)' starts from 1, not 0 + columnIndex++; + + return (Field) ReflectionUtil.invokeMethod(rsmd, "getField", new Class[]{int.class}, new Object[]{columnIndex}); + } catch (NoSuchMethodException | InvocationTargetException e) { + LOGGER.warn("Get field of the column '{}' failed, ignore the exception", columnName, e); + return null; + } + } +} diff --git a/rm-datasource-mysql5/src/main/resources/META-INF/native-image/io.seata/seata-rm-datasource-mysql5/reflect-config.json b/rm-datasource-mysql5/src/main/resources/META-INF/native-image/io.seata/seata-rm-datasource-mysql5/reflect-config.json new file mode 100644 index 00000000000..f4e5ac8bd00 --- /dev/null +++ b/rm-datasource-mysql5/src/main/resources/META-INF/native-image/io.seata/seata-rm-datasource-mysql5/reflect-config.json @@ -0,0 +1,16 @@ +[ + { + "condition": { + "typeReachable": "io.seata.rm.datasource.sql.struct.cache.Mysql5ColumnMetaProcessor" + }, + "name": "com.mysql.jdbc.ResultSetMetaData", + "methods": [ + { + "name": "getField", + "parameterTypes": [ + "int" + ] + } + ] + } +] \ No newline at end of file diff --git a/rm-datasource-mysql5/src/main/resources/META-INF/services/io.seata.rm.datasource.sql.struct.cache.IColumnMetaProcessor b/rm-datasource-mysql5/src/main/resources/META-INF/services/io.seata.rm.datasource.sql.struct.cache.IColumnMetaProcessor new file mode 100644 index 00000000000..b57443811ed --- /dev/null +++ b/rm-datasource-mysql5/src/main/resources/META-INF/services/io.seata.rm.datasource.sql.struct.cache.IColumnMetaProcessor @@ -0,0 +1 @@ +io.seata.rm.datasource.sql.struct.cache.Mysql5ColumnMetaProcessor \ No newline at end of file diff --git a/rm-datasource-mysql8/pom.xml b/rm-datasource-mysql8/pom.xml new file mode 100644 index 00000000000..c83c568908b --- /dev/null +++ b/rm-datasource-mysql8/pom.xml @@ -0,0 +1,45 @@ + + + + + + io.seata + seata-parent + ${revision} + + 4.0.0 + seata-rm-datasource-mysql8 + jar + seata-rm-datasource-mysql8 ${project.version} + datasource resource manager(mysql8) for Seata built with Maven + + + + ${project.groupId} + seata-rm-datasource + ${project.version} + + + mysql + mysql-connector-java + ${mysql8.version} + provided + + + diff --git a/rm-datasource-mysql8/src/main/java/io/seata/rm/datasource/sql/struct/cache/Mysql8ColumnMetaProcessor.java b/rm-datasource-mysql8/src/main/java/io/seata/rm/datasource/sql/struct/cache/Mysql8ColumnMetaProcessor.java new file mode 100644 index 00000000000..5e5e52a6ae7 --- /dev/null +++ b/rm-datasource-mysql8/src/main/java/io/seata/rm/datasource/sql/struct/cache/Mysql8ColumnMetaProcessor.java @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.rm.datasource.sql.struct.cache; + +import com.mysql.cj.MysqlType; +import com.mysql.cj.jdbc.result.ResultSetMetaData; +import com.mysql.cj.result.Field; +import io.seata.common.loader.LoadLevel; +import io.seata.sqlparser.struct.ColumnMeta; + +import java.sql.SQLException; +import java.sql.Types; + +import static io.seata.sqlparser.util.JdbcConstants.MYSQL8; + +/** + * The ColumnMeta processor for mysql8. + * + * @author wang.liang + */ +@LoadLevel(name = MYSQL8, dependsOnClasses = {ResultSetMetaData.class, Field.class}) +public class Mysql8ColumnMetaProcessor implements IColumnMetaProcessor { + + @Override + public void process(ColumnMeta columnMeta, int columnIndex, ColumnMetaProcessorContext context) { + this.readRealDataType(columnMeta, columnIndex, context); + } + + + /** + * Read the realDataType for fix issue#6064 + * + * @param columnMeta the column meta + * @param columnIndex the column index + * @param context the context + * @see issue#6064 + */ + protected void readRealDataType(ColumnMeta columnMeta, int columnIndex, ColumnMetaProcessorContext context) { + if (columnMeta.getRealDataType() != null || columnMeta.getDataType() == Types.TINYINT) { + return; + } + + // unwrap ResultSetMetaData + ResultSetMetaData rsmd; + try { + rsmd = context.getResultSetMetaData().unwrap(ResultSetMetaData.class); + } catch (SQLException ignore) { + return; + } + + // get field + Field field = rsmd.getFields()[columnIndex]; + + if (field.getMysqlTypeId() == MysqlType.FIELD_TYPE_TINY) { + columnMeta.setRealDataType(Types.TINYINT); + } + } +} diff --git a/rm-datasource-mysql8/src/main/resources/META-INF/services/io.seata.rm.datasource.sql.struct.cache.IColumnMetaProcessor b/rm-datasource-mysql8/src/main/resources/META-INF/services/io.seata.rm.datasource.sql.struct.cache.IColumnMetaProcessor new file mode 100644 index 00000000000..9242de0a567 --- /dev/null +++ b/rm-datasource-mysql8/src/main/resources/META-INF/services/io.seata.rm.datasource.sql.struct.cache.IColumnMetaProcessor @@ -0,0 +1 @@ +io.seata.rm.datasource.sql.struct.cache.Mysql8ColumnMetaProcessor \ No newline at end of file diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/TableRecords.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/TableRecords.java index 6dcada792e7..5cda3de1e1f 100755 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/TableRecords.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/TableRecords.java @@ -202,56 +202,15 @@ public static TableRecords buildRecords(TableMeta tmeta, ResultSet resultSet) th for (int i = 1; i <= columnCount; i++) { String colName = resultSetMetaData.getColumnName(i); ColumnMeta col = getColumnMeta(tmeta,colName); - int dataType = col.getDataType(); + int dataType = col.getRealDataType() != null ? col.getRealDataType() : col.getDataType(); + Field field = new Field(); field.setName(col.getColumnName()); if (ignoreCasePKs.contains(colName)) { field.setKeyType(KeyType.PRIMARY_KEY); } field.setType(dataType); - // mysql will not run in this code - // cause mysql does not use java.sql.Blob, java.sql.sql.Clob to process Blob and Clob column - if (dataType == Types.BLOB) { - Blob blob = resultSet.getBlob(i); - if (blob != null) { - field.setValue(new SerialBlob(blob)); - } - } else if (dataType == Types.CLOB) { - Clob clob = resultSet.getClob(i); - if (clob != null) { - field.setValue(new SerialClob(clob)); - } - } else if (dataType == Types.NCLOB) { - NClob object = resultSet.getNClob(i); - if (object != null) { - field.setValue(new SerialClob(object)); - } - } else if (dataType == Types.ARRAY) { - Array array = resultSet.getArray(i); - if (array != null) { - field.setValue(new SerialArray(array)); - } - } else if (dataType == Types.REF) { - Ref ref = resultSet.getRef(i); - if (ref != null) { - field.setValue(new SerialRef(ref)); - } - } else if (dataType == Types.DATALINK) { - java.net.URL url = resultSet.getURL(i); - if (url != null) { - field.setValue(new SerialDatalink(url)); - } - } else if (dataType == Types.JAVA_OBJECT) { - Object object = resultSet.getObject(i); - if (object != null) { - field.setValue(new SerialJavaObject(object)); - } - } else if (dataType == TIMESTAMP_WITH_TIME_ZONE || dataType == TIMESTAMP_WITH_LOCAL_TIME_ZONE) { - field.setValue(convertOffSetTime(timeToOffsetDateTime(resultSet.getBytes(i)))); - } else { - // JDBCType.DISTINCT, JDBCType.STRUCT etc... - field.setValue(holdSerialDataType(resultSet.getObject(i))); - } + loadFieldValue(field, resultSet, i); fields.add(field); } @@ -264,6 +223,59 @@ public static TableRecords buildRecords(TableMeta tmeta, ResultSet resultSet) th return records; } + private static void loadFieldValue(Field field, ResultSet resultSet, int i) throws SQLException { + int dataType = field.getType(); + + // mysql will not run in this code + // cause mysql does not use java.sql.Blob, java.sql.sql.Clob to process Blob and Clob column + if (dataType == Types.BLOB) { + Blob blob = resultSet.getBlob(i); + if (blob != null) { + field.setValue(new SerialBlob(blob)); + } + } else if (dataType == Types.CLOB) { + Clob clob = resultSet.getClob(i); + if (clob != null) { + field.setValue(new SerialClob(clob)); + } + } else if (dataType == Types.NCLOB) { + NClob object = resultSet.getNClob(i); + if (object != null) { + field.setValue(new SerialClob(object)); + } + } else if (dataType == Types.ARRAY) { + Array array = resultSet.getArray(i); + if (array != null) { + field.setValue(new SerialArray(array)); + } + } else if (dataType == Types.REF) { + Ref ref = resultSet.getRef(i); + if (ref != null) { + field.setValue(new SerialRef(ref)); + } + } else if (dataType == Types.DATALINK) { + java.net.URL url = resultSet.getURL(i); + if (url != null) { + field.setValue(new SerialDatalink(url)); + } + } else if (dataType == Types.TINYINT) { + Object object = resultSet.getObject(i); + if (object != null) { + field.setValue(resultSet.getInt(i)); + } + } else if (dataType == Types.JAVA_OBJECT) { + Object object = resultSet.getObject(i); + if (object != null) { + field.setValue(new SerialJavaObject(object)); + } + } else if (dataType == TIMESTAMP_WITH_TIME_ZONE || dataType == TIMESTAMP_WITH_LOCAL_TIME_ZONE) { + field.setValue(convertOffSetTime(timeToOffsetDateTime(resultSet.getBytes(i)))); + } else { + // JDBCType.DISTINCT, JDBCType.STRUCT etc... + field.setValue(holdSerialDataType(resultSet.getObject(i))); + } + } + /** * check if the column is null and return * diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/ColumnMetaProcessorContext.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/ColumnMetaProcessorContext.java new file mode 100644 index 00000000000..e3c124d44f1 --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/ColumnMetaProcessorContext.java @@ -0,0 +1,86 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.rm.datasource.sql.struct.cache; + +import io.seata.sqlparser.struct.TableMeta; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.Statement; + +/** + * The type column meta processor context. + * + * @author wang.liang + */ +public class ColumnMetaProcessorContext { + + private final Connection connection; + private final Statement statement; + + private final ResultSet resultSet; + private final ResultSetMetaData resultSetMetaData; + private final DatabaseMetaData databaseMetaData; + + private final TableMeta tableMeta; + + private final ResultSet rsColumns; + + + public ColumnMetaProcessorContext(Connection connection, Statement statement, ResultSet resultSet, + ResultSetMetaData resultSetMetaData, DatabaseMetaData databaseMetaData, + TableMeta tableMeta, ResultSet rsColumns) { + this.connection = connection; + this.statement = statement; + this.resultSet = resultSet; + this.resultSetMetaData = resultSetMetaData; + this.databaseMetaData = databaseMetaData; + this.tableMeta = tableMeta; + this.rsColumns = rsColumns; + } + + + public Connection getConnection() { + return connection; + } + + public Statement getStatement() { + return statement; + } + + public ResultSet getResultSet() { + return resultSet; + } + + public ResultSetMetaData getResultSetMetaData() { + return resultSetMetaData; + } + + public DatabaseMetaData getDatabaseMetaData() { + return databaseMetaData; + } + + public TableMeta getTableMeta() { + return tableMeta; + } + + public ResultSet getRsColumns() { + return rsColumns; + } + +} diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCache.java index e685d0684af..996df7fadca 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/DmTableMetaCache.java @@ -93,7 +93,7 @@ protected TableNameMeta toTableNameMeta(String tableName, String schemaFromConne protected void processColumns(TableMeta tableMeta, ResultSet rs) throws SQLException { while (rs.next()) { ColumnMeta col = toColumnMeta(rs); - tableMeta.getAllColumns().put(col.getColumnName(), col); + tableMeta.addColumnMeta(col); } } @@ -105,7 +105,7 @@ protected void processIndexes(TableMeta tableMeta, ResultSet rs) throws SQLExcep } String colName = rs.getString("COLUMN_NAME"); - ColumnMeta col = tableMeta.getAllColumns().get(colName); + ColumnMeta col = tableMeta.getColumnMeta(colName); if (tableMeta.getAllIndexes().containsKey(indexName)) { IndexMeta index = tableMeta.getAllIndexes().get(indexName); index.getValues().add(col); diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/IColumnMetaProcessor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/IColumnMetaProcessor.java new file mode 100644 index 00000000000..8132e22f0a4 --- /dev/null +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/IColumnMetaProcessor.java @@ -0,0 +1,29 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.rm.datasource.sql.struct.cache; + +import io.seata.sqlparser.struct.ColumnMeta; + +/** + * The ColumnMeta processor. + * + * @author wang.liang + */ +public interface IColumnMetaProcessor { + + void process(ColumnMeta columnMeta, int columnIndex, ColumnMetaProcessorContext context); + +} diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MariadbTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MariadbTableMetaCache.java index b8874ddc8f0..b4da885c891 100755 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MariadbTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MariadbTableMetaCache.java @@ -38,7 +38,7 @@ protected TableMeta fetchSchema(Connection connection, String tableName) throws String sql = "SELECT * FROM " + ColumnUtils.addEscape(tableName, JdbcConstants.MARIADB) + " LIMIT 1"; try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { - return resultSetMetaToSchema(rs.getMetaData(), connection.getMetaData()); + return resultSetMetaToSchema(connection, stmt, rs, rs.getMetaData(), connection.getMetaData()); } catch (SQLException sqlEx) { throw sqlEx; } catch (Exception e) { diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCache.java index c1f0c4ff510..6547b342d46 100755 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCache.java @@ -21,15 +21,17 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; +import java.util.List; import io.seata.common.exception.NotSupportYetException; import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.loader.LoadLevel; -import io.seata.sqlparser.util.ColumnUtils; import io.seata.sqlparser.struct.ColumnMeta; import io.seata.sqlparser.struct.IndexMeta; import io.seata.sqlparser.struct.IndexType; import io.seata.sqlparser.struct.TableMeta; +import io.seata.sqlparser.util.ColumnUtils; import io.seata.sqlparser.util.JdbcConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +46,9 @@ public class MysqlTableMetaCache extends AbstractTableMetaCache { protected final Logger logger = LoggerFactory.getLogger(getClass()); + private static final List COLUMN_META_PROCESSORS = EnhancedServiceLoader.loadAll(IColumnMetaProcessor.class); + + @Override protected String getCacheKey(Connection connection, String tableName, String resourceId) { StringBuilder cacheKey = new StringBuilder(resourceId); @@ -52,7 +57,7 @@ protected String getCacheKey(Connection connection, String tableName, String res String[] tableNameWithCatalog = tableName.replace("`", "").split("\\."); String defaultTableName = tableNameWithCatalog.length > 1 ? tableNameWithCatalog[1] : tableNameWithCatalog[0]; - DatabaseMetaData databaseMetaData = null; + DatabaseMetaData databaseMetaData; try { databaseMetaData = connection.getMetaData(); } catch (SQLException e) { @@ -80,7 +85,7 @@ protected TableMeta fetchSchema(Connection connection, String tableName) throws String sql = "SELECT * FROM " + ColumnUtils.addEscape(tableName, JdbcConstants.MYSQL) + " LIMIT 1"; try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { - return resultSetMetaToSchema(rs.getMetaData(), connection.getMetaData()); + return resultSetMetaToSchema(connection, stmt, rs, rs.getMetaData(), connection.getMetaData()); } catch (SQLException sqlEx) { throw sqlEx; } catch (Exception e) { @@ -88,7 +93,7 @@ protected TableMeta fetchSchema(Connection connection, String tableName) throws } } - protected TableMeta resultSetMetaToSchema(ResultSetMetaData rsmd, DatabaseMetaData dbmd) + protected TableMeta resultSetMetaToSchema(Connection connection, Statement stmt, ResultSet rs, ResultSetMetaData rsmd, DatabaseMetaData dbmd) throws SQLException { //always "" for mysql String schemaName = rsmd.getSchemaName(1); @@ -120,6 +125,12 @@ protected TableMeta resultSetMetaToSchema(ResultSetMetaData rsmd, DatabaseMetaDa try (ResultSet rsColumns = dbmd.getColumns(catalogName, schemaName, tableName, "%"); ResultSet rsIndex = dbmd.getIndexInfo(catalogName, schemaName, tableName, false, true); ResultSet onUpdateColumns = dbmd.getVersionColumns(catalogName, schemaName, tableName)) { + + // Build context + ColumnMetaProcessorContext context = new ColumnMetaProcessorContext(connection, stmt, rs, + rsmd, dbmd, tm, rsColumns); + + int columnIndex = 0; while (rsColumns.next()) { ColumnMeta col = new ColumnMeta(); col.setTableCat(rsColumns.getString("TABLE_CAT")); @@ -145,17 +156,24 @@ protected TableMeta resultSetMetaToSchema(ResultSetMetaData rsmd, DatabaseMetaDa if (tm.getAllColumns().containsKey(col.getColumnName())) { throw new NotSupportYetException("Not support the table has the same column name with different case yet"); } - tm.getAllColumns().put(col.getColumnName(), col); + + for (IColumnMetaProcessor processor : COLUMN_META_PROCESSORS) { + processor.process(col, columnIndex, context); + } + + tm.addColumnMeta(col); + + columnIndex++; } while (onUpdateColumns.next()) { - tm.getAllColumns().get(onUpdateColumns.getString("COLUMN_NAME")).setOnUpdate(true); + tm.getColumnMeta(onUpdateColumns.getString("COLUMN_NAME")).setOnUpdate(true); } while (rsIndex.next()) { String indexName = rsIndex.getString("INDEX_NAME"); String colName = rsIndex.getString("COLUMN_NAME"); - ColumnMeta col = tm.getAllColumns().get(colName); + ColumnMeta col = tm.getColumnMeta(colName); if (tm.getAllIndexes().containsKey(indexName)) { IndexMeta index = tm.getAllIndexes().get(indexName); diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java index 3477add9a6a..8105697d002 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/OracleTableMetaCache.java @@ -120,7 +120,7 @@ protected TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableNam if (tm.getAllColumns().containsKey(col.getColumnName())) { throw new NotSupportYetException("Not support the table has the same column name with different case yet"); } - tm.getAllColumns().put(col.getColumnName(), col); + tm.addColumnMeta(col); } while (rsIndex.next()) { @@ -129,7 +129,7 @@ protected TableMeta resultSetMetaToSchema(DatabaseMetaData dbmd, String tableNam continue; } String colName = rsIndex.getString("COLUMN_NAME"); - ColumnMeta col = tm.getAllColumns().get(colName); + ColumnMeta col = tm.getColumnMeta(colName); if (tm.getAllIndexes().containsKey(indexName)) { IndexMeta index = tm.getAllIndexes().get(indexName); index.getValues().add(col); diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PolarDBXTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PolarDBXTableMetaCache.java index 7e73ff81731..1c9a4a771c1 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PolarDBXTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PolarDBXTableMetaCache.java @@ -37,7 +37,7 @@ protected TableMeta fetchSchema(Connection connection, String tableName) throws String sql = "SELECT * FROM " + ColumnUtils.addEscape(tableName, JdbcConstants.POLARDBX) + " LIMIT 1"; try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { - return resultSetMetaToSchema(rs.getMetaData(), connection.getMetaData()); + return resultSetMetaToSchema(connection, stmt, rs, rs.getMetaData(), connection.getMetaData()); } catch (SQLException sqlEx) { throw sqlEx; } catch (Exception e) { diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java index 0a8a14ce0ed..863cafcacb6 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/struct/cache/PostgresqlTableMetaCache.java @@ -138,7 +138,7 @@ private TableMeta resultSetMetaToSchema(Connection connection, String tableName) if (tm.getAllColumns().containsKey(col.getColumnName())) { throw new NotSupportYetException("Not support the table has the same column name with different case yet"); } - tm.getAllColumns().put(col.getColumnName(), col); + tm.addColumnMeta(col); } while (rsIndex.next()) { @@ -147,7 +147,7 @@ private TableMeta resultSetMetaToSchema(Connection connection, String tableName) continue; } String colName = rsIndex.getString("column_name"); - ColumnMeta col = tm.getAllColumns().get(colName); + ColumnMeta col = tm.getColumnMeta(colName); if (tm.getAllIndexes().containsKey(indexName)) { IndexMeta index = tm.getAllIndexes().get(indexName); index.getValues().add(col); diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/AbstractUndoExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/AbstractUndoExecutor.java index 7d64239a6ae..1defa4c757a 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/AbstractUndoExecutor.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/AbstractUndoExecutor.java @@ -28,11 +28,11 @@ import java.util.List; import com.alibaba.fastjson.JSON; +import io.seata.common.ConfigurationKeys; import io.seata.common.util.BlobUtils; import io.seata.common.util.IOUtil; import io.seata.common.util.StringUtils; import io.seata.config.ConfigurationFactory; -import io.seata.core.constants.ConfigurationKeys; import io.seata.core.model.Result; import io.seata.rm.datasource.ConnectionProxy; import io.seata.sqlparser.util.ColumnUtils; @@ -296,7 +296,7 @@ protected TableRecords queryCurrentRecords(ConnectionProxy connectionProxy) thro // pares pk values Map> pkRowValues = parsePkValues(getUndoRows()); - if (pkRowValues.size() == 0) { + if (pkRowValues.isEmpty()) { return TableRecords.empty(tableMeta); } // build check sql @@ -313,8 +313,8 @@ protected TableRecords queryCurrentRecords(ConnectionProxy connectionProxy) thro int paramIndex = 1; int rowSize = pkRowValues.get(pkNameList.get(0)).size(); for (int r = 0; r < rowSize; r++) { - for (int c = 0; c < pkNameList.size(); c++) { - List pkColumnValueList = pkRowValues.get(pkNameList.get(c)); + for (String pkName : pkNameList) { + List pkColumnValueList = pkRowValues.get(pkName); Field field = pkColumnValueList.get(r); int dataType = tableMeta.getColumnMeta(field.getName()).getDataType(); statement.setObject(paramIndex, field.getValue(), dataType); @@ -368,8 +368,8 @@ protected Map> parsePkValues(TableRecords records) { */ protected Map> parsePkValues(List rows, List pkNameList) { List pkFieldList = new ArrayList<>(); - for (int i = 0; i < rows.size(); i++) { - List fields = rows.get(i).getFields(); + for (Row row : rows) { + List fields = row.getFields(); if (fields != null) { for (Field field : fields) { if (pkNameList.stream().anyMatch(e -> field.getName().equalsIgnoreCase(e))) { diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/TableMetaTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/TableMetaTest.java index 8e337b10cc9..dcc1c6ed17a 100644 --- a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/TableMetaTest.java +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/TableMetaTest.java @@ -44,7 +44,7 @@ public void testTableMeta() { Assertions.assertNotEquals(tableMeta, other); other = new TableMeta(); - other.getAllColumns().put("columnName", new ColumnMeta()); + other.addColumnMeta(new ColumnMeta("columnName")); Assertions.assertNotEquals(tableMeta, other); other = new TableMeta(); @@ -55,8 +55,8 @@ public void testTableMeta() { @Test public void testGetColumnMeta() { TableMeta tableMeta = new TableMeta(); - tableMeta.getAllColumns().put("id", new ColumnMeta()); - tableMeta.getAllColumns().put("name", new ColumnMeta()); + tableMeta.addColumnMeta(new ColumnMeta("id")); + tableMeta.addColumnMeta(new ColumnMeta("name")); Assertions.assertNull(tableMeta.getColumnMeta("`id`")); Assertions.assertNotNull(tableMeta.getColumnMeta("name")); } @@ -64,13 +64,13 @@ public void testGetColumnMeta() { @Test public void testGetAutoIncreaseColumn() { TableMeta tableMeta = new TableMeta(); - ColumnMeta id = new ColumnMeta(); + ColumnMeta id = new ColumnMeta("id"); id.setIsAutoincrement("YES"); - tableMeta.getAllColumns().put("id", id); + tableMeta.addColumnMeta(id); Assertions.assertNotNull(tableMeta.getAutoIncreaseColumn()); tableMeta = new TableMeta(); - tableMeta.getAllColumns().put("name", new ColumnMeta()); + tableMeta.addColumnMeta(new ColumnMeta("name")); Assertions.assertNull(tableMeta.getAutoIncreaseColumn()); } diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/MariadbTableMetaCacheTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/MariadbTableMetaCacheTest.java index a13c9991c8c..84912832857 100644 --- a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/MariadbTableMetaCacheTest.java +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/MariadbTableMetaCacheTest.java @@ -98,10 +98,10 @@ public void getTableMetaTest_0() throws SQLException { Assertions.assertEquals(columnMetas.length, tableMeta.getAllColumns().size()); - assertColumnMetaEquals(columnMetas[0], tableMeta.getAllColumns().get("id")); - assertColumnMetaEquals(columnMetas[1], tableMeta.getAllColumns().get("name1")); - assertColumnMetaEquals(columnMetas[2], tableMeta.getAllColumns().get("name2")); - assertColumnMetaEquals(columnMetas[3], tableMeta.getAllColumns().get("name3")); + assertColumnMetaEquals(columnMetas[0], tableMeta.getColumnMeta("id")); + assertColumnMetaEquals(columnMetas[1], tableMeta.getColumnMeta("name1")); + assertColumnMetaEquals(columnMetas[2], tableMeta.getColumnMeta("name2")); + assertColumnMetaEquals(columnMetas[3], tableMeta.getColumnMeta("name3")); Assertions.assertEquals(indexMetas.length, tableMeta.getAllIndexes().size()); diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCacheTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCacheTest.java index 56928aa6d70..46fac034ab3 100644 --- a/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCacheTest.java +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/sql/struct/cache/MysqlTableMetaCacheTest.java @@ -98,10 +98,10 @@ public void getTableMetaTest_0() throws SQLException { Assertions.assertEquals(columnMetas.length, tableMeta.getAllColumns().size()); - assertColumnMetaEquals(columnMetas[0], tableMeta.getAllColumns().get("id")); - assertColumnMetaEquals(columnMetas[1], tableMeta.getAllColumns().get("name1")); - assertColumnMetaEquals(columnMetas[2], tableMeta.getAllColumns().get("name2")); - assertColumnMetaEquals(columnMetas[3], tableMeta.getAllColumns().get("name3")); + assertColumnMetaEquals(columnMetas[0], tableMeta.getColumnMeta("id")); + assertColumnMetaEquals(columnMetas[1], tableMeta.getColumnMeta("name1")); + assertColumnMetaEquals(columnMetas[2], tableMeta.getColumnMeta("name2")); + assertColumnMetaEquals(columnMetas[3], tableMeta.getColumnMeta("name3")); Assertions.assertEquals(indexMetas.length, tableMeta.getAllIndexes().size()); diff --git a/seata-plugin/seata-jackson-parser-oracle/src/main/java/io/seata/plugin/jackson/parser/oracle/OracleTimestampJacksonSerializer.java b/seata-plugin/seata-jackson-parser-oracle/src/main/java/io/seata/plugin/jackson/parser/oracle/OracleTimestampJacksonSerializer.java index de65116d969..35da8cd0bc0 100644 --- a/seata-plugin/seata-jackson-parser-oracle/src/main/java/io/seata/plugin/jackson/parser/oracle/OracleTimestampJacksonSerializer.java +++ b/seata-plugin/seata-jackson-parser-oracle/src/main/java/io/seata/plugin/jackson/parser/oracle/OracleTimestampJacksonSerializer.java @@ -35,7 +35,7 @@ /** * @author jsbxyyx */ -@LoadLevel(name = "oracleTimestamp") +@LoadLevel(name = "oracleTimestamp", dependsOnClasses = oracle.sql.TIMESTAMP.class) public class OracleTimestampJacksonSerializer implements JacksonSerializer { private static final Logger LOGGER = LoggerFactory.getLogger(OracleTimestampJacksonSerializer.class); diff --git a/server/pom.xml b/server/pom.xml index 2b24b55ab62..41cced2115e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -285,12 +285,12 @@ mysql mysql-connector-java - ${mysql.jdbc.version} + ${mysql5.version} mysql mysql-connector-java - ${mysql8.jdbc.version} + ${mysql8.version} @@ -495,8 +495,6 @@ release-seata - 5.1.42 - 8.0.27 false diff --git a/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/struct/ColumnMeta.java b/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/struct/ColumnMeta.java index 6cff6eeaaee..8298ffec49c 100644 --- a/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/struct/ColumnMeta.java +++ b/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/struct/ColumnMeta.java @@ -27,6 +27,7 @@ public class ColumnMeta { private String tableSchemaName; private String tableName; private String columnName; + private Integer realDataType; private int dataType; private String dataTypeName; private int columnSize; @@ -51,6 +52,11 @@ public class ColumnMeta { public ColumnMeta() { } + public ColumnMeta(String columnName) { + this.columnName = columnName; + } + + @Override public String toString() { final StringBuilder sb = new StringBuilder("ColumnMeta{"); @@ -58,6 +64,7 @@ public String toString() { sb.append(", tableSchemaName='").append(tableSchemaName).append('\''); sb.append(", tableName='").append(tableName).append('\''); sb.append(", columnName='").append(columnName).append('\''); + sb.append(", realDataType=").append(realDataType); sb.append(", dataType=").append(dataType); sb.append(", dataTypeName='").append(dataTypeName).append('\''); sb.append(", columnSize=").append(columnSize); @@ -159,6 +166,24 @@ public void setColumnName(String columnName) { this.columnName = columnName; } + /** + * Gets real data type. + * + * @return the real data type + */ + public Integer getRealDataType() { + return realDataType; + } + + /** + * Sets real data type. + * + * @param realDataType the real data type + */ + public void setRealDataType(Integer realDataType) { + this.realDataType = realDataType; + } + /** * Gets data type. * @@ -448,6 +473,9 @@ public boolean equals(Object o) { if (!Objects.equals(columnMeta.columnName, this.columnName)) { return false; } + if (!Objects.equals(columnMeta.realDataType, this.realDataType)) { + return false; + } if (!Objects.equals(columnMeta.dataType, this.dataType)) { return false; } @@ -505,6 +533,7 @@ public int hashCode() { hash += Objects.hashCode(tableSchemaName); hash += Objects.hashCode(tableName); hash += Objects.hashCode(columnName); + hash += Objects.hashCode(realDataType); hash += Objects.hashCode(dataType); hash += Objects.hashCode(dataTypeName); hash += Objects.hashCode(columnSize); diff --git a/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/struct/TableMeta.java b/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/struct/TableMeta.java index 67a6b5f04be..3176f96b92a 100644 --- a/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/struct/TableMeta.java +++ b/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/struct/TableMeta.java @@ -29,6 +29,7 @@ import io.seata.common.exception.NotSupportYetException; import io.seata.common.util.CollectionUtils; import io.seata.common.util.LowerCaseLinkHashMap; +import io.seata.common.util.StringUtils; import io.seata.sqlparser.util.ColumnUtils; /** @@ -79,6 +80,18 @@ public ColumnMeta getColumnMeta(String colName) { return allColumns.get(colName); } + /** + * Add column meta. + * + * @param columnMeta the column meta + */ + public void addColumnMeta(ColumnMeta columnMeta) { + if (StringUtils.isBlank(columnMeta.getColumnName())) { + throw new IllegalArgumentException("The column name must be not null or blank"); + } + allColumns.put(columnMeta.getColumnName(), columnMeta); + } + /** * Gets all columns. * diff --git a/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/util/JdbcConstants.java b/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/util/JdbcConstants.java index bfbf60dc1e2..fb7d8b54701 100644 --- a/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/util/JdbcConstants.java +++ b/sqlparser/seata-sqlparser-core/src/main/java/io/seata/sqlparser/util/JdbcConstants.java @@ -23,6 +23,8 @@ public interface JdbcConstants { String ORACLE = "oracle"; String MYSQL = "mysql"; + String MYSQL5 = "mysql5"; + String MYSQL8 = "mysql8"; String DB2 = "db2";