【问题标题】:Good way to store JTable data for longterm?长期存储 JTable 数据的好方法?
【发布时间】:2015-09-07 08:06:36
【问题描述】:

在我当前的项目中,我正在使用 JTableDefaultTableModel。到目前为止一切正常,目前为了保存 TableModel,我只需将对象序列化到磁盘。

由于这可能是保存和加载数据的合理方式,我不喜欢数据完全“混淆”/以字节形式存在的事实。如果发生不好的事情,这使得数据几乎无法挽救。另一个问题是序列化 UUID,这使得更新我的程序变得更加困难,并且不会使数据无法加载。

该数据库将多年来不断填充,并将包含重要信息。为了使数据可恢复,我想将 TableModel 解析为 XML 文件,但这失败了,因为 XML Encoder 无法处理 JTable / TableModel。

有哪些建议可以有效地以“明文”形式保存来自JTable 的数据?简单地遍历列和行并将其逐行保存到文本文件中(列数据之间有分隔符)是一种好方法吗?我在这里担心的是用户可能会使用像“:”这样的分隔符(我可能会使用它)作为表数据并使解析器崩溃。

感谢您的帮助。

【问题讨论】:

  • 数据库将是我的第一个建议,可能 XML 或 JSON 作为第二个。您可以查看Introduction to JAXB 之类的内容,这将允许您在数据对象 (POJO) 中定义一系列注释,从而允许 API 对其进行编码/解码
  • 数据库还允许更大的灵活性,例如 SQL 查询、对数据库的更改、其他 DB/Excel 的文本转储等等。您可以使用像 Derby 或 H2 这样的嵌入式数据库。我喜欢@MadProgrammer 提到带有注释的 JAXB,以便为行提供对象类。

标签: java swing jtable defaulttablemodel


【解决方案1】:

什么是保存数据的建议

API 推荐使用 XMLEncoder

我想将 TableModel 解析为 XML 文件,但由于 XML 编码器无法处理 JTable / TableModel,因此失败了。

您需要创建一个自定义编码器。下面给出了 DefaultTableModel 的两种实现。

//  Following code is a more complete version of:
//  http://stackoverflow.com/q/26250939/131872

import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.util.Vector;
import javax.swing.*;
import javax.swing.table.*;

public class DefaultTableModelPersistenceDelegateTest
{
    private File file = new File("TableModel.xml");
    private final JTextArea textArea = new JTextArea();

    private final String[] columnNames = {"Column1", "Column2"};

    private final Object[][] data =
    {
        {"aaa", new Integer(1)},
        {"bbb\u2600", new Integer(2)}
    };

    private DefaultTableModel model = new DefaultTableModel(data, columnNames);
    private final JTable table = new JTable(model);

    public JComponent makeUI()
    {
        model.setColumnCount(5);
        JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        sp.setResizeWeight(.3);
        sp.setTopComponent(new JScrollPane(table));
        sp.setBottomComponent(new JScrollPane(textArea));

        JPanel p = new JPanel();
        p.add(new JButton(new AbstractAction("XMLEncoder")
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                try
                {
                    OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
                    XMLEncoder xe = new XMLEncoder(os);
                    xe.setPersistenceDelegate(DefaultTableModel.class, new DefaultTableModelPersistenceDelegate());
                    xe.writeObject(model);
                    xe.close();

                    Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
                    textArea.read(r, null);
                }
                catch (IOException ex)
                {
                    ex.printStackTrace();
                }
            }
        }));

        p.add(new JButton(new AbstractAction("XMLDecoder")
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                try
                {
                    InputStream is = new BufferedInputStream( new FileInputStream( file ));
                    XMLDecoder xd = new XMLDecoder(is);
                    model = (DefaultTableModel)xd.readObject();
                    table.setModel(model);
                }
                catch (IOException ex)
                {
                    ex.printStackTrace();
                }
            }
        }));

        p.add(new JButton(new AbstractAction("clear")
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                model = new DefaultTableModel();
                table.setModel(model);
            }
        }));

        JPanel pnl = new JPanel(new BorderLayout());
        pnl.add(sp);
        pnl.add(p, BorderLayout.SOUTH);
        return pnl;
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            @Override public void run()
            {
                createAndShowGUI();
            }
        });
    }

    public static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.getContentPane().add(new DefaultTableModelPersistenceDelegateTest().makeUI());
        f.setSize(420, 340);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

//  See following link for more information on Using XMLEncoder:
//  http://www.oracle.com/technetwork/java/persistence4-140124.html

class DefaultTableModelPersistenceDelegate extends DefaultPersistenceDelegate
{
    //  Initially creates an empty DefaultTableModel. The columns are created
    //  and finally each row of data is added to the model.

    @Override
    protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder encoder)
    {
        DefaultTableModel model= (DefaultTableModel)oldInstance;

        //  Create XML to restore the column names

        Vector<String> columnNames = new Vector<String>(model.getColumnCount());

        for (int i = 0; i < model.getColumnCount(); i++)
        {
            columnNames.add( model.getColumnName(i) );
        }

        Object[] columnNamesData = new Object[] { columnNames };
        encoder.writeStatement(new Statement(oldInstance, "setColumnIdentifiers", columnNamesData));

        //  Create XML to restore row data

        Vector row = model.getDataVector();

        for (int i = 0; i < model.getRowCount(); i++)
        {
            Object[] rowData = new Object[] { row.get(i) };
            encoder.writeStatement(new Statement(oldInstance, "addRow", rowData));
        }
    }
}

class DefaultTableModelPersistenceDelegate2 extends DefaultPersistenceDelegate
{
    //  Initially creates a DefaultTableModel with rows and columns. Then the
    //  columns are reset and proper names are used. Finally data is set for each
    //  cell in the model.

    @Override
    protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder encoder)
    {
        super.initialize(type, oldInstance, newInstance, encoder);

        DefaultTableModel model= (DefaultTableModel)oldInstance;

        //  Create XML to restore the column names

        Vector<String> columnNames = new Vector<String>(model.getColumnCount());

        for (int i = 0; i < model.getColumnCount(); i++)
        {
            columnNames.add( model.getColumnName(i) );
        }

        Object[] columnNamesData = new Object[] { columnNames };
        encoder.writeStatement(new Statement(oldInstance, "setColumnIdentifiers", columnNamesData));

        //  Create XML to reset the value of every cell to its value

        for (int row = 0; row < model.getRowCount(); row++)
        {
            for (int col = 0; col < model.getColumnCount(); col++)
            {
                Object[] o = new Object[] {model.getValueAt(row, col), row, col};
                encoder.writeStatement(new Statement(oldInstance, "setValueAt", o));
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    您应该只使用和序列化模型而不是 GUI 对象(由于您的操作方式,序列化可能不是您的最佳选择)。如果您想为此目的存储表“标题”(JTable 列的布局)是其他模型(+/- 表列模型 - 我没有 java 环境)。

    编辑:如果这个项目是旧的或重要的,并且您认为投入时间是值得的,那么也许您应该重新考虑您的模型类?

    【讨论】:

    • 对象序列化并不意味着对象的长期存储,正如 OP 指出的那样,它有很多限制。现在有很多更好的选择,JAXB 只是其中之一
    • 感谢您的回答。当然,我序列化了模型,而不是 GUI 元素。序列化 JTable 不会有任何效果,因为它存储 no 数据。
    猜你喜欢
    • 1970-01-01
    • 2014-03-31
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-10
    • 2012-01-30
    • 1970-01-01
    相关资源
    最近更新 更多