当前位置: 首页 > 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.dtcms.com/a/311063.html

相关文章:

  • 前端开发(HTML,CSS,VUE,JS)从入门到精通!第三天(JavaScript)
  • Python编程基础与实践:Python模块与包入门实践
  • MyBatisPlus之核心注解与配置
  • JP3-3-MyClub后台后端(三)
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘seaborn’问题
  • Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频摘要生成与智能检索优化进阶(377)
  • 2025-08 安卓开发面试拷打记录(面试题)
  • rabbitmq消息队列详述
  • 关于echarts的性能优化考虑
  • Pytorch-02数据集和数据加载器的基本原理和基本操作
  • Unity_数据持久化_XML基础
  • 【C++】第二十一节—一文详解 | 红黑树实现(规则+效率+结构+插入+查找+验证)
  • 福彩双色球第2025088期篮球号码分析
  • 电脑手机热点方式通信(上)
  • StarRocks vs ClickHouse:2025 年 OLAP 引擎终极对比指南
  • Day25-对称二叉树-
  • 仿真电路:(十七下)DC-DC升压压电路原理简单仿真
  • Clickhouse#记录隐藏字段
  • 综合:单臂路由+三层交换技术+telnet配置+DHCP
  • 【云计算】云主机的亲和性策略(四):云主机组
  • C 语言问题
  • 【机器学习】两大线性分类算法:逻辑回归与线性判别分析:找到分界线的艺术
  • 复杂路况下漏检率↓78%!陌讯动态决策模型在井盖缺失检测的实战解析
  • 系统性学习数据结构-第一讲-算法复杂度
  • Agents-SDK智能体开发[5]之集成MCP进阶
  • 机器学习 —— 决策树
  • [硬件电路-114]:模拟电路 - 信号处理电路 - 放大器的种类与比较
  • Node.js 路由与中间件
  • [硬件电路-119]:模拟电路 - 信号处理电路 - 比较器,模拟电路中的“决策者”,模拟信号到数字电平逻辑信号的转化者...
  • 音视频学习(四十六):声音的三要素