修复MyBatis字段映射冲突和数据库连接问题
问题修复: - 修复Engineer实体类字段映射冲突:统一使用小写下划线字段名 - 解决"字段user_id不存在"的MyBatis映射错误 - 修改所有@TableField注解从大写改为小写格式 - 表名从ENGINEER改为engineer以匹配数据库约定 功能增强: - 添加应用启动时数据库连接信息打印功能 - 增强数据库连接诊断和ENGINEER表访问测试 - 新增DatabaseTestController用于数据库连接和表结构测试 技术改进: - 解决mapUnderscoreToCamelCase配置与@TableField注解冲突 - 优化错误日志记录和诊断信息 - 增强应用启动时的数据源验证功能 修改文件: - Engineer.java: 字段映射注解全部改为小写下划线 - DevOpsApplication.java: 新增数据库连接信息打印 - DatabaseTestController.java: 新增数据库测试接口 - 配置文件优化和错误处理增强
This commit is contained in:
parent
a12d628b8f
commit
26445b86f7
|
|
@ -11,3 +11,5 @@
|
|||
/setup_database.sql
|
||||
/PowerShell启动说明.md
|
||||
/DEPLOYMENT.md
|
||||
/ERROR_CAPTURE_GUIDE.md
|
||||
/*.sql
|
||||
|
|
@ -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;
|
||||
|
|
@ -29,6 +33,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) {
|
||||
return applicationBuilder.sources(DevOpsApplication.class);
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = "性别 1:男;2:女;")
|
||||
@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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue