MVC架构下Java实现Excel数据展示到JTable的技术问询
Let's fix your code step by step while sticking to your layered architecture rules (entity ↔ controller ↔ boundary, no direct entity-boundary access). The main issues right now are that your entity isn't exposing the Excel data you read, your controller isn't passing that data to the UI, and the UI is incorrectly instantiating the entity directly.
1. Refactor the Entity Layer
Your getAllBugs1 method reads the Excel data but keeps it trapped in local variables. Let's adjust it to store the data as instance fields so the controller can access it, and remove the redundant parameter of the same class type.
import java.io.File; import java.util.Vector; import jxl.Workbook; import jxl.Sheet; import jxl.Cell; public class ListBugReportEntity { // Instance fields to store Excel data private Vector<String> headers; private Vector<Vector<String>> data; public boolean getAllBugs1() { headers = new Vector<>(); data = new Vector<>(); File file = new File("src/BugReportDB.xls"); try { Workbook workbook = Workbook.getWorkbook(file); Sheet sheet = workbook.getSheet(0); // Read headers for (int i = 0; i < sheet.getColumns(); i++) { Cell cell = sheet.getCell(i, 0); headers.add(cell.getContents()); } // Read rows for (int j = 1; j < sheet.getRows(); j++) { Vector<String> rowData = new Vector<>(); for (int i = 0; i < sheet.getColumns(); i++) { Cell cell = sheet.getCell(i, j); rowData.add(cell.getContents()); } data.add(rowData); } workbook.close(); // Don't forget to release resources! return true; } catch (Exception e) { e.printStackTrace(); headers.clear(); data.clear(); return false; } } // Getters to expose data to the controller public Vector<String> getHeaders() { return headers; } public Vector<Vector<String>> getData() { return data; } }
2. Refactor the Controller Layer
Now the controller will trigger data loading in the entity, validate that the data is actually usable (not empty), and provide access to the valid data for the UI. It no longer just returns a boolean—it acts as a proper middleman.
public class ListBugController { private ListBugReportEntity bugEntity = new ListBugReportEntity(); // Trigger data loading and validate its validity public boolean loadBugData() { boolean loadSuccess = bugEntity.getAllBugs1(); // Extra check: ensure we have actual data to display if (loadSuccess && !bugEntity.getHeaders().isEmpty() && !bugEntity.getData().isEmpty()) { return true; } return false; } // Pass data to UI (only valid if loadBugData returned true) public Vector<String> getBugHeaders() { return bugEntity.getHeaders(); } public Vector<Vector<String>> getBugData() { return bugEntity.getData(); } }
3. Refactor the Boundary (UI) Layer
The UI will now only interact with the controller (no direct entity access). We'll fix the layout (null layout is fragile—use standard Swing layouts instead), populate the JTable with data from the controller, and add user feedback for errors.
import java.awt.*; import java.util.Vector; import javax.swing.*; import javax.swing.table.DefaultTableModel; public class ListBugReportUI extends JFrame { private ListBugController bugController; public ListBugReportUI() { bugController = new ListBugController(); initUI(); loadAndDisplayBugData(); } private void initUI() { setTitle("View All Bugs"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(700, 600); Container c = getContentPane(); c.setLayout(new BorderLayout(10, 10)); c.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); // Title panel JPanel titlePanel = new JPanel(); JLabel sBug = new JLabel("List of Bugs"); sBug.setFont(new Font("Arial", Font.PLAIN, 30)); titlePanel.add(sBug); c.add(titlePanel, BorderLayout.NORTH); // Search panel JPanel searchPanel = new JPanel(); JLabel search = new JLabel("Search By"); search.setFont(new Font("Arial", Font.PLAIN, 20)); JTextField searchF = new JTextField(20); searchF.setFont(new Font("Arial", Font.PLAIN, 15)); searchPanel.add(search); searchPanel.add(searchF); c.add(searchPanel, BorderLayout.CENTER); // Table area JTable table = new JTable(); JScrollPane scroll = new JScrollPane(table); scroll.setPreferredSize(new Dimension(660, 300)); c.add(scroll, BorderLayout.SOUTH); } private void loadAndDisplayBugData() { boolean dataLoaded = bugController.loadBugData(); if (dataLoaded) { Vector<String> headers = bugController.getBugHeaders(); Vector<Vector<String>> data = bugController.getBugData(); DefaultTableModel model = new DefaultTableModel(data, headers); JTable table = (JTable) ((JScrollPane) getContentPane().getComponent(2)).getViewport().getView(); table.setModel(model); table.setAutoCreateRowSorter(true); } else { JOptionPane.showMessageDialog(this, "Failed to load bug data. Check if BugReportDB.xls exists in src/", "Data Load Error", JOptionPane.ERROR_MESSAGE); } } public static void main(String[] args) { // Run UI on Swing's event dispatch thread (best practice) SwingUtilities.invokeLater(() -> { new ListBugReportUI().setVisible(true); }); } }
Key Improvements & Best Practices
- Architecture Compliance: The UI only communicates with the controller, which talks to the entity—no direct cross-layer access.
- Data Accessibility: The entity exposes its read data via getters, so the controller can safely pass it to the UI without breaking encapsulation.
- Robust Validation: The controller checks that loaded data isn't empty, preventing empty tables from being displayed.
- Swing Best Practices: Used layout managers instead of null layout (makes the UI responsive), wrapped UI startup in
SwingUtilities.invokeLater, and added user-friendly error dialogs. - Resource Management: Added
workbook.close()to prevent resource leaks when reading the Excel file.
For even cleaner code, you could create a BugTableDTO class to wrap the headers and data instead of passing two separate vectors—this makes the controller's API more intuitive and reduces parameter clutter.
内容的提问来源于stack exchange,提问作者Joey Kang

