|
|
@@ -106,6 +106,15 @@ class LsyEntityMappingTest {
|
|
|
|| java.lang.reflect.Modifier.isStatic(f.getModifiers())) {
|
|
|
continue;
|
|
|
}
|
|
|
+ // ★ 标了 `@TableField(exist = false)` 的字段**不进 MP 的列清单**
|
|
|
+ // (`LsyOrderEntity.refunding` 是第一个:它是派生量,不是库里的列),
|
|
|
+ // 于是本条测试的前提「MP 按实体生成列清单 ⇒ 缺列会 42703」在它身上不成立。
|
|
|
+ // 放行它**不是**放宽防线:那个前提由
|
|
|
+ // nonPersistentFieldsAreExcludedFromMybatisPlusColumnList 逐字段钉住 ——
|
|
|
+ // 注解除掉、或 MP 换了行为,那条当场变红。
|
|
|
+ if (isNonPersistent(f)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
mapped.add(camelToSnake(f.getName()));
|
|
|
}
|
|
|
}
|
|
|
@@ -130,6 +139,78 @@ class LsyEntityMappingTest {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 钉住上一条测试**放行** `@TableField(exist = false)` 字段所依赖的那个前提。
|
|
|
+ *
|
|
|
+ * <p>前提不是「注解写了就没事」,而是「**MyBatis-Plus 真的会把这类字段从列清单里剔掉**」。
|
|
|
+ * 本用例不引用注解作证据,直接问 MP 要它的 {@code TableInfo}(不碰库、不起 Spring),
|
|
|
+ * 逐字段核对 —— 于是「注解被谁删掉」「升级换了行为」都会在这里当场变红,
|
|
|
+ * 而不会退化成「上一条测试悄悄放行了一个真列」。
|
|
|
+ *
|
|
|
+ * <p>★ 两向都在:非持久字段**不在**列清单里(否定读数),
|
|
|
+ * 同一实体里至少有一个真列**在**清单里(正对照 —— 否则一个空清单能让前半条也通过)。
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ void nonPersistentFieldsAreExcludedFromMybatisPlusColumnList() {
|
|
|
+ int checked = 0;
|
|
|
+ for (Class<?> entity : new Class<?>[]{LsyMerchantEntity.class, LsyOrderEntity.class,
|
|
|
+ LsyRefundEntity.class, LsyNotifyLogEntity.class}) {
|
|
|
+ Set<String> mpProperties = new LinkedHashSet<>();
|
|
|
+ for (var f : tableInfo(entity).getFieldList()) {
|
|
|
+ mpProperties.add(f.getProperty());
|
|
|
+ }
|
|
|
+ assertFalse(mpProperties.isEmpty(),
|
|
|
+ entity.getSimpleName() + " 的 MP 列清单是空的 —— 量具没在读数,本用例没有意义");
|
|
|
+
|
|
|
+ Set<String> persistentDeclared = new LinkedHashSet<>();
|
|
|
+ Set<String> nonPersistentDeclared = new LinkedHashSet<>();
|
|
|
+ for (Class<?> c = entity; c != null && c != Object.class; c = c.getSuperclass()) {
|
|
|
+ for (var f : c.getDeclaredFields()) {
|
|
|
+ if (f.isSynthetic() || java.lang.reflect.Modifier.isStatic(f.getModifiers())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ (isNonPersistent(f) ? nonPersistentDeclared : persistentDeclared).add(f.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (String name : nonPersistentDeclared) {
|
|
|
+ checked++;
|
|
|
+ assertFalse(mpProperties.contains(name),
|
|
|
+ entity.getSimpleName() + "." + name + " 标了 `@TableField(exist = false)`,"
|
|
|
+ + "却仍出现在 MP 的列清单里 —— 那么它会进 SQL,缺列就会抛 42703。"
|
|
|
+ + "此时上一条测试放行它是错的。");
|
|
|
+ }
|
|
|
+ if (!nonPersistentDeclared.isEmpty()) {
|
|
|
+ assertTrue(persistentDeclared.stream().anyMatch(mpProperties::contains),
|
|
|
+ entity.getSimpleName() + " 里连一个真列都不在 MP 列清单里 ⇒ 上面那条否定读数"
|
|
|
+ + "是「清单没读到」而不是「字段被剔掉」");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ assertTrue(checked > 0,
|
|
|
+ "四张实体里一个 `@TableField(exist = false)` 字段都没有 ⇒ 本用例在空转;"
|
|
|
+ + "若确实一个都没有了,请连同上一条测试里的放行分支一起删掉");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手工让 MP 建出 {@code TableInfo}:不碰库、不起 Spring,只喂一个空的
|
|
|
+ * {@code MapperBuilderAssistant}。取的是 MP 自己算出来的列清单,
|
|
|
+ * 而不是我们按字段名推的(两者若不同,正是本条要发现的事)。
|
|
|
+ */
|
|
|
+ private static com.baomidou.mybatisplus.core.metadata.TableInfo tableInfo(Class<?> entity) {
|
|
|
+ org.apache.ibatis.builder.MapperBuilderAssistant assistant =
|
|
|
+ new org.apache.ibatis.builder.MapperBuilderAssistant(
|
|
|
+ new com.baomidou.mybatisplus.core.MybatisConfiguration(), "");
|
|
|
+ var info = com.baomidou.mybatisplus.core.metadata.TableInfoHelper.initTableInfo(assistant, entity);
|
|
|
+ assertNotNull(info, entity.getSimpleName() + " 的 MP TableInfo 建不出来");
|
|
|
+ return info;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 字段是否被显式标成「不是库里的列」。 */
|
|
|
+ private static boolean isNonPersistent(java.lang.reflect.Field f) {
|
|
|
+ var ann = f.getAnnotation(com.baomidou.mybatisplus.annotation.TableField.class);
|
|
|
+ return ann != null && !ann.exist();
|
|
|
+ }
|
|
|
+
|
|
|
/** 抽出一张表的建表语句(到第一个分号为止)。 */
|
|
|
private static String tableDdl(String sql, String table) {
|
|
|
int at = sql.indexOf("CREATE TABLE IF NOT EXISTS " + table + " (");
|