corner imagecorner image FeaturesPluginsDocs & SupportCommunityPartners


Учебный курс по NetBeans CRUD для платформы NetBeans

В этом курсе описана интеграция базы данных MySQL в приложение NetBeans Platform. Вначале рассматривается база данных MySQL, для которой создается класс сущностей. Следует отметить, что эти указания применимы не только для MySQL. Они могут использоваться для любой реляционной базы данных, поддерживаемой средой IDE NetBeans. Затем класс сущностей помещается в оболочку модуля наряду с модулями для связанного компонента JPA JARS.

После того как модули становятся частью приложения, создается новый модуль, который обеспечивает пользовательский интерфейс приложения. Новый модуль предоставляет пользователю древовидную иерархию, отображающую данные из базы данных. Затем создается другой модуль, позволяющий пользователю изменять данные, отображаемые первым модулем. Выделение средства просмотра и средства изменения в отдельные модули позволяет устанавливать различные редакторы для одного средства просмотра, так как различные редакторы могут создаваться внешними производителями как на коммерческой, так и на бесплатной основе. Таким образом, модульная архитектура платформы NetBeans обеспечивает гибкость.

После установки редактора выполняется добавление функций CRUD. Первый компонент "R" ("Read", чтение) обрабатывается описанным выше средством просмотра. Затем выполняется компонент "U" ("Update", обновление), затем – "C" ("Create", создание) и "D" ("Delete", удаление).

Этот курс позволит получить сведения о различных функциях платформы NetBeans, которые способствуют созданию приложений такого рода. Например, в курсе описаны средства UndoRedo.Manager и ExplorerManager, а также компоненты платформы NetBeans Swing, например, TopComponent и BeanTreeView.

Содержание

Содержимое на этой странице относится к среде IDE NetBeans 6.5

Для работы с этим руководством требуется программное обеспечение и ресурсы, перечисленные в следующей таблице.

Программное обеспечение или ресурс Требуемая версия
Среда IDE NetBeans версия 6.5
Комплект для разработчика на языке Java (JDK) Версия 6 или
версия 5

Создаваемое в этом курсе приложение выглядит следующим образом:

Рекомендуется просмотреть серию демо-роликов 10 лучших интерфейсов API NetBeans перед началом работы с этим курсом. Многие используемые в этом курсе понятия более подробно рассматриваются в демо-роликах.


Настройка приложения

Начнем с создания нового приложения платформы NetBeans.

  1. Выберите команду "File > New Project" (Ctrl+Shift+N). В разделе "Categories", выберите параметр "NetBeans Modules". В разделе "Projects", выберите параметр "NetBeans Platform Application". Нажмите кнопку "Next".
  2. На панели "Name and Location" введите текст DBManager в поле "Project Name". Нажмите кнопку "Finish".

Средой IDE будет создан проект DBManager. Проект является контейнером для всех остальных создаваемых модулей.



Интеграция базы данных

Для интеграции базы данных следует создать классы сущностей из базы данных и интегрировать эти классы вместе со связанными файлами JAR в модули, которые входят в состав приложения NetBeans Platform.

Создание классов сущностей

В этом разделе выполняется создание классов сущностей из выбранной базы данных.

  1. Для этого примера выберите или создайте базу данных states, в которой указаны штаты США и их сокращения.

    Возможно также выбрать любую другую базу и применить адаптировать действия к конкретному случаю. Для получения дополнительных сведений об этом действии обратитесь к разделу Подключение к базе данных MySQL.

  2. В среде IDE выберите команду "File" | "New Project", а затем –" "Java" | "Java Class Library" для создания нового проекта библиотеки с именем StatesLibrary.
  3. В окне "Projects" правой кнопкой мыши щелкните проект библиотеки и выберите команду "File" | "New File", а затем – "Persistence" | "Entity Classes from Database". В мастере выберите ссылку "EclipseLink" на этапе создания модуля состояния. Укажите имя demo для пакета, в котором будут созданы классы сущностей.

    После выполнения этого действия просмотрите созданный код и обратите внимание на то, что теперь в папке META-INF располагается файл persistence.xml, а также классы сущностей для всех таблиц:

  4. Создайте библиотеку Java, и файл JAR будет размещен в папке "dist", которую можно просмотреть в окне "Files":

Помещение файла JAR класса сущностей в оболочку модуля

В этом разделе создается новый модуль NetBeans, который помещает созданный в предыдущем разделе файл JAR в новый раздел.

  1. Правой кнопкой мыши щелкните узел "DBManager's Modules" в окне "Projects" и выберите команду "Add New Library".
  2. Выберите файл JAR, созданный в предыдущем подразделе, а затем завершите работу мастера, указав любые значения.

Теперь в новом приложении создан первый пользовательский модуль.

Создание других связанных модулей

В этом разделе создается два новых модуля, которые помещают файлы JAR EclipseLink в оболочку, а также соединитель баз данных JAR.

  1. Выполните те же действия, что и при создании оболочки библиотеки для файла JAR класса сущностей, для файлов JAR EclipseLink, которые расположены в файле распространения GlassFish. При этом убедитесь в том, что используется и файл состояния JAR.

    Используйте сочетание "Ctrl+щелчок" для выбора нескольких файлов JAR, как показано ниже:

    Если разработчику неизвестно, какие файлы нужно включить в оболочку, следует вернуться в созданный ранее проект библиотеки, а затем раскрыть папку "Libraries", в которой будут отображаться необходимые библиотеки.

  2. Затем следует создать еще один модуль оболочки библиотеки для соединителя MySQL JAR, который доступен в каталоге установки среды IDE NetBeans (в папке ide10/modules/ext).

Проектирование пользовательского интерфейса

В этом разделе создается простой прототип пользовательского интерфейса, который предоставляет окно JTextArea для отображения данных, извлеченных из базы данных.

  1. Правой кнопкой мыши щелкните узел "DBManager's Modules" в окне "Projects" и выберите команду "Add New". Создайте новый модуль с именем StatesViewer с базовым кодовым именем org.demo.states.viewer.
  2. В окне "Projects" правой кнопкой мыши щелкните новый модуль и выберите команду "New" | "Window Component". Укажите, что компонент должен быть создан в положении editor и должен открываться при запуске приложения. Установите States в качестве префикса имени класса окна.
  3. Используйте палитру (Ctrl+Shift+8) для перетаскивания JTextArea в новое окно:

  4. Добавьте этот код в конец конструктора TopComponent:
    EntityManager entityManager =  Persistence.createEntityManagerFactory("StatesLibraryPU").createEntityManager();
    Query query = entityManager.createQuery("SELECT c FROM States c");
    List<States> resultList = query.getResultList();
    for (States c : resultList) {
        jTextArea1.append(c.getName() + " (" + c.getAbbrev() + ")" + "\n");
    }

    Так как не установлены зависимости модулей, которые предоставляют объект States и файлы состояния JAR, указанные выше операторы будут отмечены красным подчеркиванием. Это будет устранено в следующем разделе.

  5. Выше приведены ссылки на модуль состояния StatesLibraryPU, имя которого установлено в файле persistence.xml. Кроме того, здесь имеется ссылка на один из классов сущностей с именем States, который находится в модуле классов сущностей. Адаптируйте этот код к конкретной ситуации.

Установка зависимостей

В этом разделе некоторые модули настраиваются на использование кода из других модулей. Это выполняется явным образом путем установки соглашений между связанными модулями. В других случаях при отсутствии строгой модульной архитектуры платформы NetBeans возможно случайное и хаотическое использование кода.

  1. Модуль классов сущностей должен иметь зависимости от модуля MySQL, а также от модуля EclipseLink. Правой кнопкой мыши щелкните модуль StatesLibrary, выберите команду "Properties", а затем перейдите на вкладку "Libraries" для задания зависимостей от двух модулей, необходимых модулю StatesLibrary.
  2. Модуль StatesViewer требует наличия зависимости от модуля EclipseLink, а также от модуля классов сущностей. Правой кнопкой мыши щелкните модуль StatesViewer, выберите команду "Properties", а затем перейдите на вкладку "Libraries" для задания зависимостей от двух модулей, необходимых модулю StatesViewer.
  3. Откройте элемент StatesTopComponent в представлении "Source", правой кнопкой мыши щелкните окно редактора и выберите команду "Fix Imports". Среда IDE теперь может добавлять необходимые операторы импорта, так как модули, содержащие необходимые классы, теперь доступны для компонента StatesTopComponent.

Теперь между модулями приложения установлены соглашения, которые дают возможность управления зависимостями в отдельных частях кода.

Запуск прототипа

В этом разделе будет запущено приложение, чтобы удостовериться в корректности доступа к базе данных.

  1. Запустите сервер базы данных.

  2. Запустите приложение. На экране должно отобразиться следующее изображение:

Таким образом будет создан простой прототип, который будет расширяться в следующем разделе.


Интеграция функциональности CRUD

Чтобы обеспечить функциональность CRUD, которая обеспечивает тесную интеграцию с платформой NetBeans, необходимо реализовать особые шаблоны кода платформы NetBeans. В следующих разделах эти шаблоны рассматриваются более подробно.

Чтение

В этом разделе производится изменение введенного в следующем разделе элемента JTextArea для представления проводника платформы NetBeans. Представления проводника платформы NetBeans являются компонентами Swing, которые обеспечивают улучшенную интеграцию с платформой NetBeans по сравнению со стандартными компонентами Swing. Данные будут представлены в общей иерархической модели, предоставленной классом платформы NetBeans Node, который отображается во всех представлениях проводника платформы NetBeans. В конце данного раздела описан процесс синхронизации представления проводника с окном "Properties" платформы NetBeans.

  1. В компоненте TopComponent удалите элемент JTextArea в представлении "Design" и закомментируйте связанный с ним код в представлении "Source":
    EntityManager entityManager =  Persistence.createEntityManagerFactory("StatesLibraryPU").createEntityManager();
    Query query = entityManager.createQuery("SELECT c FROM States c");
    List<States> resultList = query.getResultList();
    //for (States c : resultList) {
    //    jTextArea1.append(c.getName() + " (" + c.getAbbrev() + ")" + "\n");
    //}
  2. Правой кнопкой мыши щелкните модуль StatesViewer, выберите команду "Properties", а затем перейдите на вкладку "Libraries" для задания зависимостей интерфейса API Nodes и интерфейса API Explorer & Property Sheet.
  3. Затем измените подпись класса для реализации элемента ExplorerManager.Provider:
    final class StatesTopComponent extends TopComponent implements ExplorerManager.Provider
  4. Необходимо переопределить getExplorerManager()

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

    В начале класса следует объявить и инициализировать ExplorerManager:

    private static ExplorerManager em = new ExplorerManager();

    Обратитесь к ролику 10 лучших интерфейсов API NetBeans для получения подробных сведений о приведенном выше коде, в особенности демо-ролик об интерфейсе Nodes API и интерфейсе Explorer & Property Sheet API.

  5. Откройте представление "Design" для TopComponent, правой кнопкой щелкните палитру, выберите команду "Palette Manager" | "Add from JAR". Затем перейдите к элементу org-openide-explorer.jar, который находится в папке platform9/modules внутри каталога установки среды IDE NetBeans. Выберите элемент BeanTreeView и завершите работу мастера. Теперь элемент BeanTreeView должен отображаться на палитре. Перетащите его с палитры в окно.
  6. Создайте элемент Node, моделирующий данные:
    import demo.States;
    import java.util.List;
    import org.openide.nodes.AbstractNode;
    import org.openide.nodes.ChildFactory;
    import org.openide.nodes.Children;
    import org.openide.nodes.Node;
    
    class StateChildFactory extends ChildFactory<States> {
    
        private List<States> resultList;
    
        public StateChildFactory(List<States> resultList) {
    
            this.resultList = resultList;
        }
    
        @Override
        protected boolean createKeys(List<States> list) {
            for (States states : resultList) {
                list.add(states);
            }
            return true;
        }
    
        @Override
        protected Node createNodeForKey(States s) {
            Node node = new AbstractNode(Children.LEAF);
            node.setDisplayName(s.getName());
            node.setShortDescription(s.getAbbrev());
            return node;
        }
    
    }
  7. Повторно выберите компонент StatesTopComponent и используйте ExplorerManager для передачи результата из запроса JPA в элемент Node:
    EntityManager entityManager =  Persistence.createEntityManagerFactory("StatesLibraryPU").createEntityManager();
    Query query = entityManager.createQuery("SELECT c FROM States c");
    List<States> resultList = query.getResultList();
    em.setRootContext(new AbstractNode(Children.create(new StateChildFactory(resultList), true)));
    //for (States c : resultList) {
    //    jTextArea1.append(c.getName() + " (" + c.getAbbrev() + ")" + "\n");
    //}
  8. Запустите приложение. После запуска приложения откройте окно "Properties". Обратите внимание на то, что несмотря на доступность данных, отображаемых в BeanTreeView, элемент BeanTreeView не синхронизирован с окном "Properties", которое открывается с помощью команды "Window" | "Properties". Другими словами, при перемещении по древовидной иерархии в окне "Properties" элементы не отображаются:

  9. Синхронизация окна "Properties" с элементом BeanTreeView осуществляется путем добавления в конструктор элемента TopComponent следующего кода:
    ActionMap map = getActionMap();
    associateLookup(ExplorerUtils.createLookup(em, map));
  10. Здесь элементы TopComponent ActionMap и ExplorerManager добавляются в элемент Lookup TopComponent. Это также приводит к тому, что в окне "Properties" появляется отображаемое имя и текст всплывающей подсказки выбранного элемента Node.

  11. Повторно запустите приложение и обратите внимание на то, что окно "Properties" теперь синхронизировано с представлением проводника:

Теперь данные можно просмотреть в древовидной иерархии, как и в случае с элементом JTree. В то же время возможен переход в другое представление проводника без изменения модели, так как ExplorerManager выполняет роль посредника между моделью и представлением. Кроме того, теперь будет возможна синхронизация представления с окном "Properties".

Обновление

В этом разделе вначале создается редактор. Редактор будет предоставлен новым модулем NetBeans. Таким образом, вначале будет создан новый модуль. Затем в этом модуле будет создан новый элемент TopComponent, содержащий два поля JTextFields (для каждого столбца, который доступен пользователю для редактирования). Модулю средства просмотра необходимо разрешить взаимодействие с модулем редактора. Каждый раз при выборе нового элемента Node в модуле средства просмотра текущий объект States будет добавлен в окно Lookup. В модуле редактора будет осуществляться прослушивание Lookup на появление объектов States. При появлении нового объекта States в Lookup будет выполнено обновление полей JTextFields в редакторе.

Затем поля JTextFields будут синхронизированы с функциями платформы NetBeans "Undo", "Redo" и "Save". Другими словами, при внесении пользователем изменения в поле JTextField существующая функциональность платформы NetBeans должна быть доступной, чтобы обеспечивать поддержку платформы NetBeans, исключающую необходимость создания новых функций. Для этого необходимо использовать элемент UndoRedoManager наряду с элементом SaveCookie.

  1. Создайте новый модуль с именем StatesEditor и базовым кодовым именем org.demo.states.editor.
  2. Правой кнопкой мыши щелкните модуль StatesEditor и выберите команду "New" | "Window Component". Убедитесь в том, что окно настроено на отображение в положении editor и открытие при запуске приложения. На последней панели мастера задайте префикс имени класса "Editor".
  3. Используйте палитру (Ctrl+Shift+8) для добавления двух меток JLabels и двух полей JTextFields в новое окно. Задайте тексты меток "State" и "Abbreviation", а затем установите имена переменных полей JTextFields равными nameField и abbrevField.

    В GUI Builder окно теперь должно выглядеть следующим образом:

  4. Вернитесь к модулю StatesViewer и убедитесь в том, что файл layer.xml указывает на то, что окно будет отображаться в режиме explorer.

    Правой кнопкой мыши щелкните проект и выберите команду "Clean", перейдя в файл layer.xml. Почему? При каждом запуске приложения и его закрытии положения окон сохраняются в пользовательском каталоге. Таким образом, если элемент StatesViewer изначально отображался в режиме editor, он останется в режиме editor до выполнения команды "Clean", которая сбрасывает пользовательский каталог (т.е. удаляет его) и позволяет отображать StatesViewer в положении, установленном в настоящий момент в файле layer.xml.

  5. Следует также убедиться в том, что BeanTreeView в StatesViewer будет растягиваться по горизонтали и вертикали при изменении размера приложения пользователем. Для проверки этого откройте окно, выберите элемент BeanTreeView, а затем нажмите кнопки со стрелками на панели инструментов GUI Builder.

  6. Теперь можно приступить к добавлению кода. Вначале необходимо отобразить выбранный в настоящий момент объект States в редакторе:

    • Вначале настройте модуль StatesViewer таким образом, чтобы текущий объект States добавлялся в окно средства просмотра Lookup при каждом выборе элемента Node. Для этого измените элемент Node, созданный StateChildFactory, для добавления объекта States в окно Lookup следующим образом (обратите внимание на выделенный жирным текст):
      @Override
      protected Node createNodeForKey(States s) {
          Node node = new AbstractNode(Children.LEAF, Lookups.singleton(s));
          node.setDisplayName(s.getName());
          node.setShortDescription(s.getAbbrev());
          return node;
      }
    • Теперь при каждом создании элемента Node, которое выполняется при выборе нового состояния в средстве просмотра, новый объект States добавляется в окно Lookup элемента Node.

    • Теперь следует изменить модуль редактора таким образом, чтобы его окно отслеживало объекты States, добавляемые в окно Lookup. Вначале установите в модуле редактора зависимость от модуля, который предоставляет класс сущностей, а также от модуля, предоставляющего файлы состояния JAR.
    • Затем настройте подпись класса EditorTopComponent для внедрения LookupListener:
      public final class EditorTopComponent extends TopComponent implements LookupListener
    • Переопределите resultChanged таким образом, чтобы поля JTextFields обновлялись при вставке нового объекта States в окно Lookup:
      @Override
      public void resultChanged(LookupEvent lookupEvent) {
          Lookup.Result r = (Lookup.Result) lookupEvent.getSource();
          Collection<States> c = r.allInstances();
          if (!c.isEmpty()) {
              for (States s : c) {
                  nameField.setText(s.getName());
                  abbrevField.setText(s.getAbbrev());
              }
          } else {
              nameField.setText("[no state]");
              abbrevField.setText("[no abbreviation]");
          }
      }

    • После определения LookupListener необходимо добавить его к какому-либо элементу. В данном случае он добавляется к элементу Lookup.Result, полученному из глобального контекста. Глобальный контекст используется в качестве посредника для контекста выбранного элемента Node. Например, если в древовидной иерархии выбрано значение "Missouri", то объект States для значения "Missouri" добавляется в окно Lookup элемента Node. Так как элемент Node является выбранным в настоящее время элементом, объект States для значения "Missouri" становится доступным в глобальном контексте. Это передается в элемент resultChanged и приводит к заполнению текстовых полей.
    • Все описанные операции выполняются, когда элемент LookupListener становится активным при каждом открытии окна редактора, как показано ниже:

      @Override
      public void componentOpened() {
          result = Utilities.actionsGlobalContext().lookupResult(States.class);
          result.addLookupListener(this);
          resultChanged(new LookupEvent(result));
      }
      
      @Override
      public void componentClosed() {
          result.removeLookupListener(this);
          result = null;
      }

      Так как редактор открывается при запуске приложения, элемент LookupListener также доступен при запуске приложения.

    • Затем необходимо следующим образом объявить переменную result в начале класса:
      private Lookup.Result result = null;
    • Запустите приложение повторно и обратите внимание на то, что окно редактора обновляется при выборе нового элемента Node:

      В то же время следует отметить операции, выполняемые при переходе в окно редактора:

      Так как элемент Node больше не является текущим, объект States не находится в глобальном контексте. Это происходит потому, что глобальный контекст выступает посредником для окна Lookup текущего элемента Node. Таким образом, в этом случае глобальный контекст не может использоваться. Вместо этого будет использоваться локальное окно Lookup, предоставленное окном States.

    • Замените строку

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

      следующей строкой

      result = WindowManager.getDefault().findTopComponent("StatesTopComponent").getLookup().lookupResult(States.class);

      Строка "StatesTopComponent" является идентификатором StatesTopComponent, который представляет собой строковую константу, находящуюся в исходном коде элемента StatesTopComponent. Недостатком описанного выше подхода является то, что элемент EditorTopComponent при этом может работать только в том случае, если обнаруживается элемент TopComponent с идентификатором "StatesTopComponent". Это должно явным образом документироваться, чтобы информировать пользователей других редакторов о том, что идентификация TopComponent средства просмотра выполняется именно таким образом. Разработчик также может изменить модель выбора, как описано здесь Тимом Будро.

      При использовании описанных подходов контекст не будет потерян при переходе к элементу EditorTopComponent, как показано ниже:

  7. Теперь следует перейти к функциям "Undo" и "Redo". Необходимо добиться того, чтобы при изменении пользователем одного из полей JTextFields стали доступны кнопки "Undo" и "Redo", а также связанные с ними команды меню "Edit". Для этого платформа NetBeans предоставляет UndoRedo.Manager.

    • Объявите новый элемент UndoRedoManager и создайте его экземпляр в начале элемента EditorTopComponent:
      private UndoRedo.Manager manager = new UndoRedo.Manager();
    • Затем следует переопределить метод getUndoRedo() в компоненте EditorTopComponent:
      @Override
      public UndoRedo getUndoRedo() {
          return manager;
      }
    • В конструкторе элемента EditorTopComponent следует добавить элемент KeyListener в поля JTextFields. Затем добавьте прослушиватели UndoRedoListeners в связанные события, которые необходимо внедрить:
      nameField.addKeyListener(new KeyListener() {
      
          public void keyTyped(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
          }
      
          public void keyPressed(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
          }
      
          public void keyReleased(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
          }
      
      });
      
      abbrevField.addKeyListener(new KeyListener() {
      
          public void keyTyped(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
          }
      
          public void keyPressed(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
          }
      
          public void keyReleased(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
          }
      
      });

    • Запустите приложение и проверьте функции "Undo" и "Redo" в действии с помощью кнопок и команд меню:
    • Функции будут работать ожидаемым образом. Можно изменить прослушиватель KeyListener таким образом, чтобы не ВСЕ клавиши вызывали включение функций "Undo" и "Redo". Например, при нажатии клавиши Enter включение функций "Undo" и "Redo", скорее всего, не будет нужно. Следует соответствующим образом исправить код, чтобы он соответствовал требованиям предприятия.

  8. Необходимо также выполнить интеграцию с функциональностью платформы NetBeans "Save":

    • По умолчанию на панели инструментов платформы NetBeans доступна кнопка "Save All". В рассматриваемой ситуации сохранение "всех" элементов не требуется, так как понятие "все" подразумевает наличие различных документов. Здесь имеется только один "документ", то есть редактор, который используется для всех узлов древовидной иерархии. Удалите кнопку "Save All" и добавьте кнопку "Save". Для этого добавьте следующий код в файл layer модуля StatesEditor:
      <folder name="Toolbars">
          <folder name="File">
              <file name="org-openide-actions-SaveAllAction.instance_hidden"/>
              <file name="org-openide-actions-SaveAction.instance"/>
          </folder>
      </folder>
    • При запуске приложения на панели инструментов отобразится другой значок. Вместо кнопки "Save All" будет доступна кнопка "Save".

    • Установите зависимости от интерфейса API Dialogs и интерфейса API Nodes.
    • Создайте новый элемент Node. Этот узел будет называться "DummyNode", так как функциональность "Save" платформы NetBeans добавляется именно с помощью элемента Node. Для этого создается новый элемент Node, добавляющий новые реализации SaveCookie в набор возможностей. Этот элемент затем устанавливается в качестве активированного элемента Node компонента TopComponent.
      private class DummyNode extends AbstractNode {
      
          SaveCookieImpl impl;
      
          public DummyNode() {
              super(Children.LEAF);
              impl = new SaveCookieImpl();
          }
      
          public void fire(boolean modified) {
              if (modified) {
                  //Если текст изменен,
                  //выполняется внедрение SaveCookie,
                  //и реализация добавляется в элемент cookieset,
                  //который определяет возможности элемента Node,
                  //в этом случае – возможность сохранения:
                  getCookieSet().assign(SaveCookie.class, impl);
              } else {
                  //В противном случае присвоение не выполняется,
                  //а элемент SaveCookie не устанавливается в качестве
                  //одной из возможностей элемента Node:
                  getCookieSet().assign(SaveCookie.class);
              }
          }
      
          private class SaveCookieImpl implements SaveCookie {
      
              public void save() throws IOException {
      
                  Confirmation msg = new NotifyDescriptor.Confirmation("Do you want to save \"" +
                          nameField.getText() + " (" + abbrevField.getText() + ") " + "\"?",
                          NotifyDescriptor.OK_CANCEL_OPTION,
                          NotifyDescriptor.QUESTION_MESSAGE);
      
                  Object result = DialogDisplayer.getDefault().notify(msg);
      
                  //Когда пользователь щелкает кнопку "Yes", подтверждая сохранение,
                  //следует отключить кнопку "Save" и команду меню "Save",
                  //чтобы они могли использоваться только при внесении следующего изменения 
                  //в текстовое поле:
                  if (NotifyDescriptor.YES_OPTION.equals(result)) {
                      fire(false);
                      //Здесь будет добавлен код обработки действия "Save".
                  }
      
              }
          }
      }
    • Объявите элемент Node в начале класса TopComponent:
      private DummyNode dummyNode;
    • Теперь следует добавить его в конструкторе TopComponent к активированным узлам TopComponent:

      setActivatedNodes(new Node[]{dummyNode = new DummyNode()});

    • Следует вызвать изменение в DummyNode при каждом изменении элемента JTextFields, который добавляет реализацию SaveCookie к возможностям активированного элемента Node с именем "DummyNode":
      nameField.addKeyListener(new KeyListener() {
      
          public void keyTyped(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
              dummyNode.fire(true);
          }
      
          public void keyPressed(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
              dummyNode.fire(true);
          }
      
          public void keyReleased(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
              dummyNode.fire(true);
          }
          
      });
      
      abbrevField.addKeyListener(new KeyListener() {
      
          public void keyTyped(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
              dummyNode.fire(true);
          }
      
          public void keyPressed(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
              dummyNode.fire(true);
          }
      
          public void keyReleased(KeyEvent e) {
              nameField.getDocument().addUndoableEditListener(manager);
              abbrevField.getDocument().addUndoableEditListener(manager);
              dummyNode.fire(true);
          }
      
      });
    • Запустите приложение и обратите внимание на включение и отключение кнопки "Save":

    • В настоящий момент при нажатии кнопки "OK" в приведенном выше диалоговом окне не происходит. На следующем этапе будет добавлен код JPA, обрабатывающий состояние изменений.

    • Затем следует добавить код JPA для сохранения изменений. Для этого замените комментарий «//Здесь будет добавлен код обработки действия "Save"». Этот комментарий необходимо заменить следующим кодом.
      EntityManager entityManager = Persistence.createEntityManagerFactory("StatesLibraryPU").createEntityManager();
      entityManager.getTransaction().begin();
      States states = entityManager.find(States.class, s.getId());
      states.setName(nameField.getText());
      states.setAbbrev(abbrevField.getText());
      entityManager.getTransaction().commit();
    • Значение "s" в s.getId() в настоящее время не определено. Повторно определите resultChanged следующим образом после объявления States s; в начале класса. Это позволяет объекту States задавать значение s, которое используется в коде состояния для получения идентификатора текущего объекта States.

      @Override
      public void resultChanged(LookupEvent lookupEvent) {
          Lookup.Result r = (Lookup.Result) lookupEvent.getSource();
          Collection<States> c = r.allInstances();
          if (!c.isEmpty()) {
              for (States states : c) {
                  s = states;
                  nameField.setText(states.getName());
                  abbrevField.setText(states.getAbbrev());
              }
          } else {
              nameField.setText("[no state]");
              abbrevField.setText("[no abbreviation]");
          }
      }

    • Запустите приложение и измените данные. В настоящее время функциональность "Refresh" отсутствует, поэтому для просмотра обновленной информации следует перезапустить приложение. Например, в данном случае древовидная иерархия отображает сохраненное имя штата "Missouri":

  9. Затем следует добавить функцию для обновления средства просмотра States. Разработчик может добавить элемент Timer, который периодически обновляет средство просмотра. В этом примере в узел Root будет добавлена команда меню "Refresh", позволяющая пользователю вручную обновить средство просмотра.

    • В основном пакете модуля StatesViewer необходимо создать новый элемент Node, заменяющий элемент AbstractNode, который в настоящее время используется в качестве корневого элемента нижестоящих элементов средства просмотра. Обратите внимание на то, что действие "Refresh" также привязывается к новому корневому узлу.
      class StatesRootNode extends AbstractNode {
      
          public StatesRootNode(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) {
                  StatesTopComponent.refreshNode();
              }
          }
      
      }
    • Добавьте этот метод в компонент StatesTopComponentдля обновления приложения:
      public static void refreshNode() {
          EntityManager entityManager = Persistence.createEntityManagerFactory("StatesLibraryPU").createEntityManager();
          Query query = entityManager.createQuery("SELECT c FROM States c");
          List<States> resultList = query.getResultList();
          em.setRootContext(new StatesRootNode(Children.create(new StateChildFactory(resultList), true)));
      }
    • Теперь следует заменить приведенный выше код в конструкторе StatesTopComponent на вызов упомянутого элемента. Как видно из выделенной части кода, теперь вместо элемента AbstractNode используется элемент StatesRootNode. Элемент StatesRootNode содержит действие "Refresh", которое вызывает приведенный выше код.

    • Повторно запустите приложение и обратите внимание на то, что появился новый корневой узел с действием "Refresh":

    • Измените данные, сохраните их, выполните действие "Refresh" и убедитесь в том, что средство просмотра обновляется.

В этом разделе был рассмотрен способ обработки изменений полей JTextFields в платформе NetBeans. При изменении текста кнопки платформы NetBeans "Undo" и "Redo" будут включены или отключены. Кроме того, кнопка "Save" также корректно включается и отключается, что позволяет пользователю сохранять измененные данные в базу данных.

Создание

В этом разделе пользователю предоставляется возможность создания новой записи базы данных.

  1. Правой кнопкой мыши щелкните модуль StatesEditor и выберите команду "New" | "Action". Используйте мастер "New Action" для создания нового действия "Always Enabled". Новое действие должно отображаться на панели инструментов и на панели меню.

    На следующем шаге мастера вызовите действие NewAction:

    Убедитесь в наличии значка размером 16 на 16, который необходимо выбрать в мастере, если действие должно выбираться с панели инструментов.

  2. В действии "New" следует открыть компонент TopComponent вместе с пустыми полями JTextFields:
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public final class NewAction implements ActionListener {
    
        public void actionPerformed(ActionEvent e) {
            EditorTopComponent tc = EditorTopComponent.getDefault();
            tc.resetFields();
            tc.open();
            tc.requestActive();
        }
    
    }

    Это действие внедряет класс ActionListener, который привязан к приложению через записи в файле layer. Эти записи создаются мастером "New Action". Представьте себе легкость переноса существующего приложения Swing на платформу NetBeans при наличии возможности использования тех же классов Action, которые использовались в исходном приложении, без необходимости их изменения в соответствии с классами Action, предоставляемыми платформой NetBeans!

  3. В компоненте EditorTopComponent необходимо добавить следующий метод для сброса полей JTextFields и создания нового объекта States:

    public void resetFields() {
        s = new States();
        nameField.setText("");
        abbrevField.setText("");
    }

  4. В элементе SaveCookie следует убедиться в том, что возврат значения null указывает на сохранение новой записи, а не на обновление существующей записи:
    public void save() throws IOException {
    
        Confirmation msg = new NotifyDescriptor.Confirmation("Do you want to save \"" +
                nameField.getText() + " (" + abbrevField.getText() + ") " + "\"?", NotifyDescriptor.OK_CANCEL_OPTION,
                NotifyDescriptor.QUESTION_MESSAGE);
    
        Object result = DialogDisplayer.getDefault().notify(msg);
    
       //Когда пользователь щелкает кнопку "Yes", подтверждая сохранение,
                //следует отключить кнопку "Save" и команду меню "Save",
                //чтобы они могли использоваться только при внесении следующего изменения 
                //в текстовое поле:
        if (NotifyDescriptor.YES_OPTION.equals(result)) {
            fire(false);
            EntityManager entityManager = Persistence.createEntityManagerFactory("StatesLibraryPU").createEntityManager();
            entityManager.getTransaction().begin();
            if (s.getId() != null) {
                States states = entityManager.find(States.class, s.getId());
                states.setName(nameField.getText());
                states.setAbbrev(abbrevField.getText());
                entityManager.getTransaction().commit();
            } else {
                Query query = entityManager.createQuery("SELECT c FROM States c");
                List<States> resultList = query.getResultList();
                s.setId(resultList.size()+1);
                s.setName(nameField.getText());
                s.setAbbrev(abbrevField.getText());
                entityManager.persist(s);
                entityManager.getTransaction().commit();
            }
        }
    
    }
  5. Повторно запустите приложение и добавьте новое состояние в базу данных:

    При обновлении данных новые записи будут добавляться в нижнюю часть списка, так как они сортируются по номеру идентификатора. Так, значение "Disneyland" будет добавлено в конец, а не в алфавитном порядке.

Удаление

В этом разделе пользователю предоставляется возможность удалять выбранную запись в базе данных. С помощью описанных выше приемов и кода внедрите действие "Delete" самостоятельно.

  1. Создайте новое действие DeleteAction. Следует определить необходимость привязки действия к узлу State, к панели инструментов, к меню или ко всем элементам. В зависимости от места привязки следует использовать различные классы платформы NetBeans. Повторно изучите учебный курс, обратив особое внимание на способ создания действия "New" в сравнении с действием корневого узла "Refresh".
  2. Получите текущий объект States, возвратите диалоговое окно "Are you sure?", а затем удалите запись. Для получения дополнительных сведений повторно прочитайте учебный курс, в особенности ту часть, в которой реализуется функция "Save". Вместо сохранения записи теперь производится ее удаление из базы данных.

Связанные демо-ролики

Первый демо-ролик описывает процесс создания представления в платформе NetBeans для базы данных:

Второй демо-ролик объясняет способ создания связанного редактора. (Планируется).

Дополнительная информация

На этом учебный курс по функциям CRUD в платформе NetBeans завершен. В этом документе описано создание нового приложения платформы NetBeans с функциональностью CRUD для определенной базы данных. Дополнительные сведения о создании и разработке приложений приведены в следующих ресурсах:

 
 
loading
Please Confirm