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

【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.lryc.cn/news/607716.html

相关文章:

  • 在 AKS 中运行 Azure DevOps 自托管代理-2
  • 【Office】Office2024最新版下载安装使用教程(附多版本安装包)
  • 【深度学习新浪潮】什么是专业科研智能体?
  • Flutter镜像替换
  • 大模型学习专栏-导航页
  • 第十四天:C++内存管理
  • 5-EP4CE10F17C8-引脚配置
  • 亚像素级精度的二维图像配准方法
  • Metamorph、LlamaFusion、MetaQuery论文解读
  • 第13届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2022年1月22日真题
  • 两个服务之间的大规模数据推送
  • 《文明5》错误代码0xc0000142修复方法
  • linux编译基础知识-工具链
  • Java 日期时间格式化模式说明
  • 蓝桥杯----DA、AD
  • Prim算法
  • 26数据结构-顺序表
  • python列表推导式
  • windows系统安装文生图大模型Stable diffusion V3.5 large(完整详细可用教程)
  • 损失函数和调度器相关类代码回顾理解 |nn.CrossEntropyLoss\CosineAnnealingLR
  • 接口幂等性
  • 数据库小知识
  • C4画图实战案例分享
  • 利用CompletableFuture优化查询效率
  • FreeRTOS硬件中断发生时的现场
  • 逻辑回归在银行贷款审批中的应用:参数选择与实践
  • c++详解(宏与内联函数,nullptr)
  • 查看主板信息的3种方法
  • PL-0功能拓展及基于VSCode的IDE配置
  • QT开发---图形与图像(补充)