NetBeans Platform CURD应用程序教程

本文参考:

https://github.com/apache/netbeans-website/blob/master/netbeans.apache.org/src/content/tutorials/nbm-crud.asciidoc

https://github.com/apache/netbeans-website/blob/master/netbeans.apache.org/src/content/tutorials/nbm-crud_zh_CN.asciidoc

本教程介绍了如何将 Java DB 数据库集成到 NetBeans 平台应用程序中。我们先从分析 Java DB 数据库入手,将通过这些数据库创建实体类。但请注意,这些说明并非仅适用于 Java DB。对于 NetBeans IDE 所支持的任何关系数据库,这些说明均适用。接下来,我们将把这些实体类和相关 JPA JAR 的模块一起包装到一个模块中。

在以上模块成为应用程序的一部分之后,我们将创建一个为应用程序提供用户界面的新模块。该新模块将为用户提供一个显示数据库中数据的树状分层结构。然后,我们将创建另一个模块,以使用户能够编辑第一个模块显示的数据。我们将使用不同模块将查看器和编辑器分开,以使用户能够为同一查看器安装不同的编辑器,因为外部供应商会创建各种不同的编辑器,有些用于商业用途,有些则是免费提供的。正是 NetBeans 平台的模块化体系结构促成了这种灵活性。

有了编辑器后,我们将开始添加 CRUD 功能。首先,上面所述的查看器将处理代表“读取”的 "R"。接下来,将处理代表“更新”的 "U",然后依次是代表“创建”的 "C" 和代表“删除”的 "D"。

在本教程结束时,您将了解到多种帮助您创建此类应用程序的 NetBeans 平台功能。例如,您将会了解到UndoRedo.Manager 和 ExplorerManager ,以及一些 NetBeans 平台 Swing 组件,如 TopComponent 和 BeanTreeView

建议您在开始学习本教程之前,先观看截屏视频系列 Top 10 NetBeans APIs(最主要的 10 个 NetBeans API)。本教程中提到的许多概念都在该截屏视频系列中进行了详细讨论。

Netbeans IDE 11.3,本文使用的是Postgres SQL数据库

设置应用程序

首先让我们来创建一个新的 NetBeans 平台应用程序。

1、选择“文件”>“新建项目”(Ctrl+Shift+N)。在“类别”下选择“NetBeans 模块”。在“项目”下选择“NetBeans 平台应用程序”。单击“下一步”。

2、在“名称和位置”面板的“项目名称”字段中键入 DBManager 。单击“完成”。

集成数据库

为了集成数据库,您需要从数据库创建实体类,并将这些实体类及其相关的 JAR 一起集成到 NetBeans 平台应用程序所包含的模块中。

创建实体类

在此部分,您将从一个选定的数据库生成实体类。

1、就本示例而言,将使用“服务”窗口连接到 NetBeans IDE 附带的样例数据库:

或者,您可以随意使用任何数据库,并根据特定的用例调整操作步骤。

2、在 IDE 中,选择“文件”|“新建项目”,然后选择 "Java" |“Java 类库”以创建一个名为 UserLibrary的新库项目。

3、在“项目”窗口中,右键单击该库项目,选择“文件”|“新建文件”,然后从“数据库”中选择“持久性”|“实体类”。在向导中,选择数据库和所需的表。此处,我们选择 "user"

package输入demo,点击完成。完成此步骤后,查看生成的代码,注意,在一个名为 META-INF 的文件夹中出现了一个persistence.xml 文件

注意:这里的id映射应该为Integer类型

4、右键项目,点击构建,会生成dist JAR,可在“文件”窗口中查看此文件。

 

将实体类 JAR 包装到模块中

在此部分,将在应用程序中添加第一个模块!新 NetBeans 模块将包装在上一节中创建的 JAR 文件。

1、在“项目”窗口中右键单击 DBManager 的“模块”节点,然后选择“添加新库”。

2、选择上一子部分创建的 JAR 并完成向导。项目名称不变,包输入org.shop.model,点击完成。

 

现在,新应用程序中已包含第一个定制模块,它包装的 JAR 包含实体类和 persistence.xml 文件:

创建其他相关模块

在此部分,将创建两个新模块,用来包装 EclipseLink JAR 和数据库连接器 JAR。

1、添加EclipseLink JAR,JAR包位置在NetBeans-11.3\netbeans\java\modules\ext\eclipselink

v8和v11的名称大同小异

2、添加数据库jdbc JAR,pg的JAR在NetBeans-11.3\netbeans\ide\modules\ext,可自由伸展。

这里有人可能不理解,为什么不用添加依赖的方式,因为后面还有其他模块引用此模块,如果不用引入jar包的形式,那么添加到其他依赖后,所需的JAR会找不到。添加依赖只限于自己模块所用。

设计用户界面

在此部分,将创建一个简单的原型用户界面,此界面提供了一个使用 JTextArea 显示从数据库检索的数据的窗口。

1、在“项目”窗口中右键单击 DBManager 的“模块”节点,然后选择“添加新模块”。创建一个名为 UserViewer的新模块,其代码名称基为 org.shop.ui 。

2、在“项目”窗口中,右键单击该新模块,然后选择“新建”|“窗口组件”。指定editor 创建该窗口组件,并且在应用程序启动时应将其打开。将 User设置为该窗口的类名前缀。

3、使用“组件面板”(Ctrl-Shift-8) 将一个 JTextArea 拖放到新窗口中。

4、在 TopComponent 构造函数的末尾添加以下代码:

        EntityManager entityManager = Persistence.createEntityManagerFactory("UserLibraryPU").createEntityManager();
        Query query = entityManager.createQuery("SELECT c FROM TblUser c");
        List<TblUser> resultList = query.getResultList();
        for (TblUser c : resultList) {
            jTextArea1.append(c.getName() + " (" + c.getCity() + ")" + "\n");
        }

因为您未在提供 TblUser对象和持久性 JAR 的模块上设置依赖关系,将使用表示错误的红色下划线标记上面的语句。此问题将在下一部分中解决。

在上面,您可以看到对一个名为 "UserLibraryPU" 的持久性单元的引用,此名称是在 persistence.xml 文件中设置的。此外,还有一个对名为 TblUser的实体类的引用,该实体类位于实体类模块中。如果不同于上面的内容,请根据需要修改这些代码。

设置依赖关系

在此部分,将使一些模块能够使用其他模块中的代码。通过在相关模块之间设置约定来清楚地执行此操作,即不会随意重复滥用代码(在没有诸如 NetBeans 平台所提供的严格模块化体系结构时容易发生此情况)。

1、右键DBManager属性,选择库,展开列表中的java选项,选择EclipseLink JPA,这样套件下的每个模块都可以添加EclipseLink JPA的依赖包

2、UserViewer模块添加EclipseLink JPA和user model,右键项目属性,点击库,添加依赖,输入Eclipse和user根据提示添加依赖

回到UserTopComponent,右键修复导入,不再出现语法错误。

运行应用

在此部分,将运行该应用程序,以便查看能否正确访问数据库。

1、启动数据库服务器。

2、运行应用程序。您应看到如下所示的内容:

现在,您已具有一个简单原型,它包含的 NetBeans 平台应用程序将显示数据库中的数据,下一节将对其进行扩展。

集成 CRUD 功能

为了创建与 NetBeans 平台顺利集成的 CRUD 功能,需要实现一些特定的 NetBeans 平台编码模式。以下部分详细介绍了这些模式。

读取

在此部分,将针对 NetBeans 平台资源管理器视图更改上一部分中引入的 JTextArea 。NetBeans 平台资源管理器视图是一种 Swing 组件,与标准 Swing 组件相比,此组件与 NetBeans 平台集成的效果更好。它们支持很多功能,其中之一是上下文概念,以便与上下文相关联。

为了表示数据,NetBeans 平台 Node 类将提供一个通用的分层结构模型,此模型可通过任何 NetBeans 平台资源管理器视图显示。此部分末尾说明了如何将资源管理器视图与 NetBeans 平台“属性”窗口进行同步。

1、对于 TopComponent ,在“设计”视图中删除JTextArea ,并在“源”视图中注释掉jTextArea1其相关代码

2、右键单击 UserViewer模块,选择“属性”,选择库,添加依赖Nodes API和Explorer & Property Sheet API。

3、UserTopComponent实现ExplorerManager.Provider

public final class UserTopComponent extends TopComponent implements ExplorerManager.Provider

在类的顶部声明并初始化ExplorerManager 

private static ExplorerManager em = new ExplorerManager();

实现接口方法。返回em

@Override
public ExplorerManager getExplorerManager() {
    return em;
}

有关以上代码的详细信息,请观看 Top 10 NetBeans APIs(最主要的 10 个 NetBeans API)

4、切换到设计视图,在组件面板(palette)鼠标右键,组件面板管理器,添加JAR,找到Netbeans IDE安装目录下的NetBeans-11.3\netbeans\platform\modules\org-openide-explorer.jar,选择BeanTreeView,完成向导。现在,组件面板中可以看到BeanTreeView,将他拖到面板

5、创建一个工厂类,它将为数据库中的每个客户创建一个新的 BeanNode

import demo.TblUser;
import java.util.List;
import org.openide.nodes.BeanNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;

public class UserChildFactory extends ChildFactory<TblUser> {
 
    private List<TblUser> resultList;
 
    public UserChildFactory(List<TblUser> resultList) {
        this.resultList = resultList;
    }
 
    @Override
    protected boolean createKeys(List<TblUser> list) {
        for (TblUser user : resultList) {
            list.add(user);
        }
        return true;
    }
 
    @Override
    protected Node createNodeForKey(TblUser c) {
        try {
            return new BeanNode(c);
        } catch (java.beans.IntrospectionException ex) {
            Exceptions.printStackTrace(ex);
        }
        return null;
    }
}

6、返回到 UserTopComponent,使用 ExplorerManager 将来自 JPA 查询的结果列表传递到 Node :

    public UserTopComponent() {
        initComponents();
        setName(Bundle.CTL_UserTopComponent());
        setToolTipText(Bundle.HINT_UserTopComponent());
        EntityManager entityManager = Persistence.createEntityManagerFactory("UserLibraryPU").createEntityManager();
        Query query = entityManager.createQuery("SELECT c FROM TblUser c");
        List<TblUser> resultList = query.getResultList();
        em.setRootContext(new AbstractNode(Children.create(new UserChildFactory(resultList), true)));
    }

7、运行应用程序,显示一个树形菜单,打开“属性”窗口(Window->IDE Tools->properties),点击左侧树,他们之间并不能同步。

8、通过向 TopComponent 中的构造函数添加以下代码,将“属性”窗口与BeanTreeView 进行同步。

associateLookup(ExplorerUtils.createLookup(em, getActionMap()));

这里我们将 TopComponent 的 ActionMap 和 ExplorerManager 添加到TopComponent 的 Lookup 中。此操作的一个副效应是“属性”窗口开始显示选定Node 的显示名称和工具提示文本。

9、再次运行应用,注意,树形菜单(资源管理器视图)与属性同步了

ExplorerManager 会在模型和视图之间进行协调。最后,您还可以将视图与“属性”窗口进行同步。

更新

在此部分,将首先创建一个编辑器。该编辑器将由一个新的 NetBeans 模块提供。因此,首先需要创建一个新的模块。然后,在新模块中创建一个新的TopComponent ,其中含有两个 JTextField (分别用于允许用户编辑的两个列)。您将需要使查看器模块与编辑器模块进行通信。每当在查看器模块中选择新的 Node 时,都会将当前的 TblUser对象添加到 Lookup 中。在编辑器模块中,将需要侦听 Lookup 以确定是否引入了 TblUser对象。每当将新的 TblUser对象引入到 Lookup 时,都会在编辑器中更新 JTextField 。

接下来,将 JTextField 与 NetBeans 平台的撤销、重做和保存功能进行同步。换言之,当用户更改 JTextField 时,您希望可以使用 NetBeans 平台的现有功能,以便无需创建新功能,即可轻松获得 NetBeans 平台支持。为此,您需要使用UndoRedoManager 和 SaveCookie 。

1、创建一个新模块,命名为UserEditor,包名为org.shop.editor

2、右键点击UserEditor,新建窗口组件-选择editor,启动应用打开,命名为Editor

3、使用“组件面板”(Ctrl-Shift-8) 向新窗口中添加两个 JLabel 和两个JTextField 。将标签的文本设置为 "Name" 和 "City",并将两个JTextField 的变量名称设置为 jTextField1 和 jTextField2 。

4、返回到UserViewer模块并更改其 layer.xml 文件,指定UserTopComponent窗口将以 explorer 模式显示。

在Netbeans v7以后就不会自动创建layer.xml文件了,需要自己创建。右键UserViewer模块,new -> XML Layer,它会在包下创建一个layer.xml,然后配置自动写进文件里。创建完成后,点击模块clean and build,在Important File-->XML Layer会生成系统的组件代码,可以提供参考。我们把UserTopComponent相关的代码拷贝进自己的layer.xml中,把editor改为explorer

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
<filesystem>
    <folder name="Windows2">
        <folder name="Components">
            <file name="UserTopComponent.settings">
                <!--org.shop.ui.UserTopComponent--><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE settings PUBLIC "-//NetBeans//DTD Session settings 1.0//EN" "http://www.netbeans.org/dtds/sessionsettings-1_0.dtd">
<settings version="1.0">
  <instance class="org.shop.ui.UserTopComponent"/>
</settings>
]]></file>
        </folder>
        <folder name="Modes">
            <folder name="editor">
                <file name="UserTopComponent.wstcref">
                    <!--org.shop.ui.UserTopComponent--><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tc-ref PUBLIC "-//NetBeans//DTD Top Component in Mode Properties 2.0//EN" "http://www.netbeans.org/dtds/tc-ref2_0.dtd">
<tc-ref version="2.0">
  <tc-id id="UserTopComponent"/>
  <state opened="true"/>
</tc-ref>
]]></file>
            </folder>
        </folder>
    </folder>
</filesystem>

5、然后重新clean and build 应用程序,运行如图所示

在更改 layer.xml 文件后,右键单击该应用程序项目,然后选择“清理”。为什么要这样做?因为每当运行应用程序并将其关闭后,都会将窗口位置存储到用户目录中。因此,如果 UserViewer 最初以 editor 模式显示,则会一直处于editor 模式,直到执行“清理”操作,此操作会重置用户目录(即,删除用户目录),并使 UserViewer 在 layer.xml 文件中当前设置的位置显示。

6、现在我们可以开始添加一些代码。第一,我们需要在编辑器中显示当前选中的 TblUser对象:

首先调整 UserViewer 模块,以便每当选择了新的 Node 时,都会将当前的 TblUser对象添加到查看器窗口的 Lookup 中。为此,请在UserChildFactory类中创建 AbstractNode ,而不是 BeanNode 。这样,您就可以将当前 TblUser对象添加到该 Node 的 Lookup 中

    @Override
    protected Node createNodeForKey(TblUser c) {
        Node node = new AbstractNode(Children.LEAF, Lookups.singleton(c));
        node.setDisplayName(c.getName());
        node.setShortDescription(c.getCity());
        return node;
//        try { 
//            return new BeanNode(c); 
//        } catch (IntrospectionException ex) { 
//            Exceptions.printStackTrace(ex); 
//            return null; 
//        } 
    }

现在,每当创建新的 Node (当用户在查看器中选择新的客户时即会执行此操作)时,就会将新的 TblUser对象添加到 Node 的 Lookup 中。

  • 现在,我们将通过某种方式来更改编辑器模块,使其窗口最终侦听被添加到Lookup 的 TblUser对象。首先,在编辑器模块中设置对提供实体类的模块以及提供持久性 JAR 的模块的依赖关系。

  • 接下来,更改 EditorTopComponent 类签名以实现 LookupListner

public final class EditorTopComponent extends TopComponent implements LookupListener{

覆盖 resultChanged ,以便在将新的 TblUser对象引入 Lookup 中时,对JTextField 进行更新:

    @Override
    public void resultChanged(LookupEvent lookupEvent) {
        Lookup.Result r = (Lookup.Result) lookupEvent.getSource();
        Collection<TblUser> coll = r.allInstances();
        if (!coll.isEmpty()) {
            for (TblUser user : coll) {
                jTextField1.setText(user.getName());
                jTextField2.setText(user.getCity());
            }
        } else {
            jTextField1.setText("[no name]");
            jTextField2.setText("[no city]");
        }
    }

如果找不到TblUser引入,搜索对应的依赖加入到模块。

现在定义了 LookupListener ,我们需要将其添加到某个对象。这里,我们将其添加到从全局上下文中获取的 Lookup.Result 中。全局上下文将代理选定Node 的上下文。例如,如果在树状分层结构中选择了 "baidu",则会将 "baidu" 的 TblUser对象添加到该 Node 的 Lookup 中,这意味着 "baidu" 的 TblUser对象当前在全局上下文中可用(因为该节点为当前选定的 Node )。随后即会将此对象传递到 resultChanged ,以填充该文本字段。

每当编辑器窗口打开时,便开始执行上述所有操作,即会激活 LookupListener ,如下所示:

    private Lookup.Result result = null;
    @Override
    public void componentOpened() {
        result = Utilities.actionsGlobalContext().lookupResult(TblUser.class);
        result.addLookupListener(this);
        resultChanged(new LookupEvent(result));
    }

    @Override
    public void componentClosed() {
        result.removeLookupListener(this);
        result = null;
    }

由于编辑器窗口会在应用程序启动时打开,因此在应用程序启动时LookupListener 即可用。

再次运行应用程序,注意,每当选择一个新的 Node 时,编辑器窗口即会更新。

但请注意,将焦点切换到其他位置,editor window内容丢失了。

由于该 Node 不再是当前节点,因此 TblUser对象不再位于全局上下文中。这是因为全局上下文代理的是当前 Node 的 Lookup (如上文中所述)。因此,在这种情况下,我们不能使用全局上下文。而应使用 TblUser窗口提供的本地 Lookup 。

将此行

result = Utilities.actionsGlobalContext().lookupResult(TblUser.class);

重写为:

result = WindowManager.getDefault().findTopComponent("UserTopComponent").getLookup().lookupResult(TblUser.class);

字符串 "UserTopComponent" 是 UserTopComponent的 ID,它是一个字符串常量,可以在 UserTopComponent 源代码中找到。上述方法有一个缺点,即, EditorTopComponent 仅在找到 ID 为 "UserTopComponent" 的 TopComponent 时才发挥作用。可通过以下两种方法解决此问题:明确记录此问题,以使其他编辑器的开发人员了解他们需要这样标识查看器 TopComponent ,或者重写该选定模型,如 Tim Boudreau 在此处所述

如果使用上述一种方法,您会发现将焦点切换到 EditorTopComponent 时,上下文并未丢失。

再次运行程序,失去焦点editor内容依然不变。

由于您现在使用的是 AbstractNode 而不是 BeanNode ,“属性”窗口中不会显示任何属性。您需要自行提供这些属性,如 节点 API 教程中所述。

然后,让我们来处理撤销/重做功能。当用户更改某个 JTextField 时,我们希望启用“撤销”按钮、“重做”按钮以及“编辑”菜单中的相关菜单项。为此,NetBeans 平台提供了 UndoRedo.Manager

  • 在 EditorTopComponent 项部声明并实例化一个新的 UndoRedoManager:

private UndoRedo.Manager manager = new UndoRedo.Manager();

接下来,覆盖 EditorTopComponent 中的 getUndoRedo() 方法:

@Override
public UndoRedo getUndoRedo() {
    return manager;
}

在 EditorTopComponent 的构造函数中,向 JTextField 中添加一个 KeyListener ,并在需要实现的相关方法中,添加 UndoRedoListener :

jTextField1.getDocument().addUndoableEditListener(manager);
jTextField2.getDocument().addUndoableEditListener(manager);

运行应用程序并显示运行中的撤销和重做功能,即相关按钮和菜单项。功能将按预期方式运行。您可能需要更改 KeyListener ,以免任何键都可启用撤销/重做功能。例如,当按下 Enter 键时,您可能不希望启用撤销/重做功能。因此,请调整上述代码以满足您的业务需求。

8、接下来,我们需要集成 NetBeans 平台的保存功能。

缺省情况下,NetBeans 平台工具栏中提供了“全部保存”按钮。在当前情况下,我们并不希望保存“全部”,因为“全部”指许多不同的文档。在本例中,只有一个“文档”,即供树状分层结构中所有节点重复使用的编辑器。删除“全部保存”按钮,然后添加“保存”按钮,方法是向 UserEditor模块的层文件中添加以下代码:

<folder name="Toolbars">
    <folder name="File">
        <file name="org-openide-actions-SaveAction.shadow">
            <attr name="originalFile" stringvalue="Actions/System/org-openide-actions-SaveAction.instance"/>
            <attr name="position" intvalue="444"/>
        </file>
        <file name="org-openide-actions-SaveAllAction.shadow_hidden"/>
    </folder>
</folder>

现在运行应用程序时,将在工具栏中看到一个不同的图标。现在我们可以使用“保存”按钮,而非“全部保存”按钮。

 

设置对于对话框 API 和节点 API 的依赖关系。

在 EditorTopComponent 构造函数中添加一个调用,以便在每次检测到更改时触发一个方法(将在下一步骤中定义):

SaveCookie impl;
InstanceContent content;

public EditorTopComponent() {
 
        ...
        ...
        ...
 
        jTextField1.getDocument().addDocumentListener(new DocumentListener() {
            public void insertUpdate(DocumentEvent arg0) {
                fire(true);
            }
            public void removeUpdate(DocumentEvent arg0) {
                fire(true);
            }
            public void changedUpdate(DocumentEvent arg0) {
                fire(true);
            }
        });
 
        jTextField2.getDocument().addDocumentListener(new DocumentListener() {
            public void insertUpdate(DocumentEvent arg0) {
                fire(true);
            }
            public void removeUpdate(DocumentEvent arg0) {
                fire(true);
            }
            public void changedUpdate(DocumentEvent arg0) {
                fire(true);
            }
        });
 
        //Create a new instance of our SaveCookie implementation: 
        impl = new SaveCookieImpl();
 
        //Create a new instance of our dynamic object: 
        content = new InstanceContent();
 
        //Add the dynamic object to the TopComponent Lookup: 
        associateLookup(new AbstractLookup(content));
 
    }
 
    ...
    ...
    ...

 以下是上面提到的两种方法。首先,每当检测到更改,就会触发该方法。每次检测到更改时,就会将节点 API 中的 SaveCookie 实现添加到 InstanceContent 中:

    public void fire(boolean modified) {
        if (modified) {
            //If the text is modified, 
            //we add SaveCookie impl to Lookup: 
            content.add(impl);
        } else {
            //Otherwise, we remove the SaveCookie impl from the lookup: 
            content.remove(impl);
        }
    }
 
    private class SaveCookieImpl implements SaveCookie {
 
        @Override
        public void save() throws IOException {
 
           Confirmation message = new NotifyDescriptor.Confirmation("Do you want to save \""
                    + jTextField1.getText() + " (" + jTextField2.getText() + ")\"?",
                    NotifyDescriptor.OK_CANCEL_OPTION,
                    NotifyDescriptor.QUESTION_MESSAGE);
 
            Object result = DialogDisplayer.getDefault().notify(message);
            //When user clicks "Yes", indicating they really want to save, 
            //we need to disable the Save action, 
            //so that it will only be usable when the next change is made 
            //to the JTextArea: 
            if (NotifyDescriptor.YES_OPTION.equals(result)) {
                fire(false);
                //Implement your save functionality here. 
            }
        }
    }

运行应用程序并注意“保存”按钮的启用/禁用情况。

现在,单击上面对话框中的“确定”时什么也不会发生。在下一个步骤中,我们将添加一些 JPA 代码,用于处理更改的持久性。

接下来,我们会添加 JPA 代码以持久保留更改。要执行此操作,请替换注释 "//Implement your save functionality here."。应使用以下代码替换该注释:

                EntityManager entityManager = Persistence.createEntityManagerFactory("UserLibraryPU").createEntityManager();
                entityManager.getTransaction().begin();
                TblUser c = entityManager.find(TblUser.class, user.getId());
                c.setName(jTextField1.getText());
                c.setCity(jTextField2.getText());
                entityManager.getTransaction().commit();

当前未定义 tblUser.getId() 中的 "tblUser"。请在 resultChanged 中添加下面的粗体行(在类顶部声明 TblUser tblUser; 后),以使当前 TblUser对象设置 tblUser,上面的持久性代码将使用它获取当前 TblUser对象的 ID。

    public void resultChanged(LookupEvent lookupEvent) {
        Lookup.Result r = (Lookup.Result) lookupEvent.getSource();
        Collection<TblUser> coll = r.allInstances();
        if (!coll.isEmpty()) {
            for (TblUser user : coll) {
                tblUser = user;//赋值操作
                jTextField1.setText(user.getName());
                jTextField2.setText(user.getCity());
            }
        } else {
            jTextField1.setText("[no name]");
            jTextField2.setText("[no city]");
        }
    }

运行应用程序并更改一些数据。目前,没有“刷新”功能(将在下一步中添加),因此,要查看更改的数据,请重新启动应用程序。例如,j将tencent修改为tencent1,点击保存,查看数据库,发现值被修改了。

9、第四,我们需要添加刷新客户查看器的功能。您可能希望添加一个定期刷新查看器的 Timer 。而在此例中,我们将向根节点添加一个“刷新”菜单项,以便用户可以手动刷新查看器。

  • 在 UserViewer模块的主包中,创建一个新的 Node ,用于替换在查看器中用作子对象根的AbstractNode 。注意,我们还会将一个“刷新”操作绑定到新的根节点。

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;

public class UserRootNode extends AbstractNode {
 
    public UserRootNode(Children kids) {
        super(kids);
        setDisplayName("Root");
    }
 
    @Override
    public Action[] getActions(boolean context) {
        Action[] result = new Action[]{
            new RefreshAction()};
        return result;
    }
 
    private final class RefreshAction extends AbstractAction {
 
        public RefreshAction() {
            putValue(Action.NAME, "Refresh");
        }
 
        public void actionPerformed(ActionEvent e) {
            UserTopComponent.refreshNode();
        }

    }
}

将以下方法添加到 UserTopComponent,用于刷新视图:

    public static void refreshNode() {
        EntityManager entityManager = Persistence.createEntityManagerFactory("UserLibraryPU").createEntityManager();
        Query query = entityManager.createQuery("SELECT c FROM TblUser c");
        List<TblUser> resultList = query.getResultList();
        em.setRootContext(new UserRootNode(Children.create(new UserChildFactory(resultList), true)));
    }

//UserTopComponent构造方法中的em.setRootContext也需要一起改

现在,用对以上方法的调用替换 UserTopComponent构造函数中的以上代码。在上面的突出显示部分,我们可以看到现在使用的是 UserRootNode,而不是 AbstractNode 。 UserRootNode包括“刷新”操作,此操作将调用以上代码。

  • 在保存功能中,添加对上述方法的调用,以便每次保存数据时,都会自动进行刷新。可以使用不同方法为保存功能实现此扩展。例如,您可能希望创建一个包含刷新操作的新模块。然后,在查看器模块和编辑器模块之间共享该模块,以便为两者提供相同的功能。

  • 再次运行应用程序,注意,您拥有了一个新的根节点,其中带有“刷新”操作。

更改一些数据并保存,调用“刷新”操作,注意,将更新查看器。

现在,您已学会了如何让 NetBeans 平台处理对 JTextField 所做的更改。当文本发生更改时,即会启用或禁用 NetBeans 平台的“撤销”和“重做”按钮。此外,还会正确启用和禁用“保存”按钮,让用户将更改的数据保存到数据库。

创建

在此部分,将允许用户在数据库中创建一个新的条目。

1、右键单击 UserEditor模块,然后选择“新建操作”。使用“新建操作”向导创建一个新的“始终启用”操作。新的Action应该在显示在工具栏和/或菜单栏中的任意位置。在向导的下一步中,调用操作 NewAction 。

确保有一个 16x16 的图标,当希望从工具栏调用此操作时,向导将强制选择此图标。

2、在新建操作中,使 TopComponent 处于打开状态,并使 JTextField 保留空白:

    @Override
    public void actionPerformed(ActionEvent e) {        
//        EditorTopComponent tc = EditorTopComponent.getDefault();
        EditorTopComponent tc = (EditorTopComponent)WindowManager.getDefault().findTopComponent("EditorTopComponent");
        tc.resetFields();
        tc.open();
        tc.requestActive();
    }

此操作将实现 ActionListener 类,此类通过层文件中的条目绑定到应用程序,并由“新建操作”向导在此处生成。设想一下将现有的 Swing 应用程序移植到 NetBeans 平台会有多么容易,因为您将可以使用与原始应用程序中相同的 Action 类,而无需重写这些类以符合 NetBeans 平台提供的 Action 类的标准!

在 EditorTopComponent 中,添加以下方法以重置 JTextField 并创建新的 TblUser对象:

    public void resetFields() {
        tblUser = new TblUser();
        jTextField1.setText("");
        jTextField2.setText("");
    }

3、在 SaveCookie 中,确保返回的 null 表示已保存新条目,而非更新了现有条目:

        @Override
        public void save() throws IOException {

            Confirmation message = new NotifyDescriptor.Confirmation("Do you want to save \""
                    + jTextField1.getText() + " (" + jTextField2.getText() + ")\"?",
                    NotifyDescriptor.OK_CANCEL_OPTION,
                    NotifyDescriptor.QUESTION_MESSAGE);

            Object result = DialogDisplayer.getDefault().notify(message);
            //When user clicks "Yes", indicating they really want to save, 
            //we need to disable the Save action, 
            //so that it will only be usable when the next change is made 
            //to the JTextArea: 
            if (NotifyDescriptor.YES_OPTION.equals(result)) {
                fire(false);
                //Implement your save functionality here. 
                EntityManager entityManager = Persistence.createEntityManagerFactory("UserLibraryPU").createEntityManager();
                entityManager.getTransaction().begin();

                if (tblUser.getId() != null) {
                    TblUser user = entityManager.find(TblUser.class, tblUser.getId());
                    user.setName(jTextField1.getText());
                    user.setCity(jTextField2.getText());
                    entityManager.getTransaction().commit();
                } else {
                    Query query = entityManager.createQuery("SELECT c FROM TblUser c");
                    List<TblUser> resultList = query.getResultList();
                    tblUser.setId(resultList.size() + 1);
                    tblUser.setName(jTextField1.getText());
                    tblUser.setCity(jTextField2.getText());
                    //add more fields that will populate all the other columns in the table! 
                    entityManager.persist(tblUser);
                    entityManager.getTransaction().commit();
                }
            }
        }

4、再次运行应用程序,并向数据库中添加一个新user

 

删除

在此部分,将使用户能够删除数据库中选定的条目。使用上面介绍的概念和代码,自己实现“删除”操作。

  1. 创建一个新的操作 DeleteAction 。确定要将其绑定到 "TblUser" 节点,还是绑定到工具栏、菜单栏、快捷键或上述内容的组合。根据要绑定到的位置,您需要在代码中使用不同的方法。再次阅读教程以获取帮助,特别参见如何创建“新建”操作的部分,并将其与根节点上的“刷新”操作进行比较。

  1. 获取当前 TblUser对象,返回“您是否确定?”对话框,然后删除该条目。有关此阶段的相关帮助,请再次阅读教程,重点查看实现“保存”功能的部分。现在不是保存,而是从数据库中删除条目。

注:如果Persistence.createEntityManagerFactory("UserLibraryPU").createEntityManager();这行代码空指针

解决办法:因为UserLibrary没有引入pg的jar

如果出现类型问题,bulid和clean也不起作用

解决办法,修改UserLibrary库,重新build,然后删除UserLibrary模块,再新建UserLibrary引入UserLibrary库

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值