修复MyBatis字段映射冲突和数据库连接问题

问题修复:
- 修复Engineer实体类字段映射冲突:统一使用小写下划线字段名
- 解决"字段user_id不存在"的MyBatis映射错误
- 修改所有@TableField注解从大写改为小写格式
- 表名从ENGINEER改为engineer以匹配数据库约定

功能增强:
- 添加应用启动时数据库连接信息打印功能
- 增强数据库连接诊断和ENGINEER表访问测试
- 新增DatabaseTestController用于数据库连接和表结构测试

技术改进:
- 解决mapUnderscoreToCamelCase配置与@TableField注解冲突
- 优化错误日志记录和诊断信息
- 增强应用启动时的数据源验证功能

修改文件:
- Engineer.java: 字段映射注解全部改为小写下划线
- DevOpsApplication.java: 新增数据库连接信息打印
- DatabaseTestController.java: 新增数据库测试接口
- 配置文件优化和错误处理增强
This commit is contained in:
75681 2025-08-12 19:26:43 +08:00
parent a12d628b8f
commit 26445b86f7
6 changed files with 372 additions and 17 deletions

4
.gitignore vendored
View File

@ -10,4 +10,6 @@
/*.bat
/setup_database.sql
/PowerShell启动说明.md
/DEPLOYMENT.md
/DEPLOYMENT.md
/ERROR_CAPTURE_GUIDE.md
/*.sql

View File

@ -4,7 +4,11 @@ import com.chinaweal.youfool.devops.config.ErrorLogProperties;
import com.chinaweal.youfool.devops.util.ErrorLogUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import javax.sql.DataSource;
import java.sql.Connection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@ -28,6 +32,14 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
@Autowired
private ErrorLogProperties errorLogProperties;
@Autowired
@Qualifier("devopsDS")
private DataSource devopsDataSource;
@Autowired
@Qualifier("youfoolDS")
private DataSource youfoolDataSource;
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder applicationBuilder) {
@ -55,6 +67,10 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
errorLogProperties.isDatabaseEnabled(),
errorLogProperties.isBusinessEnabled());
}
// 打印数据库连接信息
printDatabaseConnectionInfo();
log.info("====================================================================");
// 记录启动成功信息
@ -172,4 +188,75 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
}
return false; // 默认认为是Java 8
}
/**
* 打印数据库连接信息
*/
private void printDatabaseConnectionInfo() {
try {
log.info("========== 数据库连接信息 ==========");
// 打印devops数据源信息
try (Connection conn = devopsDataSource.getConnection()) {
String url = conn.getMetaData().getURL();
String username = conn.getMetaData().getUserName();
String databaseName = conn.getCatalog();
log.info("🔗 Devops数据源:");
log.info(" URL: {}", url);
log.info(" 用户: {}", username);
log.info(" 数据库: {}", databaseName);
ErrorLogUtils.logStartupInfo("Devops数据源连接: " + url + ", 用户: " + username + ", 数据库: " + databaseName);
} catch (Exception e) {
log.error("❌ 获取devops数据源信息失败", e);
ErrorLogUtils.saveStartupError("获取devops数据源信息失败", e);
}
// 打印youfool数据源信息
try (Connection conn = youfoolDataSource.getConnection()) {
String url = conn.getMetaData().getURL();
String username = conn.getMetaData().getUserName();
String databaseName = conn.getCatalog();
log.info("🔗 Youfool数据源:");
log.info(" URL: {}", url);
log.info(" 用户: {}", username);
log.info(" 数据库: {}", databaseName);
ErrorLogUtils.logStartupInfo("Youfool数据源连接: " + url + ", 用户: " + username + ", 数据库: " + databaseName);
} catch (Exception e) {
log.error("❌ 获取youfool数据源信息失败", e);
ErrorLogUtils.saveStartupError("获取youfool数据源信息失败", e);
}
// 测试ENGINEER表访问
try (Connection conn = devopsDataSource.getConnection()) {
// 简单测试查询
String testSql = "SELECT COUNT(*) as total FROM engineer";
try (java.sql.PreparedStatement ps = conn.prepareStatement(testSql);
java.sql.ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
int total = rs.getInt("total");
log.info("✅ ENGINEER表测试: 共{}条记录", total);
ErrorLogUtils.logStartupInfo("ENGINEER表访问测试成功共" + total + "条记录");
}
} catch (Exception e) {
log.error("❌ ENGINEER表访问测试失败", e);
ErrorLogUtils.saveStartupError("ENGINEER表访问测试失败", e);
}
} catch (Exception e) {
log.error("❌ 数据库连接测试失败", e);
ErrorLogUtils.saveStartupError("数据库连接测试失败", e);
}
log.info("=====================================");
} catch (Exception e) {
log.error("❌ 打印数据库连接信息时发生异常", e);
ErrorLogUtils.saveStartupError("打印数据库连接信息异常", e);
}
}
}

View File

@ -0,0 +1,238 @@
package com.chinaweal.youfool.devops.base.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 数据库测试控制器
* 用于测试ENGINEER表的SQL查询问题
*/
@RestController
@RequestMapping("/api/test")
@Api(tags = "数据库测试接口")
@Slf4j
public class DatabaseTestController {
@Autowired
@Qualifier("devopsDS")
private DataSource devopsDataSource;
@Autowired
@Qualifier("youfoolDS")
private DataSource youfoolDataSource;
/**
* 测试数据库连接信息
*/
@GetMapping("/connection")
@ApiOperation("测试数据库连接信息")
public Map<String, Object> testConnection() {
Map<String, Object> result = new HashMap<>();
try {
// 测试devops数据源
try (Connection conn = devopsDataSource.getConnection()) {
result.put("devops_url", conn.getMetaData().getURL());
result.put("devops_username", conn.getMetaData().getUserName());
result.put("devops_database", conn.getCatalog());
}
// 测试youfool数据源
try (Connection conn = youfoolDataSource.getConnection()) {
result.put("youfool_url", conn.getMetaData().getURL());
result.put("youfool_username", conn.getMetaData().getUserName());
result.put("youfool_database", conn.getCatalog());
}
result.put("success", true);
result.put("message", "数据库连接测试成功");
} catch (Exception e) {
log.error("数据库连接测试失败", e);
result.put("success", false);
result.put("message", "连接测试失败: " + e.getMessage());
}
return result;
}
/**
* 测试ENGINEER表结构
*/
@GetMapping("/engineer-structure")
@ApiOperation("测试ENGINEER表结构")
public Map<String, Object> testEngineerStructure() {
Map<String, Object> result = new HashMap<>();
try {
// 使用devops数据源测试
try (Connection conn = devopsDataSource.getConnection()) {
// 检查表是否存在
String checkTableSql = "SELECT COUNT(*) as table_count FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'engineer'";
try (PreparedStatement ps = conn.prepareStatement(checkTableSql);
ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
result.put("table_exists", rs.getInt("table_count") > 0);
}
}
// 获取字段信息
String fieldsSql = "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'engineer' ORDER BY ordinal_position";
List<Map<String, Object>> fields = new ArrayList<>();
try (PreparedStatement ps = conn.prepareStatement(fieldsSql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
Map<String, Object> field = new HashMap<>();
field.put("name", rs.getString("column_name"));
field.put("type", rs.getString("data_type"));
field.put("nullable", rs.getString("is_nullable"));
fields.add(field);
}
}
result.put("fields", fields);
result.put("datasource", "devops");
result.put("success", true);
}
} catch (Exception e) {
log.error("ENGINEER表结构测试失败", e);
result.put("success", false);
result.put("message", "测试失败: " + e.getMessage());
result.put("error", e.getClass().getSimpleName());
}
return result;
}
/**
* 测试问题SQL - 分步执行
*/
@GetMapping("/engineer-query")
@ApiOperation("测试ENGINEER表查询")
public Map<String, Object> testEngineerQuery() {
Map<String, Object> result = new HashMap<>();
try {
// 使用devops数据源测试
try (Connection conn = devopsDataSource.getConnection()) {
result.put("connection_url", conn.getMetaData().getURL());
result.put("connection_user", conn.getMetaData().getUserName());
// 测试1: 简单查询
String simpleSql = "SELECT COUNT(*) as total FROM ENGINEER";
try (PreparedStatement ps = conn.prepareStatement(simpleSql);
ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
result.put("total_records", rs.getInt("total"));
}
result.put("simple_query", "✓ 成功");
} catch (Exception e) {
result.put("simple_query", "✗ 失败: " + e.getMessage());
}
// 测试2: 测试USER_ID字段
String userIdSql = "SELECT USER_ID FROM ENGINEER LIMIT 1";
try (PreparedStatement ps = conn.prepareStatement(userIdSql);
ResultSet rs = ps.executeQuery()) {
result.put("user_id_query", "✓ 成功");
if (rs.next()) {
result.put("sample_user_id", rs.getString("USER_ID"));
}
} catch (Exception e) {
result.put("user_id_query", "✗ 失败: " + e.getMessage());
}
// 测试3: 测试完整的问题SQL
String fullSql = "SELECT USER_ID, USERNAME, PASSWORD, NICKNAME, SEX, PHONE, EMAIL, STATUS, " +
"DESCRIPTION, SORT, ROLES, SOURCE, IS_DELETED, create_by, " +
"create_time, update_by, update_time FROM ENGINEER WHERE IS_DELETED='0' LIMIT 1";
try (PreparedStatement ps = conn.prepareStatement(fullSql);
ResultSet rs = ps.executeQuery()) {
result.put("full_query", "✓ 成功");
if (rs.next()) {
Map<String, Object> sampleData = new HashMap<>();
sampleData.put("USER_ID", rs.getString("USER_ID"));
sampleData.put("USERNAME", rs.getString("USERNAME"));
sampleData.put("IS_DELETED", rs.getString("IS_DELETED"));
result.put("sample_data", sampleData);
}
} catch (Exception e) {
result.put("full_query", "✗ 失败: " + e.getMessage());
result.put("full_query_error", e.getClass().getSimpleName());
}
// 测试4: 测试带参数的查询(模拟实际应用场景)
String paramSql = "SELECT USER_ID, USERNAME FROM ENGINEER WHERE IS_DELETED='0' AND USERNAME = ?";
try (PreparedStatement ps = conn.prepareStatement(paramSql)) {
ps.setString(1, "test");
try (ResultSet rs = ps.executeQuery()) {
result.put("param_query", "✓ 成功");
}
} catch (Exception e) {
result.put("param_query", "✗ 失败: " + e.getMessage());
result.put("param_query_error", e.getClass().getSimpleName());
}
result.put("success", true);
}
} catch (Exception e) {
log.error("ENGINEER查询测试失败", e);
result.put("success", false);
result.put("message", "查询测试失败: " + e.getMessage());
result.put("error_class", e.getClass().getSimpleName());
result.put("error_cause", e.getCause() != null ? e.getCause().getMessage() : null);
}
return result;
}
/**
* 测试MyBatis查询使用相同的数据源配置
*/
@GetMapping("/mybatis-test")
@ApiOperation("测试MyBatis数据源配置")
public Map<String, Object> testMyBatisDataSource() {
Map<String, Object> result = new HashMap<>();
try {
// 检查数据源配置
result.put("devops_datasource_class", devopsDataSource.getClass().getSimpleName());
result.put("youfool_datasource_class", youfoolDataSource.getClass().getSimpleName());
// 测试两个数据源是否指向同一个数据库
try (Connection devopsConn = devopsDataSource.getConnection();
Connection youfoolConn = youfoolDataSource.getConnection()) {
result.put("devops_url", devopsConn.getMetaData().getURL());
result.put("youfool_url", youfoolConn.getMetaData().getURL());
result.put("same_database", devopsConn.getMetaData().getURL().equals(youfoolConn.getMetaData().getURL()));
}
result.put("success", true);
} catch (Exception e) {
log.error("MyBatis数据源测试失败", e);
result.put("success", false);
result.put("message", "测试失败: " + e.getMessage());
}
return result;
}
}

View File

@ -26,7 +26,7 @@ import javax.validation.constraints.NotBlank;
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("ENGINEER")
@TableName("engineer")
@ApiModel(value = "Engineer对象", description = "运维工程师")
public class Engineer extends SuperEntity {
@ -36,7 +36,7 @@ public class Engineer extends SuperEntity {
* 用户ID
*/
@ApiModelProperty(value = "用户ID")
@TableId(value = "USER_ID", type = IdType.ASSIGN_UUID)
@TableId(value = "user_id", type = IdType.ASSIGN_UUID)
private String userId;
/**
@ -44,7 +44,7 @@ public class Engineer extends SuperEntity {
*/
@NotBlank(message = "用户名不能为空")
@ApiModelProperty(value = "用户名 登陆账号")
@TableField("USERNAME")
@TableField("username")
private String username;
/**
@ -52,7 +52,7 @@ public class Engineer extends SuperEntity {
*/
@NotBlank(message = "密码不能为空")
@ApiModelProperty(value = "密码 采用国产SM3")
@TableField("PASSWORD")
@TableField("password")
private String password;
/**
@ -60,7 +60,7 @@ public class Engineer extends SuperEntity {
*/
@NotBlank(message = "昵称不能为空")
@ApiModelProperty(value = "昵称")
@TableField("NICKNAME")
@TableField("nickname")
private String nickname;
/**
@ -68,21 +68,21 @@ public class Engineer extends SuperEntity {
*/
@ApiModelProperty(value = "性别 12")
@NotBlank(message = "性别不能为空")
@TableField("SEX")
@TableField("sex")
private String sex;
/**
* 手机号
*/
@ApiModelProperty(value = "手机号")
@TableField("PHONE")
@TableField("phone")
private String phone;
/**
* 邮箱
*/
@ApiModelProperty(value = "邮箱")
@TableField("EMAIL")
@TableField("email")
private String email;
/**
@ -90,35 +90,35 @@ public class Engineer extends SuperEntity {
*/
@NotBlank(message = "状态不能为空")
@ApiModelProperty(value = "状态 1正常2冻结3离职")
@TableField("STATUS")
@TableField("status")
private String status;
/**
* 描述
*/
@ApiModelProperty(value = "描述")
@TableField("DESCRIPTION")
@TableField("description")
private String description;
/**
* 排序 越小则优先级更高
*/
@ApiModelProperty(value = "排序 越小则优先级更高")
@TableField("SORT")
@TableField("sort")
private BigDecimal sort;
/**
* 角色 角色代码集合
*/
@ApiModelProperty(value = "角色 角色代码集合")
@TableField("ROLES")
@TableField("roles")
private String roles;
/**
* 所负责的系统 集合
*/
@ApiModelProperty(value = "所负责的系统 集合")
@TableField("SOURCE")
@TableField("source")
private String source;
/**
@ -126,7 +126,7 @@ public class Engineer extends SuperEntity {
*/
@ApiModelProperty(value = "是否删除 0未删除、1已删除")
@TableLogic(value = ConstantsUtil.NOT_DELETED, delval = ConstantsUtil.DELETED)
@TableField("IS_DELETED")
@TableField("is_deleted")
private String isDeleted;

View File

@ -32,3 +32,31 @@ applicationName: devOps
swagger:
enable: true
# 错误日志配置
error-log:
# 是否启用错误日志文件写入功能
enabled: true
# 启动错误日志
startup-enabled: true
# 运行时错误日志
runtime-enabled: true
# 数据库错误日志
database-enabled: true
# 业务错误日志
business-enabled: true
# 启动信息日志
startup-info-enabled: true
# 日志目录
log-directory: logs/errors
# 单个日志文件最大大小
max-file-size: 10MB
# 保留日志文件的最大天数
max-history: 30
# 是否在控制台同时输出
console-output: true
# 详细级别 (0-3, 3为最详细)
detail-level: 3
# 是否异步写入
async-write: true
# 异步队列大小
async-queue-size: 1000

View File

@ -3,7 +3,7 @@ logging:
root: info
spring:
profiles:
active: prod
active: dev
application:
name: youfoo-devops
datasource:
@ -84,7 +84,7 @@ dbUsernames: AICSCR,AICORG,SPEEQU,ZKRUSER,GOLDENGATE,DEVOPS,SYSTEM,SPEE_OS,DWDDA
# 错误日志默认配置
error-log:
# 全局开关,默认启用
enabled: true
enabled: false
# 各类型日志开关
startup-enabled: true
runtime-enabled: true