我创建了一个 java swing 应用程序,它从数据库中获取数据,并将其显示在 JTable 中。我目前只是想查看数据库中的所有数据。但是当我单击按钮将查询发送到数据库并在jtable上查看获得的数据时,该表没有更新。这是我的代码:
TableData.java
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
public class TableData extends JPanel {
private JTable table;
private DefaultTableModel model;
private final Object Column[] = {"id", "titolo", "autore", "prezzo"};
TableData(){
model = new DefaultTableModel();
table = new JTable(model);
model.setColumnIdentifiers(Column);
setLayout(new BorderLayout());
add(new JScrollPane(table), BorderLayout.CENTER);
}
public void updateTable(Object row[]){
model.addRow(row);
}
}
ConnectionDB.java
import java.sql.Statement;
import java.sql.DriverManager;
import java.sql.ResultSet;
public class ConnectionDB {
private String url = "jdbc:mysql://localhost:4444/database_name";
private String user = "root";
private String password = "password";
TableData tableData = new TableData();
//id titolo autore prezzo
public void upload_data(String titolo, String autore, int prezzo){
try{
Connection con = DriverManager.getConnection(url, user, password);
Statement statement = con.createStatement();
String query = "insert into libri(titolo, autore, prezzo)" +
"values ('"+titolo+"','"+autore+"','"+prezzo+"')";
statement.executeUpdate(query);
System.out.println("success");
}
catch(Exception e){
e.printStackTrace();
}
}
public void get_data(){
Object row[] = new Object[4];
try{
Connection conn = DriverManager.getConnection(url, user, password);
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery("select * from libri");
while(resultSet.next()){
for(int i = 1; i<=4; i++){
row[i-1] = resultSet.getString(i);
}
tableData.updateTable(row);
}
}
catch(Exception e){
e.printStackTrace();
}
}
}
PanelForm.java
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.jar.JarEntry;
public class PanelForm extends JPanel {
....somebutton.....
ConnectionDB conn = new ConnectionDB();
....some board....
buttonVisualizza.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
conn.get_data();
}
});
}
}
这是我的
HomePage.java
import javax.swing.text.AbstractDocument;
import java.awt.*;
public class HomePage extends JFrame {
private PanelForm panelForm;
private TableData tableData;
HomePage(){
super("Home");
setSize(800,500);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocation(200,200);
setLayout(new BorderLayout());
panelForm = new PanelForm();
tableData = new TableData();
add(panelForm, BorderLayout.LINE_START);
add(tableData, BorderLayout.CENTER);
setVisible(true);
}
}
我已经尝试重新加载模型,这个命令:
model.fireTableDataChanged();
因此,您立即创建了
TableData
的多个实例....
public class ConnectionDB {
//...
TableData tableData = new TableData();
public class HomePage extends JFrame {
private PanelForm panelForm;
private TableData tableData;
HomePage(){
//..
tableData = new TableData();
这两个实例彼此之间没有任何关系,因此更新一个实例不会更新另一个实例。
这表明您需要花更多时间研究诸如
之类的概念首先,您正在使用面向对象的语言,这意味着,您应该尝试将数据更多地描述为“对象”,而不是数组中元素的丢失连接。
让我们从“书”的概念开始......
public interface Book {
public String getTitle();
public String getAuthor();
public int getPrice();
}
我喜欢
interface
,如果您需要将数据源从本地数据库等转移到 Web API,它们可以使您的代码免于在实现级别可能发生的更改。
话虽如此,我们仍然需要一个具体的工具......
public class DefaultBook implements Book {
private String title;
private String author;
private int price;
public DefaultBook(String title, String author, int price) {
this.title = title;
this.author = author;
this.price = price;
}
@Override
public String getTitle() {
return title;
}
@Override
public String getAuthor() {
return author;
}
@Override
public int getPrice() {
return price;
}
}
我可能也想将记录数据库密钥注入到实现中,但这是您稍后可以解决的详细信息。
接下来,我们来看看
ConnectionDB
...
public class ConnectionDB {
//...
public void upload_data(String titolo, String autore, int prezzo){
try{
Connection con = DriverManager.getConnection(url, user, password);
Statement statement = con.createStatement();
String query = "insert into libri(titolo, autore, prezzo)" +
"values ('"+titolo+"','"+autore+"','"+prezzo+"')";
statement.executeUpdate(query);
System.out.println("success");
}
catch(Exception e){
e.printStackTrace();
}
}
这不仅效率低、速度慢,而且还很危险。
每次创建
Connection
的新实例都很慢,而且您很容易受到 sql 注入 攻击。您也没有关闭任何资源,这可能意味着连接将使它们保持打开状态,从而降低数据库的性能。
首先查看 try-with-resources 语句 和 使用准备好的语句。
相反,您应该使用依赖注入来传递对
Connection
的引用。这解决了创建连接的责任类别,允许您创建与您想要的数据库所需的任何连接。
public class ConnectionDB {
private Connection connection;
public ConnectionDB(Connection connction) {
this.connection = connction;
}
protected Connection getConnection() {
return connection;
}
public void uploadData(Book book) throws SQLException {
try(PreparedStatement statement = getConnection().prepareStatement("insert into libri(titolo, autore, prezzo) values (?, ?, ?)")) {
statement.setString(1, book.getTitle());
statement.setString(2, book.getAuthor());
statement.setInt(3, book.getPrice());
statement.executeUpdate();
System.out.println("success");
}
}
public List<Book> getData() throws SQLException {
List<Book> books = new ArrayList<>(32);
try (Statement statement = getConnection().createStatement()) {
ResultSet resultSet = statement.executeQuery("select titolo, autore, prezzo from libri");
while (resultSet.next()) {
String title = resultSet.getString(1);
String author = resultSet.getString(2);
int price = resultSet.getInt(3);
books.add(new DefaultBook(title, author, price));
}
}
return books;
}
}
您会注意到
getData
返回 List
的 Book
,这很重要,因为它删除了以前存在的任何实现细节。 ConnectionDB
并不关心您如何使用数据,只关心当您请求时,它会为您返回 List
数据。
另请注意,我不会消耗这些错误。错误管理(除了清理资源之外)不是此类的责任,调用者会想知道何时出现问题。
好吧,让我们进入 UI,说明如何为 UI 的
ConnectionDB
中的数据建模。
我个人会创建一个自定义的
TableModel
,它可以采用 List
的 Book
并根据需要对其进行建模,例如...
public class BookTableModel extends AbstractTableModel {
private List<Book> books;
private String[] columnNames = new String[] {
"Title", "Author", "Price"
};
public BookTableModel(List<Book> books) {
this.books = books;
}
public List<Book> getBooks() {
return books;
}
@Override
public int getRowCount() {
return getBooks().size();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Book book = getBooks().get(rowIndex);
switch (columnIndex) {
case 0: return book.getTitle();
case 1: return book.getAuthor();
case 2: return book.getPrice();
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0: return String.class;
case 1: return String.class;
case 2: return Integer.class;
}
return Object.class;
}
}
这很重要,因为如果我愿意的话,我可以使用
ListModel
来代替!相同的数据,不同的呈现方式!
public class BookTable extends JPanel {
private JTable table;
public BookTable(BookTableModel model) {
setLayout(new BorderLayout());
table = new JTable(model);
add(new JScrollPane(table));
}
protected JTable getTable() {
return table;
}
public BookTableModel getModel() {
return (BookTableModel) getTable().getModel();
}
public void setModel(BookTableModel model) {
getTable().setModel(model);
}
}
现在,除非我的 UI 足够复杂,否则我可能不会将表管理分离到单独的类中,相反,我可能会做类似的事情......
public class BookPane extends JPanel {
private ConnectionDB connectionDB;
private JTable bookTable;
public BookPane(ConnectionDB connectionDB) {
this.connectionDB = connectionDB;
setLayout(new BorderLayout());
// You could add a empty constructor to the
// BookTableModel to make this a bit easier
bookTable = new JTable(new BookTableModel(new ArrayList<>()));
add(new JScrollPane(bookTable));
JButton loadButton = new JButton("Load");
loadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
List<Book> data = getConnectionDB().getData();
// You could just replace the `List` managed by
// BookTableModel, but this is just simpler
getBookTable().setModel(new BookTableModel(data));
} catch (SQLException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(BookPane.this, "Failed to load data", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
}
public ConnectionDB getConnectionDB() {
return connectionDB;
}
protected JTable getBookTable() {
return bookTable;
}
}
那么,这一切是如何联系在一起的呢?嗯,也许类似...
Connection connection = ...; // Create an instance of a Connection for your database
ConnectionDB connectionDB = new ConnectionDB(connection);
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new BookPane(connectionDB));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});