当前位置: 首页 > ds >正文

【Java】不允许直接操作数据表中的数据,开发前台界面来实现对多个数据表的增删改查

企业的生产环境中,如果不允许直接操作数据表中的数据,则需要开发一个前台界面,在必要时实现对多个数据表中数据的增删改查,如下为实现修改数据的方案……

一、前后端数据交互设计

  1. 修改数据DTO设计
@Data
public class TableUpdateDTO {private String tableName;          // 表名private String primaryKey;         // 主键列名private Object primaryKeyValue;    // 主键值private Map<String, Object> changes; // 修改的字段和值@Datapublic static class BatchUpdateDTO {private String tableName;private List<TableUpdateDTO> updates;}
}
  1. 前端传参格式示例
{"tableName": "EMPLOYEES","primaryKey": "EMPLOYEE_ID","primaryKeyValue": 100,"changes": {"FIRST_NAME": "张","LAST_NAME": "三","EMAIL": "zhangsan@example.com","SALARY": 8500}
}

二、后端处理实现

  1. 数据更新服务层
@Service
@Transactional
public class OracleDataUpdateService {@Autowiredprivate DataSource dataSource;public int updateTableData(TableUpdateDTO updateDTO) throws SQLException {validateUpdate(updateDTO);// 构建动态SQLString sql = buildUpdateSql(updateDTO);try (Connection conn = dataSource.getConnection();PreparedStatement stmt = conn.prepareStatement(sql)) {// 设置参数int paramIndex = 1;for (Map.Entry<String, Object> entry : updateDTO.getChanges().entrySet()) {setParameter(stmt, paramIndex++, entry.getValue());}setParameter(stmt, paramIndex, updateDTO.getPrimaryKeyValue());return stmt.executeUpdate();}}private String buildUpdateSql(TableUpdateDTO updateDTO) {StringBuilder sql = new StringBuilder("UPDATE ").append(updateDTO.getTableName()).append(" SET ");// 添加SET子句updateDTO.getChanges().keySet().forEach(col -> sql.append(col).append(" = ?, "));// 移除最后一个逗号sql.setLength(sql.length() - 2);// 添加WHERE条件sql.append(" WHERE ").append(updateDTO.getPrimaryKey()).append(" = ?");return sql.toString();}private void setParameter(PreparedStatement stmt, int index, Object value) throws SQLException {// 处理Oracle特定类型if (value instanceof java.util.Date) {stmt.setTimestamp(index, new Timestamp(((java.util.Date)value).getTime()));} else if (value instanceof String && ((String)value).length() > 4000) {// 处理大文本stmt.setCharacterStream(index, new StringReader((String)value), ((String)value).length());} else {stmt.setObject(index, value);}}private void validateUpdate(TableUpdateDTO updateDTO) {// 表名安全校验if (!updateDTO.getTableName().matches("[A-Za-z0-9_]+")) {throw new IllegalArgumentException("非法的表名");}// 检查主键if (updateDTO.getPrimaryKey() == null || updateDTO.getPrimaryKeyValue() == null) {throw new IllegalArgumentException("必须指定主键");}// 检查修改字段if (updateDTO.getChanges() == null || updateDTO.getChanges().isEmpty()) {throw new IllegalArgumentException("未指定修改字段");}}
}
  1. 控制器层实现
@RestController
@RequestMapping("/api/oracle")
public class OracleDataUpdateController {@Autowiredprivate OracleDataUpdateService updateService;@PutMapping("/update")public ResponseEntity<?> updateData(@RequestBody TableUpdateDTO updateDTO) {try {int affected = updateService.updateTableData(updateDTO);return ResponseEntity.ok(Map.of("success", true, "message", "更新成功", "affectedRows", affected));} catch (Exception e) {return ResponseEntity.badRequest().body(Map.of("success", false, "message", e.getMessage()));}}@PutMapping("/batch-update")public ResponseEntity<?> batchUpdate(@RequestBody TableUpdateDTO.BatchUpdateDTO batchUpdate) {try {List<Integer> results = new ArrayList<>();for (TableUpdateDTO updateDTO : batchUpdate.getUpdates()) {results.add(updateService.updateTableData(updateDTO));}return ResponseEntity.ok(Map.of("success", true, "message", "批量更新成功", "results", results));} catch (Exception e) {return ResponseEntity.badRequest().body(Map.of("success", false, "message", e.getMessage()));}}
}

三、前端实现方案

  1. Vue.js表格编辑组件
<template><div><table class="editable-table"><thead><tr><th v-for="col in columns" :key="col.columnName">{{ col.columnName }}</th><th>操作</th></tr></thead><tbody><tr v-for="(row, index) in rows" :key="index"><td v-for="col in columns" :key="col.columnName"><template v-if="editingRow === index && col.editable !== false"><input v-model="editingData[col.columnName]" :type="getInputType(col.dataType)"></template><template v-else>{{ formatValue(row[col.columnName]) }}</template></td><td><template v-if="editingRow === index"><button @click="saveEdit(index)">保存</button><button @click="cancelEdit">取消</button></template><button v-else @click="startEdit(index, row)">编辑</button></td></tr></tbody></table></div>
</template><script>
export default {props: {columns: Array,  // 列元数据rows: Array      // 表数据},data() {return {editingRow: null,editingData: {},primaryKey: 'ID' // 根据实际情况设置主键};},methods: {startEdit(index, row) {this.editingRow = index;this.editingData = {...row};},cancelEdit() {this.editingRow = null;this.editingData = {};},async saveEdit(index) {try {const updateDTO = {tableName: this.tableName,primaryKey: this.primaryKey,primaryKeyValue: this.rows[index][this.primaryKey],changes: this.getChanges(this.rows[index], this.editingData)};const response = await this.$axios.put('/api/oracle/update', updateDTO);if (response.data.success) {// 更新本地数据this.$emit('update-row', index, this.editingData);this.cancelEdit();this.$message.success('更新成功');} else {this.$message.error(response.data.message);}} catch (error) {this.$message.error('更新失败: ' + error.message);}},getChanges(original, edited) {const changes = {};for (const key in edited) {if (!this.deepEqual(original[key], edited[key])) {changes[key] = edited[key];}}return changes;},deepEqual(a, b) {// 实现深度比较return JSON.stringify(a) === JSON.stringify(b);},formatValue(value) {if (value === null || value === undefined) return 'NULL';if (value instanceof Object) return JSON.stringify(value);return value;},getInputType(dataType) {// 根据Oracle数据类型返回合适的input类型const typeMap = {'NUMBER': 'number','DATE': 'datetime-local','VARCHAR2': 'text','CHAR': 'text','CLOB': 'textarea','BLOB': 'file'};return typeMap[dataType] || 'text';}}
};
</script><style scoped>
.editable-table {width: 100%;border-collapse: collapse;
}
.editable-table th, .editable-table td {border: 1px solid #ddd;padding: 8px;
}
.editable-table th {background-color: #f2f2f2;
}
.editable-table input {width: 90%;padding: 5px;
}
</style>
  1. 批量编辑实现
// 在父组件中
methods: {async handleBatchSave(changedRows) {try {const batchUpdate = {tableName: this.tableName,updates: changedRows.map(row => ({tableName: this.tableName,primaryKey: this.primaryKey,primaryKeyValue: row[this.primaryKey],changes: row.changes}))};const response = await this.$axios.put('/api/oracle/batch-update', batchUpdate);if (response.data.success) {this.$message.success(`成功更新 ${response.data.results.length} 条记录`);this.loadTableData(); // 重新加载数据} else {this.$message.error(response.data.message);}} catch (error) {this.$message.error('批量更新失败: ' + error.message);}}
}

四、安全增强措施

  1. 字段权限控制
// 在服务层添加字段白名单检查
private void validateUpdateFields(String tableName, Set<String> fields) {Set<String> allowedFields = getAllowedFields(tableName);for (String field : fields) {if (!allowedFields.contains(field.toUpperCase())) {throw new IllegalArgumentException("不允许修改字段: " + field);}}
}private Set<String> getAllowedFields(String tableName) {// 可以从配置文件中加载每个表允许修改的字段Map<String, Set<String>> tableFields = new HashMap<>();tableFields.put("EMPLOYEES", Set.of("FIRST_NAME", "LAST_NAME", "EMAIL", "SALARY"));// 其他表配置...return tableFields.getOrDefault(tableName.toUpperCase(), Collections.emptySet());
}
  1. 数据验证
// 添加数据验证逻辑
private void validateFieldValue(String tableName, String field, Object value) {// 1. 非空检查if (value == null && isNotNullField(tableName, field)) {throw new IllegalArgumentException(field + " 不能为空");}// 2. 数据类型检查if (value != null) {String expectedType = getFieldType(tableName, field);if (!isTypeMatch(expectedType, value)) {throw new IllegalArgumentException(field + " 类型不匹配");}}// 3. 业务规则检查if ("SALARY".equalsIgnoreCase(field) && value instanceof Number) {double salary = ((Number)value).doubleValue();if (salary < 0) {throw new IllegalArgumentException("薪资不能为负数");}}
}

五、高级功能扩展

  1. 乐观锁控制
// 在DTO中添加版本号字段
@Data
public class TableUpdateDTO {private String versionColumn; // 版本号列名(如VERSION)private Object versionValue;  // 当前版本号值// 其他字段...
}// 在SQL构建中添加版本检查
private String buildUpdateSql(TableUpdateDTO updateDTO) {StringBuilder sql = new StringBuilder("UPDATE ").append(updateDTO.getTableName()).append(" SET ");updateDTO.getChanges().keySet().forEach(col -> sql.append(col).append(" = ?, "));// 添加版本号自增if (updateDTO.getVersionColumn() != null) {sql.append(updateDTO.getVersionColumn()).append(" = ").append(updateDTO.getVersionColumn()).append(" + 1, ");}sql.setLength(sql.length() - 2);sql.append(" WHERE ").append(updateDTO.getPrimaryKey()).append(" = ?");// 添加版本检查条件if (updateDTO.getVersionColumn() != null) {sql.append(" AND ").append(updateDTO.getVersionColumn()).append(" = ?");}return sql.toString();
}
  1. 修改历史记录
// 使用AOP记录数据修改
@Aspect
@Component
public class DataUpdateAuditAspect {@AfterReturning(pointcut = "execution(* com..OracleDataUpdateService.updateTableData(..)) && args(updateDTO)",returning = "affected")public void auditUpdate(TableUpdateDTO updateDTO, int affected) {if (affected > 0) {DataChangeLog log = new DataChangeLog();log.setTableName(updateDTO.getTableName());log.setRecordId(String.valueOf(updateDTO.getPrimaryKeyValue()));log.setChangeContent(JSON.toJSONString(updateDTO.getChanges()));log.setChangeTime(new Date());log.setChangeUser(getCurrentUser());changeLogRepository.save(log);}}
}

六、错误处理与用户体验

  1. 后端统一异常处理
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(SQLException.class)public ResponseEntity<?> handleSqlException(SQLException e) {String message = "数据库操作失败";// Oracle特定错误码处理if (e.getErrorCode() == 1) {message = "违反唯一约束: " + e.getMessage();} else if (e.getErrorCode() == 2292) {message = "违反外键约束: " + e.getMessage();}return ResponseEntity.badRequest().body(Map.of("success", false, "message", message));}@ExceptionHandler(DataIntegrityViolationException.class)public ResponseEntity<?> handleDataIntegrityViolation() {return ResponseEntity.badRequest().body(Map.of("success", false, "message", "数据完整性校验失败,请检查输入数据"));}
}
  1. 前端错误反馈
// 在Vue组件中增强错误处理
async saveEdit(index) {this.$loading = true;try {const response = await this.$axios.put('/api/oracle/update', this.updateDTO);if (response.data.success) {this.$emit('update-row', index, this.editingData);this.cancelEdit();this.$notify({title: '成功',message: '数据更新成功',type: 'success'});} else {this.$notify.error({title: '错误',message: response.data.message});}} catch (error) {let message = error.message;if (error.response && error.response.data) {message = error.response.data.message || message;}this.$notify.error({title: '请求失败',message: `更新失败: ${message}`});} finally {this.$loading = false;}
}
http://www.xdnf.cn/news/17013.html

相关文章:

  • 【LeetCode刷题指南】--二叉树的前序遍历,二叉树的中序遍历
  • 力扣-105.从前序与中序遍历序列构造二叉树
  • Makefile 从入门到精通:自动化构建的艺术
  • 【人工智能agent】--服务器部署PaddleX 的 印章文本识别模型
  • 详解Python标准库之互联网数据处理
  • 电脑手机热点方式通信(下)
  • 基于OAuth2与JWT的微服务API安全实战经验分享
  • 【云计算】云主机的亲和性策略(四):云主机组
  • Go语言中的闭包详解
  • 【读代码】 KAG项目:开源知识图谱自动构建与推理平台原理与实践
  • Spring框架深度学习实战
  • 深度学习核心:神经网络-激活函数 - 原理、实现及在医学影像领域的应用
  • 进阶向:自动化天气查询工具(API调用)
  • Munge 安全认证和授权服务的工作原理,以及与 Slurm 的配合
  • SpringCloud实战:机器人对战系统架构
  • 电脑手机热点方式通信(上)
  • 什么是AWS Region和AWS Availability Zones
  • 算法复杂度分析
  • 服装ERP系统:高效整合管理,优化生产流程
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现道路上头盔的检测识别(C#代码,UI界面版)
  • 排查解决 nvidia-suspend 导致的 linux 系统无响应/死机问题
  • ICCV2025 | 对抗样本智能安全方向论文汇总 | 持续更新中~
  • 6-EP4CE10F17C8-锁相环
  • [Windows] 微软.Net运行库离线合集包 Microsoft .Net Packages AIO v13.05.25
  • Flutter开发 初识目录结构
  • 【07】VisionMaster入门到精通——Blob分折
  • 2 安装 Docker 和 Jenkins:持续构建环境起步
  • 第三章 用户和权限
  • 基于落霞归雁思维框架的软件需求管理实践指南
  • MyBatis与MySQL