Java核心技术卷2 程序清单4-4

package view;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.nio.file.*;
import java.sql.*;
import java.util.*;

import javax.sql.*;
import javax.sql.rowset.*;
import javax.swing.*;

/**
 * This program uses metadata to display arbitrary tables in a database.
 * @version 1.33 2016-04-27
 * @author Cay Horstmann
 */
public class ViewDB
{
   public static void main(String[] args)
   {
      EventQueue.invokeLater(() ->
         {
            JFrame frame = new ViewDBFrame();
            frame.setTitle("ViewDB");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
         });
   }
}

/**
 * The frame that holds the data panel and the navigation buttons.
 */
class ViewDBFrame extends JFrame
{
   private JButton previousButton;
   private JButton nextButton;
   private JButton deleteButton;
   private JButton saveButton;
   private DataPanel dataPanel;
   private Component scrollPane;
   private JComboBox<String> tableNames;
   private Properties props;
   private CachedRowSet crs;
   private Connection conn;

   public ViewDBFrame()
   {
      tableNames = new JComboBox<String>();

      try
      {
         readDatabaseProperties();
         conn = getConnection();
         DatabaseMetaData meta = conn.getMetaData();
         try (ResultSet mrs = meta.getTables(null, null, null, new String[] { "TABLE" }))
         {
            while (mrs.next())
               tableNames.addItem(mrs.getString(3));
         }
      }
      catch (SQLException ex)
      {
         for (Throwable t : ex)
            t.printStackTrace();
      }
      catch (IOException ex)
      {
         ex.printStackTrace();
      }

      tableNames.addActionListener(
         event -> showTable((String) tableNames.getSelectedItem(), conn));
      add(tableNames, BorderLayout.NORTH);
      addWindowListener(new WindowAdapter()
         {
            public void windowClosing(WindowEvent event)
            {
               try
               {
                  if (conn != null) conn.close();
               }
               catch (SQLException ex)
               {
                  for (Throwable t : ex)
                     t.printStackTrace();
               }               
            }
         });

      JPanel buttonPanel = new JPanel();
      add(buttonPanel, BorderLayout.SOUTH);

      previousButton = new JButton("Previous");
      previousButton.addActionListener(event -> showPreviousRow());
      buttonPanel.add(previousButton);

      nextButton = new JButton("Next");
      nextButton.addActionListener(event -> showNextRow());
      buttonPanel.add(nextButton);

      deleteButton = new JButton("Delete");
      deleteButton.addActionListener(event -> deleteRow());
      buttonPanel.add(deleteButton);

      saveButton = new JButton("Save");
      saveButton.addActionListener(event -> saveChanges());
      buttonPanel.add(saveButton);
      if (tableNames.getItemCount() > 0)
         showTable(tableNames.getItemAt(0), conn);
   }

   /**
    * Prepares the text fields for showing a new table, and shows the first row.
    * @param tableName the name of the table to display
    * @param conn the database connection
    */
   public void showTable(String tableName, Connection conn)
   {
      try (Statement stat = conn.createStatement();
         ResultSet result = stat.executeQuery("SELECT * FROM " + tableName))
      {
         // get result set
         
         // copy into cached row set
         RowSetFactory factory = RowSetProvider.newFactory();            
         crs = factory.createCachedRowSet();
         crs.setTableName(tableName);
         crs.populate(result);            

         if (scrollPane != null) remove(scrollPane);
         dataPanel = new DataPanel(crs);
         scrollPane = new JScrollPane(dataPanel);
         add(scrollPane, BorderLayout.CENTER);
         pack();
         showNextRow();
      }
      catch (SQLException ex)
      {
         for (Throwable t : ex)
            t.printStackTrace();
      }
   }

   /**
    * Moves to the previous table row.
    */
   public void showPreviousRow()
   {
      try
      {
         if (crs == null || crs.isFirst()) return;
         crs.previous();
         dataPanel.showRow(crs);
      }
      catch (SQLException ex)
      {
         for (Throwable t : ex)
            t.printStackTrace();
      }
   }

   /**
    * Moves to the next table row.
    */
   public void showNextRow()
   {
      try
      {
         if (crs == null || crs.isLast()) return;
         crs.next();
         dataPanel.showRow(crs);
      }
      catch (SQLException ex)
      {
         for (Throwable t : ex)
            t.printStackTrace();
      }
   }

   /**
    * Deletes current table row.
    */
   public void deleteRow()
   {
      if (crs == null) return;
      new SwingWorker<Void, Void>()
      {
         public Void doInBackground() throws SQLException
         {
            crs.deleteRow();
            crs.acceptChanges(conn);               
            if (crs.isAfterLast()) 
               if (!crs.last()) crs = null;
            return null;
         }
         public void done()
         {
            dataPanel.showRow(crs);
         }
      }.execute();
   }
   /**
    * Saves all changes.
    */
   public void saveChanges()
   {
      if (crs == null) return;
      new SwingWorker<Void, Void>()
      {
         public Void doInBackground() throws SQLException
         {
            dataPanel.setRow(crs);
            crs.acceptChanges(conn);
            return null;
         }
      }.execute();      
   }

   private void readDatabaseProperties() throws IOException
   {
      props = new Properties();
      try (InputStream in = Files.newInputStream(Paths.get("database.properties")))
      {
         props.load(in);
      }
      String drivers = props.getProperty("jdbc.drivers");
      if (drivers != null) System.setProperty("jdbc.drivers", drivers);
   }
   
   /**
    * Gets a connection from the properties specified in the file database.properties.
    * @return the database connection
    */
   private Connection getConnection() throws SQLException
   {
      String url = props.getProperty("jdbc.url");
      String username = props.getProperty("jdbc.username");
      String password = props.getProperty("jdbc.password");

      return DriverManager.getConnection(url, username, password);
   }
}

/**
 * This panel displays the contents of a result set.
 */
class DataPanel extends JPanel
{
   private java.util.List<JTextField> fields;

   /**
    * Constructs the data panel.
    * @param rs the result set whose contents this panel displays
    */
   public DataPanel(RowSet rs) throws SQLException
   {
      fields = new ArrayList<>();
      setLayout(new GridBagLayout());
      GridBagConstraints gbc = new GridBagConstraints();
      gbc.gridwidth = 1;
      gbc.gridheight = 1;

      ResultSetMetaData rsmd = rs.getMetaData();
      for (int i = 1; i <= rsmd.getColumnCount(); i++)
      {
         gbc.gridy = i - 1;

         String columnName = rsmd.getColumnLabel(i);
         gbc.gridx = 0;
         gbc.anchor = GridBagConstraints.EAST;
         add(new JLabel(columnName), gbc);

         int columnWidth = rsmd.getColumnDisplaySize(i);
         JTextField tb = new JTextField(columnWidth);
         if (!rsmd.getColumnClassName(i).equals("java.lang.String"))
            tb.setEditable(false);
               
         fields.add(tb);

         gbc.gridx = 1;
         gbc.anchor = GridBagConstraints.WEST;
         add(tb, gbc);
      }
   }

   /**
    * Shows a database row by populating all text fields with the column values.
    */
   public void showRow(ResultSet rs)
   {
      try
      {
         if (rs == null) return;
         for (int i = 1; i <= fields.size(); i++)
         {
            String field = rs == null ? "" : rs.getString(i);
            JTextField tb = fields.get(i - 1);
            tb.setText(field);
         }
      }
      catch (SQLException ex)
      {
         for (Throwable t : ex)
            t.printStackTrace();
      }
   }
   
   /**
    * Updates changed data into the current row of the row set.
    */
   public void setRow(RowSet rs) throws SQLException
   {
      for (int i = 1; i <= fields.size(); i++)
      {
         String field = rs.getString(i);
         JTextField tb = fields.get(i - 1);
         if (!field.equals(tb.getText()))
            rs.updateString(i, tb.getText());
      }
      rs.updateRow();
   }
}
 

构造函数的解释


readDatabaseProperties()函数的主要作用是给props对象赋初值,然后将jdbc.drivers设置为相应的值(即加载jdbc驱动),getConnection函数建立一个到数据库的连接。

首先,是对主程序的解释,用一个Lambda表达式来作为EventQueue.invokeLater(Runnable runnable)的参数,即重载了该接口的run()函数,该方法的主要目的是创建一个Frame。该Frame的名称为ViewDB,该Frame的默认关闭操作为:JFame.EXIT_ON_CLOSE。然后设置该Frame可见。

接着是对ViewDBFrame的介绍,有4个JButton,为previousButton、nextButton、deleteButton、saveButton。一个私有变量DataPanel(extends JPanel),一个Component scrollPane,一个JComboBox<String> tableNames, 一个Properties props,一个CachedRowSet crs,一个Connection conn。

该ViewDBFrame的构造函数主要目的就是给各个参数赋值,

从元数据对象meta中返回   类型为“Table”的表的相关信息(返回结果的类型为ResultSet),将得到的相关表的信息加载到JcomboBox<E>中,作为这个下拉列表的一个Item。

给下拉列表tableNames加一个事件侦听器,这个事件侦听器的目的是为了展示选中的表,然后将这个下拉列表放在Frame的NORTH位置。

添加一个WindowListener对象,用于定义关闭时的操作。

添加一个JPanel,并将它放在Frame的SOUTH位置。

创建一个标签为“Previous”的按钮,加的侦听器的作用是展示前一行,同理,还有相应按钮的作用是展示下一行,删除行,保存更改。

如果下拉列表中包含的项数不止一项,那么就展示第一行的存储的表的相关信息。


非构造函数函数


public void showTable(String tableName, Connection conn)

准备文本字段来展示一个新的表,或者显示第一行。

参数——要显示的表的名称,以及已经与数据库建立连接的对象

首先是根据表创建一个Statement,然后执行 “SELECT * FROM” + tableName, 从数据库中选出这个表的信息,

将结果复制进一个CachedRowSet,(CachedRowSet 创建的步骤,首先建立一个RowSetFactory,根据RowSetFactory的createCachedRowSet方法来创建)。

根据CachedRowSet创建一个DataPanel

用DataPanel创建一个JScrollPane,将ScrollPane放到Frame的中间

使用函数pack()使此窗口的大小适合其子组件的首选大小和布局。

然后调用showNextRow();


然后是函数showPreviousRow:

 public void showPreviousRow()

首先,检测crs是否为null或者游标是否只在第一行,如果是,就返回,

不是,则调用previous()函数,将游标移动到前一行,再调用dataPanel的showRow()方法。


showNextRow()函数的实质与上面的函数相同,只不过是显示下一行


deleteRow()函数:

 public void deleteRow()

首先检查crs是否为null,是的或,就直接返回,

否则,就调用SwingWorker<Void, Void>(首先创建一个SwingWorker的具体实现,即重写doInBackground()函数,),然后调用excute()函数开始执行。

 

doInBackground()函数中,调用CachedRowSet类的deleteRow()方法,

然后使用CachedRowSet的acceptChanges(Connection)方法来将更改反映到底层数据库。

接着检测删除行后游标是否位于最后一行之后,

是的话再检测能否将游标移到最后一行,不能的话就将crs设置为null。

此函数最终返回null

 

 done函数中:

 public void done()

调用DataPanel的showRow函数。


public void saveChanges()

该函数的基本结构与上面的函数相同。

doInBackground()函数调用了DataOanel的setRow()函数,然后调用acceptChanges()函数。


readDatabaseProperties()函数根据一个.properties文件设置一个Properties对象,然后注册数据库的驱动器。

(感觉这里不是很合理,将注册驱动器的相关代码放到getConnection函数中会更好)


getConnection()函数将得到的url、username、password用于DriverManager的getConnection连接。


接下来是建立的一个DataPanel类(extends JPanel)

这个面板呈现了结果集的内容,

该类的字段为:

List<JTextField> fields


首先是DataPanel的构造函数,

该构造函数的参数为一个RowSet,

将fields设置为一个ArrayList<>,

设置宽度为1,高度为1。

从参数ResultSet得到一个ResultSetMetaData,

接着是一个for循环{

该循环首先设置gridy的值,

使用rs来存储该列的列名。将gridx设置为0;

将anchor设置为GridBagConstraints.EAST,

将一个带有列名的JLacel放在gbc中

创建一个与列最大宽度同宽的JTextField,

如果某一列的类的类型为“java.lang.String”,就将这个JTextField设置为不可编辑。

将这个tb放进fields中,

将gridx设置为1,将anchor设置为WEST,

将tb放进gbc

}


接下来是一个函数showRow

通过用列值填充所有文本字段来显示数据库行

该函数获取rs在第i列的值,采用i-1列tb因为ArrayList的列是从0开始的,而列的值是从1开始的。


最后是一个函数setRow

将更改后的数据更新为行集的当前行

首先检测从行集中得到的值与JTextField是否相等,不相等,则更新为JTextField中的值。

最后调用的是RowSet的updateRowset方法,使用此ResultSet对象的当前行的新内容更新底层数据库

对所有行做检验,但是只会更新当前行


最后来做一个总结,主函数创建一个ViewDBFrame,frame的标题为ViewDB,设置默认的关闭操作是调用System.exit()方法,设置该Frame为visible。

这个Frame上面的功能有查看前一行、查看下一行、删除、保存。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值