import javax.swing.*;import javax.swing.table.*;import javax.swing.text.*;import javax.swing.tree.*;import javax.swing.event.*;import java.awt.*;import java.awt.event.*;import java.sql.*;import java.util.ArrayList;import java.util.List;import java.util.Vector;import java.util.regex.*;public class OceanBaseClientFinal extends JFrame {private JTabbedPane mainTabbedPane;private JTree databaseTree;private DefaultTreeModel treeModel;private Connection connection;// UI 样式常量private static final Color PRIMARY_COLOR = new Color(0, 82, 204);private static final Color SUCCESS_COLOR = new Color(40, 167, 69);private static final Color DANGER_COLOR = new Color(220, 53, 69);private static final Color SQL_PURPLE = new Color(103, 58, 183);private static final Color INFO_BLUE = new Color(23, 162, 184);private static final Color BG_LIGHT = new Color(248, 249, 250);private static final Font MAIN_FONT = new Font("Microsoft YaHei", Font.PLAIN, 13);private static final Font MONO_FONT = new Font("Consolas", Font.PLAIN, 15);private static final String URL = "jdbc:oceanbase://50.80.100.1:2881/AI_LLM?useSSL=false&useUnicode=true&characterEncoding=gbk";private static final String USERNAME = "AI_L@leaf#oboracle";private static final String PASSWORD = "Hs@3#";public OceanBaseClientFinal() {setTitle("OceanBase 智能管理终端 - 实时同步版");setSize(1400, 900);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLocationRelativeTo(null);try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) {}initComponents();connectToDB();}private void initComponents() {mainTabbedPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);mainTabbedPane.setFont(MAIN_FONT);JPanel leftPanel = new JPanel(new BorderLayout());leftPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.LIGHT_GRAY));JPanel actionPanel = new JPanel(new BorderLayout());actionPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));JButton btnNewQuery = createStyledButton(" + 新建 SQL 查询 ", SQL_PURPLE);btnNewQuery.addActionListener(e -> openSqlEditor());actionPanel.add(btnNewQuery, BorderLayout.CENTER);DefaultMutableTreeNode root = new DefaultMutableTreeNode("OceanBase 实例");treeModel = new DefaultTreeModel(root);databaseTree = new JTree(treeModel);databaseTree.setFont(MAIN_FONT);databaseTree.setRowHeight(28);setupTreeInteractions();leftPanel.add(actionPanel, BorderLayout.NORTH);leftPanel.add(new JScrollPane(databaseTree), BorderLayout.CENTER);JSplitPane mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, mainTabbedPane);mainSplit.setDividerLocation(280);mainSplit.setDividerSize(5);getContentPane().add(mainSplit, BorderLayout.CENTER);}private void setupTreeInteractions() {JPopupMenu rootMenu = new JPopupMenu();JMenuItem reconnectItem = new JMenuItem("重新连接数据库");reconnectItem.addActionListener(e -> connectToDB());rootMenu.add(reconnectItem);JPopupMenu tableMenu = new JPopupMenu();JMenuItem dataItem = new JMenuItem("打开数据视图");JMenuItem structItem = new JMenuItem("查看字段结构");JMenuItem ddlItem = new JMenuItem("生成建表语句");tableMenu.add(dataItem);tableMenu.add(structItem);tableMenu.addSeparator();tableMenu.add(ddlItem);databaseTree.addMouseListener(new MouseAdapter() {public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) handlePopup(e); }public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) handlePopup(e); }private void handlePopup(MouseEvent e) {TreePath path = databaseTree.getPathForLocation(e.getX(), e.getY());if (path == null) return;databaseTree.setSelectionPath(path);DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();if (node.isRoot()) rootMenu.show(e.getComponent(), e.getX(), e.getY());else if (node.isLeaf()) {String tbl = node.getUserObject().toString();setupTableMenuActions(dataItem, structItem, ddlItem, tbl);tableMenu.show(e.getComponent(), e.getX(), e.getY());}}public void mouseClicked(MouseEvent e) {if (e.getClickCount() == 2) {DefaultMutableTreeNode node = (DefaultMutableTreeNode) databaseTree.getLastSelectedPathComponent();if (node != null && node.isLeaf()) openDataEditor(node.getUserObject().toString());}}});}private void setupTableMenuActions(JMenuItem data, JMenuItem struct, JMenuItem ddl, String tbl) {for (ActionListener al : data.getActionListeners()) data.removeActionListener(al);for (ActionListener al : struct.getActionListeners()) struct.removeActionListener(al);for (ActionListener al : ddl.getActionListeners()) ddl.removeActionListener(al);data.addActionListener(e -> openDataEditor(tbl));struct.addActionListener(e -> openTableStructure(tbl));ddl.addActionListener(e -> showDDL(tbl));}// --- 紧凑可关闭页签 ---private void addTab(String title, JPanel content) {for (int i = 0; i < mainTabbedPane.getTabCount(); i++) {if (mainTabbedPane.getTitleAt(i).equals(title)) {mainTabbedPane.setSelectedIndex(i);return;}}mainTabbedPane.addTab(title, content);int idx = mainTabbedPane.getTabCount() - 1;JPanel head = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0));head.setOpaque(false);head.add(new JLabel(title));JButton close = new JButton("×ばつ");close.setPreferredSize(new Dimension(16, 16));close.setFont(new Font("Arial", Font.BOLD, 12));close.setBorderPainted(false);close.setContentAreaFilled(false);close.setCursor(new Cursor(Cursor.HAND_CURSOR));close.addActionListener(e -> {int i = mainTabbedPane.indexOfComponent(content);if (i != -1) mainTabbedPane.remove(i);});head.add(close);mainTabbedPane.setTabComponentAt(idx, head);mainTabbedPane.setSelectedIndex(idx);}// --- 核心:表格内直接编辑与新增 ---private void openDataEditor(String tbl) {JPanel panel = new JPanel(new BorderLayout());JToolBar bar = new JToolBar(); bar.setFloatable(false);JButton add = createStyledButton("+ 表格内加行", SUCCESS_COLOR);JButton commitAdd = createStyledButton("✔ 提交新行", INFO_BLUE);JButton del = createStyledButton("- 删除选中", DANGER_COLOR);JButton ref = createStyledButton("↻ 刷新数据", PRIMARY_COLOR);bar.add(add); bar.add(commitAdd); bar.addSeparator(); bar.add(del); bar.add(ref);DefaultTableModel model = new DefaultTableModel() {@Overridepublic boolean isCellEditable(int r, int c) {// 如果是最后一行(新加行),则允许编辑所有列(包括 ID)if (r == getRowCount() - 1 && getRowCount() > 0 && getValueAt(r, 0) == null) return true;// 否则禁止编辑第一列主键return c > 0;}};JTable table = new JTable(model);table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);table.setRowHeight(28);table.setDefaultRenderer(Object.class, new ZebraRenderer());ref.addActionListener(e -> refreshTableData(tbl, model, table));// 删除逻辑del.addActionListener(e -> {int r = table.getSelectedRow();if (r != -1 && JOptionPane.showConfirmDialog(this, "确认删除该记录?") == 0) {executeUpdate("DELETE FROM " + tbl + " WHERE " + table.getColumnName(0) + " = ?", table.getValueAt(r, 0));refreshTableData(tbl, model, table);}});// 在表格末尾增加空行add.addActionListener(e -> {model.addRow(new Object[model.getColumnCount()]);table.setRowSelectionInterval(model.getRowCount() - 1, model.getRowCount() - 1);});// 提交新加的行到库中commitAdd.addActionListener(e -> {int r = model.getRowCount() - 1;if (r < 0 || model.getValueAt(r, 0) == null) return;StringBuilder sql = new StringBuilder("INSERT INTO " + tbl + " (");StringBuilder vals = new StringBuilder();List<Object> params = new ArrayList<>();for (int i = 0; i < model.getColumnCount(); i++) {Object val = model.getValueAt(r, i);if (val != null && !val.toString().isEmpty()) {sql.append(table.getColumnName(i)).append(",");vals.append("?,");params.add(val);}}if (params.isEmpty()) return;sql.setLength(sql.length() - 1); vals.setLength(vals.length() - 1);sql.append(") VALUES (").append(vals).append(")");executeUpdate(sql.toString(), params.toArray());refreshTableData(tbl, model, table);});// 监听单元格编辑,自动更新到库model.addTableModelListener(e -> {if (e.getType() == TableModelEvent.UPDATE) {int r = e.getFirstRow(), c = e.getColumn();// 排除新加行(只有已存在的行修改才触发自动更新)if (r < model.getRowCount() - 1 || (r == model.getRowCount()-1 && model.getValueAt(r,0) != null)) {if (c > 0) {String sql = "UPDATE " + tbl + " SET " + table.getColumnName(c) + " = ? WHERE " + table.getColumnName(0) + " = ?";executeUpdate(sql, table.getValueAt(r, c), table.getValueAt(r, 0));}}}});panel.add(bar, BorderLayout.NORTH);panel.add(new JScrollPane(table), BorderLayout.CENTER);addTab("数据:" + tbl, panel);refreshTableData(tbl, model, table);}// --- 其他功能保留 ---private void openSqlEditor() {JPanel panel = new JPanel(new BorderLayout());JToolBar bar = new JToolBar(); bar.setFloatable(false);JButton run = createStyledButton(" ▶ 运行 ", SUCCESS_COLOR);JButton fmt = createStyledButton(" ≡ 格式化 ", INFO_BLUE);bar.add(run); bar.addSeparator(new Dimension(10,0)); bar.add(fmt);JTextPane edit = new JTextPane();edit.setFont(MONO_FONT);setupSqlHighlight(edit);DefaultTableModel model = new DefaultTableModel();JTable table = new JTable(model);table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);table.setDefaultRenderer(Object.class, new ZebraRenderer());run.addActionListener(e -> executeSql(edit.getSelectedText() != null ? edit.getSelectedText() : edit.getText(), model, table));fmt.addActionListener(e -> edit.setText(formatSql(edit.getText())));JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(edit), new JScrollPane(table));split.setDividerLocation(300);panel.add(bar, BorderLayout.NORTH);panel.add(split, BorderLayout.CENTER);addTab("SQL 查询", panel);}private void openTableStructure(String tbl) {DefaultTableModel model = new DefaultTableModel(new String[]{"ID", "列名", "类型", "长度", "可空", "备注"}, 0);JTable table = new JTable(model);table.setDefaultRenderer(Object.class, new ZebraRenderer());new Thread(() -> {String sql = "SELECT a.COLUMN_ID, a.COLUMN_NAME, a.DATA_TYPE, a.DATA_LENGTH, a.NULLABLE, b.COMMENTS " +"FROM USER_TAB_COLUMNS a LEFT JOIN USER_COL_COMMENTS b ON a.TABLE_NAME = b.TABLE_NAME AND a.COLUMN_NAME = b.COLUMN_NAME " +"WHERE a.TABLE_NAME = ? ORDER BY a.COLUMN_ID";try (PreparedStatement pst = connection.prepareStatement(sql)) {pst.setString(1, tbl.toUpperCase());ResultSet rs = pst.executeQuery();while (rs.next()) model.addRow(new Object[]{rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getString(6)});SwingUtilities.invokeLater(() -> adjustWidth(table));} catch (Exception ex) {}}).start();JPanel p = new JPanel(new BorderLayout()); p.add(new JScrollPane(table));addTab("结构:" + tbl, p);}private void showDDL(String tbl) {new Thread(() -> {try (PreparedStatement pst = connection.prepareStatement("SELECT DBMS_METADATA.GET_DDL('TABLE', ?) FROM DUAL")) {pst.setString(1, tbl.toUpperCase());ResultSet rs = pst.executeQuery();if (rs.next()) {JTextArea area = new JTextArea(rs.getString(1));area.setFont(MONO_FONT); area.setEditable(false);JOptionPane.showMessageDialog(this, new JScrollPane(area), "DDL - " + tbl, JOptionPane.PLAIN_MESSAGE);}} catch (Exception ex) {}}).start();}private void connectToDB() {new Thread(() -> {try {if (connection != null) connection.close();Class.forName("com.oceanbase.jdbc.Driver");connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);loadTree();SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(this, "连接成功"));} catch (Exception e) { SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(this, "连接失败: " + e.getMessage())); }}).start();}private void loadTree() {try {ResultSet rs = connection.getMetaData().getTables(null, null, "%", new String[]{"TABLE"});DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();root.removeAllChildren();DefaultMutableTreeNode dbNode = new DefaultMutableTreeNode("AIP_LLM");while (rs.next()) dbNode.add(new DefaultMutableTreeNode(rs.getString("TABLE_NAME")));root.add(dbNode);SwingUtilities.invokeLater(() -> { treeModel.reload(); for(int i=0; i<databaseTree.getRowCount(); i++) databaseTree.expandRow(i); });} catch (Exception e) {}}private void executeSql(String sql, DefaultTableModel model, JTable table) {new Thread(() -> {try (Statement st = connection.createStatement()) {if (st.execute(sql)) {ResultSet rs = st.getResultSet();ResultSetMetaData md = rs.getMetaData();Vector<String> names = new Vector<>();for (int i = 1; i <= md.getColumnCount(); i++) names.add(md.getColumnName(i));Vector<Vector<Object>> data = new Vector<>();while (rs.next()) {Vector<Object> row = new Vector<>();for (int i = 1; i <= md.getColumnCount(); i++) row.add(rs.getObject(i));data.add(row);}SwingUtilities.invokeLater(() -> { model.setDataVector(data, names); adjustWidth(table); });} else SwingUtilities.invokeLater(() -> {try {JOptionPane.showMessageDialog(this, "影响行数: " + st.getUpdateCount());} catch (SQLException e) {throw new RuntimeException(e);}});} catch (Exception ex) { SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(this, "SQL 错误: " + ex.getMessage())); }}).start();}private void refreshTableData(String tbl, DefaultTableModel model, JTable table) {executeSql("SELECT * FROM " + tbl + " WHERE ROWNUM <= 100", model, table);}private void executeUpdate(String sql, Object... p) {try (PreparedStatement pst = connection.prepareStatement(sql)) {for (int i = 0; i < p.length; i++) pst.setObject(i + 1, p[i]);pst.executeUpdate();} catch (Exception ex) { JOptionPane.showMessageDialog(this, "数据库更新失败: " + ex.getMessage()); }}private void setupSqlHighlight(JTextPane p) {StyledDocument doc = p.getStyledDocument();Style keyStyle = p.addStyle("k", null); StyleConstants.setForeground(keyStyle, Color.BLUE); StyleConstants.setBold(keyStyle, true);doc.addDocumentListener(new DocumentListener() {public void insertUpdate(DocumentEvent e) { run(); }public void removeUpdate(DocumentEvent e) { run(); }public void changedUpdate(DocumentEvent e) {}private void run() { SwingUtilities.invokeLater(() -> {try {String text = doc.getText(0, doc.getLength());doc.setCharacterAttributes(0, text.length(), p.getStyle("default"), true);Matcher m = Pattern.compile("\\b(SELECT|FROM|WHERE|INSERT|UPDATE|DELETE|TABLE|VALUES|AND|OR|JOIN|ON)\\b", 2).matcher(text);while (m.find()) doc.setCharacterAttributes(m.start(), m.end()-m.start(), keyStyle, false);} catch (Exception ex) {}});}});}private String formatSql(String s) {return s.replaceAll("(?i)\\s+", " ").replaceAll("(?i)\\b(SELECT|FROM|WHERE|LEFT JOIN|AND|OR|VALUES|SET)\\b", "\n1ドル\n ").trim();}private JButton createStyledButton(String t, Color c) {JButton b = new JButton(t); b.setBackground(c); b.setForeground(Color.WHITE);b.setFocusPainted(false); b.setBorderPainted(false); b.setOpaque(true); b.setFont(MAIN_FONT);b.setCursor(new Cursor(Cursor.HAND_CURSOR));b.addMouseListener(new MouseAdapter() {public void mouseEntered(MouseEvent e) { b.setBackground(c.brighter()); }public void mouseExited(MouseEvent e) { b.setBackground(c); }});return b;}private void adjustWidth(JTable t) {for (int i = 0; i < t.getColumnCount(); i++) {int w = 80;for (int j = 0; j < Math.min(t.getRowCount(), 20); j++) {Component c = t.prepareRenderer(t.getCellRenderer(j, i), j, i);w = Math.max(c.getPreferredSize().width + 20, w);}t.getColumnModel().getColumn(i).setPreferredWidth(Math.min(w, 400));}}class ZebraRenderer extends DefaultTableCellRenderer {public Component getTableCellRendererComponent(JTable t, Object v, boolean s, boolean f, int r, int c) {Component comp = super.getTableCellRendererComponent(t, v, s, f, r, c);if (!s) comp.setBackground(r % 2 == 0 ? Color.WHITE : BG_LIGHT);return comp;}}public static void main(String[] args) { SwingUtilities.invokeLater(() -> new OceanBaseClientFinal().setVisible(true)); }}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。