Tutorial do aplicativo CRUD para a plataforma NetBeans
Este tutorial mostra como integrar um banco de dados MySQL com um aplicativo da plataforma Netbeans. Começamos por explorar um banco de dados MySQL, para o qual criamos uma classe de entidade. No entanto, observe que estas instruções não são aplicáveis somente ao MySQL. Ao invés disso, elas são relevantes para qualquer banco de dados relacional suportado pelo NetBeans IDE. A seguir, colocamos a classe da entidade em um módulo, junto com os módulos dos JPA JARS relacionados.
Uma vez que os módulos acima fizerem parte do nosso aplicativo, criamos um novo módulo que fornece a interface do usuário para o nosso aplicativo. O novo módulo fornece ao usuário uma hierarquia em árvore mostrando os dados do banco de dados. Nós então criamos outro módulo que permite que o usuário edite os dados exibidos pelo primeiro módulo. Ao separar o visualizador do editor em módulos distintos, permitimos que o usuário instale um editor diferente para o mesmo visualizador, já que editores diferentes poderiam ser criados por fornecedores externos, alguns comercialmente e alguns sem custo. É esta a flexibilidade que a arquitetura modular da plataforma NetBeans torna possível.
Uma vez que tivermos um editor, começamos a adicionar a funcionalidade CRUD (Create, Read, Update, Delete). Primeiro, o "R" que indica "Read" (ler) é manipulado pelo visualizador acima descrito. A seguir, é manipulado o "U" para "Update" (atualizar), seguido pelo "C" para "Create" (criar) e pelo "D" para "Delete" (excluir).
Ao final do tutorial, você terá aprendido sobre a gama de recursos da plataforma NetBeans que lhe ajudam na criação de aplicativos deste tipo. Por exemplo, você aprendeu sobre o UndoRedo.Manager e o ExplorerManager, assim como sobre os componentes Swing da plataforma NetBeans, tais como TopComponent e BeanTreeView.
Conteúdo
- Configurando o aplicativo
- Integrando o banco de dados
- Integrando a funcionalidade CRUD
- Apresentações na tela relacionadas
Para seguir este tutorial, você preciso dos softwares e recursos listados na tabela seguinte.
| Software ou recurso | Versão necessária |
|---|---|
| NetBeans IDE | versão 6.5 |
| Java Developer Kit (JDK) | Versão 6 ou versão 5 |
O aplicativo que você criará neste tutorial terá esta aparência:

É aconselhável assistir à série de screencasts As 10 principais APIs do NetBeans antes de começar a trabalhar neste tutorial. Muitos dos conceitos tratados neste tutorial são discutidos em maior detalhe dentro da série de screencasts.
Configurando o aplicativo
Vamos começar por criar um novo aplicativo NetBeans.
- Escolha Arquivo > Novo projeto (Ctrl-Shift-N). Em Categorias, selecione Módulos do NetBeans. Em Projetos, selecione Aplicativo da plataforma NetBeans. Clique em Próximo.
- No painel Nome e localização, digite DBManager no campo Nome do projeto. Clique em Terminar.
O IDE cria o projeto DBManager. O projeto é um contêiner para todos os outros módulos que você criará.
Integrando o banco de dados
Para poder integrar o banco de dados, é preciso criar classes de entidades de seu banco de dados e integrar estas classes de entidades, junto com seus JARs relacionados, com os módulos que fazem parte de seu aplicativo da plataforma NetBeans.
Criando as classes de entidade
Nesta seção, você gera classes de entidade de um banco de dados selecionado.
Para o propósito deste exemplo, escolha ou crie um banco de dados "estados", listando os estados dos EUA, junto com suas abreviaturas.
Como alternativa, use qualquer banco de dados que desejar e adapte as etapas que se seguem para seu caso particular. Para obter ajuda para esta etapa, consulte Conectando ao meu banco de dados MySQL.
- No IDE, escolha Arquivo | Novo projeto, seguido por Java | Biblioteca de classes Java, para criar uma nova biblioteca denominada StatesLibrary.
Na janela Projetos, clique com o botão direito do mouse no projeto da biblioteca e escolha Arquivo | Novo arquivo, seguido por Persistência | Classes de entidade do banco de dados. No assistente, selecione EclipseLink na etapa na qual você usa o assistente para gerar a unidade de persistência. Especifique "demo" como o nome do pacote no qual as classes de entidade serão geradas.
Após ter completado esta etapa, examine o código gerado e observe que, entre outras coisas, você agora tem um arquivo persistence.xml em uma pasta denominada META-INF, assim como as classes de entidade para cada uma de suas tabelas:
Construa a biblioteca Java e você terá um arquivo JAR na pasta "dist" do projeto da biblioteca, a qual poderá visualizar na janela Arquivos:
Colocando a classe da entidade JAR em um módulo
Nesta seção, você cria um novo módulo NetBeans que conterá o arquivo JSR criado na seção anterior.
- Clique com o botão direito do mouse no nó do módulo DBManager na janela Projetos e escolha Adicionar nova biblioteca.
- Selecione o JAR criado na subsseção anterior e complete o assistente, especificando quaisquer valores que desejar.
Você agora tem seu primeiro módulo personalizado no novo aplicativo:
Criando outros módulos relacionados
Nesta seção, você cria dois novos módulos, colocando o EclipseLinks JARs, assim como o JAR do conector do banco de dados.
Faça o mesmo que fez ao criar o empacotador de bibliotecas para a classe de entidade JAR, mas desta vez para os EclipseLink JARs, que estão em sua distribuição Glassfish, e assegure de incluir o JAR de persistência também encontrado aqui.
Use Ctrl-clique para selecionar múltiplos JARs, como ilustrado aqui:

Caso não saiba quais devem ser incluídos, volte para o projeto da biblioteca criado anteriormente e, a seguir, expanda a pasta Bibliotecas, que mostrará quais bibliotecas são necessárias.
- A seguir, você cria outro módulo de empacotador de bibliotecas, desta vez para o JAR de conector de banco de dados, que está disponível na pasta de instalação do NetBeans, dentro da pasta ide10/modules/ext.
Projetando a interface do usuário
Nesta seção, você cria um protótipo simples de interface do usuário, fornecendo uma janela que usa uma JTextArea para exibir os dados recuperados do banco de dados.
- Clique com o botão direito do mouse no nó do módulo DBManager na janela Projetos e escolha Adicionar novo. Crie um novo módulo denominado StatesViewer, com a base de nome de código org.demo.states.viewer.
- Na janela Projetos, clique com o botão direito do mouse no novo módulo e escolha Novo | Componente de janela. Especifique que ele deve ser criado na posição do editor e que deve ser aberto quando o aplicativo for iniciado. Defina States como o prefixo do nome da classe da janela.
Use a paleta (Ctrl-Shift-8) para arrastar e soltar uma JTextArea na nova janela:

- Adicione isto no fim do construtor 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"); }Como você não definiu dependências no módulo que fornece o objeto States e as JARS de persistência, as declarações acima serão marcadas com linhas sublinhadas em vermelho indicando o erro. Estes serão corrigidos na seção que segue.
Acima, você pode ver as referências à unidade de persistência denominada "StatesLibraryPU", que é o mesmo nome definido no arquivo persistence.xml. Além disso, há uma referência a uma das classes de entidade, denominada States, que está no módulo de classes de entidade. Adapte estas partes a suas necessidades.
Configurando as dependências
Nesta seção, você habilita alguns dos módulos para usar o código de alguns dos outros módulos. Você faz isso de forma bem explícita ao definir contratos intencionais entre módulos relacionados, ou seja, o oposto à reutilização acidental e caótica do código, que tende a acontecer quando não há uma arquitetura modular estrita, como a fornecida pela plataforma NetBeans.
- O módulo de classes de entidade precisa ter dependências no módulo MySQL, assim como no módulo EclipseLink. Clique com o botão direito do mouse no módulo StatesLibrary, escolha Propriedades e use a aba Bibliotecas para definir as dependências nos dois módulos, que o módulo StatesLibrary necessita.
- O módulo StatesViewer precisa de uma dependência no módulo EclipseLink, assim como no módulo de classes de entidade. Clique com o botão direito do mouse no módulo StatesLibrary , escolha Propriedades e use a aba Bibliotecas para definir as dependências nos dois módulos, que o módulo StatesViewer necessita.
- Abra o StatesTopComponent na visualização Código-fonte, clique com o botão direito do mouse no editor e escolha "Corrigir importações". O IDE agora é capaz de adicionar as declarações importadas, porque os módulos que fornecem as classes necessárias agora estão disponíveis no StatesTopComponent.
Você agora definiu os contratos entre os módulos em seu aplicativo, fornecendo-lhe o controle sobre as dependências entre as distintas partes do código.
Executando o protótipo
Nesta seção, você executa o aplicativo para que possa ver se está acessando corretamente seu banco de dados.
- Inicie seu servidor de banco de dados.
- Execute o aplicativo. O seguinte deverá ser exibido:

Você agora tem um protótipo simples, que estenderá na próxima seção.
Integrando a funcionalidade CRUD
Para poder criar a funcionalidade CRUD que se integra com suavidade à plataforma NetBeans, alguns padrões bem específicos de codificação da plataforma NetBeans precisam ser implementados. A seção que segue descreve estes padrões em detalhes.
Ler
Nesta seção, você altera a JTextArea, apresentada na seção anterior, para uma visualização do explorador da plataforma NetBeans. As visualizações do explorador da plataforma NetBeans são componentes Swing que se integram melhor com a plataforma NetBeans do que os componentes padrão do Swing. Representando seus dados, você terá um modelo hierárquico genérico fornecido por uma classe Node da plataforma NetBeans, que pode ser exibido por quaisquer das visualizações do explorador da plataforma NetBeans. Esta seção termina com uma explicação de como sincronizar as visualizações do explorador com a janela Propriedades da plataforma Netbeans.
- Em seu TopComponent, exclua a JTextArea na visualização Desenho e comente seu código relacionado na visualização Código-fonte:
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"); //} - Clique com o botão direito do mouse no módulo StatesViewer , escolha Propriedades e use a aba Bibliotecas para definir as dependências nos nós da API e a API do explorador e da folha de propriedades.
- A seguir, altere a assinatura da classe para implementar o ExplorerManager.Provider:
final class StatesTopComponent extends TopComponent implements ExplorerManager.Provider
- Alterne para a visualização Desenho do TopComponent , clique com o botão direito do mouse na paleta, escolha Gerenciador de paleta | Adicionar do JAR. A seguir, vá a org-openide-explorer.jar, que está na pasta platform9/modules, dentro do diretório de instalação do NetBeans IDE. Feche a BeanTreeView e complete o assistente. Agora você deveria ver a BeanTreeView na paleta. Arraste-a da paleta e solte-a na janela.
- Crie um Node que modela seus dados:
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; } } - De volta ao StatesTopComponent, use o ExplorerManager para passar a lista de resultados da consulta JPA para o 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"); //} Execute o aplicativo. Quando o aplicativo estiver sendo executado abra a janela Propriedades. Observe que embora os dados estejam disponíveis, exibidos em uma BeanTreeView, a BeanTreeView não está sincronizada com a janela Propriedades, que está disponível através de Janela | Propriedades. Em outras palavras, nada é exibido na janela Propriedades quando você move acima e abaixo na hierarquia da árvore:

- Sincronize a janela Propriedades com BeanTreeView ao adicionar o seguinte construtor no TopComponent:
ActionMap map = getActionMap(); associateLookup(ExplorerUtils.createLookup(em, map));
Execute novamente o aplicativo e observe que a janela Propriedades agora está sincronizada com a visualização do explorador:

Será necessário sobrepor o getExplorerManager()
@Override
public ExplorerManager getExplorerManager() {
return em;
}
Acima da classe, declare e inicialize o ExplorerManager:
private static ExplorerManager em = new ExplorerManager();
Assista As 10 principais APIs do NetBeans para obter os detalhes para o código acima, especialmente o screencast que trata dos nós da API e da API do explorador e da folha de propriedades.
Aqui nós adicionamos o ExplorerManager e o ActionMap do TopComponent ao Lookup do TopComponent. Uma consequência disso, é que a janela Propriedades começa a exibir o nome e o texto da dica de ferramenta do Node selecionado.
Agora você pode visualizar seus dados na hierarquia da árvore, assim como deveria ser capaz de fazer com um JTree. No entanto, também é possível alternar entre diferentes visualizações do explorador sem a necessidade de alterar nada no modelo porque o ExplorerManager faz a mediação entre o modelo e a visualização. Finalmente, agora também é possível sincronizar a visualização com a janela Propriedades.
Atualizar
Nesta seção, primeiro você cria um editor. O editor será fornecido por um novo módulo do NetBeans. Portanto, primeiro você criará um novo módulo. A seguir, dentro daquele novo módulo, criará um novo TopComponent, contendo dois JTextFields, em cada uma das colunas que deseja permitir que sejam editadas pelo usuário. Será necessário deixar que o módulo do visualizador se comunique com o módulo do editor. Sempre que um novo Node for selecionado no módulo do visualizador, você adicionará o objeto States atual no Lookup. No módulo do editor, você ouvirá o Lookup para a apresentação dos objetos States. Sempre que um novo objeto States for introduzido no Lookup, você atualizará o JTextFields no editor.
A seguir, você sincronizará seu JTextFields com a funcionalidade de Desfazer, Refazer e Salvar da plataforma NetBeans. Em outras palavras, quando o usuário faz alterações em um JTextField, você deseja que a funcionalidade existente da plataforma NetBeans se torne disponível para que, em vez de ter que criar uma nova funcionalidade, você tenha somente que utilizar o suporte da plataforma NetBeans. Para este fim, será necessário usar o UndoRedoManager junto com o SaveCookie.
- Crie um novo módulo denominado StatesEditor com o org.demo.states.viewer como a base do nome do código.
- Clique com o botão direito do mouse no módulo StatesEditor e escolha Novo | Componente de janela. Certifique-se de especificar que a janela deve aparecer na posição do editor e que deve abrir quando o aplicativo for iniciado. No painel final do assistente, defina "Editor" como o prefixo do nome da classe.
Use a paleta (Ctrl-Shift-8) para adicionar dois JLabels e dois JTextFields na nova janela. Defina os textos dos rótulo como "Estado" e "Abreviatura" e defina os nomes da variáveis dos dois JTextFields como nameField e abbrevField.
No construtor de GUI, a janela agora deve se parecer com a figura seguinte:

- Volte para o módulo StatesViewer e certifique-se de que o arquivo layer.xml especifique que a janela aparecerá no modo explorer.
Clique com o botão direito do mouse no projeto do aplicativo e escolha "Limpar", após alterar o arquivo layer.xml. Porquê? Porque sempre que você executa o aplicativo e o fecha, as posições da janela são armazenadas no diretório do usuário. Portanto, se o StatesViewer foi inicialmente exibido no modo editor, ele permanecerá no modo editor, até que você faça "Limpar", portanto, redefinindo o diretório do usuário (ou seja, excluindo o diretório do usuário) e permitindo que o StatesViewer seja exibido na posição definida atualmente no arquivo layer.xml.
- Agora podemos começar a adicionar alguns códigos. Primeiramente, precisamos mostrar no editor o objeto States no momento selecionado:
- Inicie por adaptar o módulo StatesViewer para que o objeto States atual seja adicionado na janela Lookup do visualizador, sempre que um novo Node for selecionado. Para isso, altere o Node criado pelo StateChildFactory para que o objeto States atual seja adicionado no Lookup, como segue (observe a parte em negrito):
@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; } - Agora vamos alterar o módulo do editor de tal forma que sua janela detecte os objetos States que são adicionados ao Lookup. Primeiro, defina uma dependência no módulo do editor que fornece a classe da entidade, bem como o módulo que fornece os JARS de persistência.
- A seguir, altere a assinatura da classe EditorTopComponente para implementar o LookupListener:
public final class EditorTopComponent extends TopComponent implements LookupListener
- Sobreponha o resultChanged para que os JTextFields sejam atualizados sempre que um novo objeto States seja introduzido no 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]"); } } - Agora que o LookupListener está definido, precisamos adicioná-lo a algo. Aqui, nós o adicionamos ao Lookup.Result obtido do contexto global. Os proxies do contexto global do contexto do Node selecionado. Por exemplo, se "Missouri" for selecionado na hierarquia da árvore, o objeto States de "Missouri" é adicionado ao Lookup do Node que, por ser o Node atualmente selecionado, significa que o objeto States de "Missouri" agora está disponível no contexto global. Isso é, então, passado para o resultChanged, fazendo com que os campos de texto sejam preenchidos.
Agora, sempre que um novo Node for criado, o que acontece sempre que o usuário seleciona um novo estado no visualizador, um novo objeto States é adicionado no Lookup do Node.
Todo o acima começa a acontecer, ou seja, o LookupListener se torna ativo sempre que a janela do editor for aberta, como pode ser visto abaixo:
@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; } - Inicie por adaptar o módulo StatesViewer para que o objeto States atual seja adicionado na janela Lookup do visualizador, sempre que um novo Node for selecionado. Para isso, altere o Node criado pelo StateChildFactory para que o objeto States atual seja adicionado no Lookup, como segue (observe a parte em negrito):
- Finalmente, declare a variável do resultado acima da classe, como segue:
private Lookup.Result result = null;
Execute novamente o aplicativo e observe que a janela do editor é atualizada sempre que você seleciona um novo Node:

No entanto, observe o que acontece quando você alterna para a janela do editor:

Como o Node não é mais o atual, o objeto States não está mais no contexto global. Isto se deve, conforme mostrado acima, aos proxies do contexto global do Lookup do Node atual. Portanto, neste caso, não podemos usar o contexto global. Ao invés disso, usaremos o Lookup local fornecido pela janela Estados.
- Em segundo lugar, vamos trabalhar na funcionalidade Desfazer/Refazer. O que gostaríamos que acontecesse é que sempre que o usuário fizer uma alteração em um dos JTextFields, o botão "Desfazer" e o botão "Refazer", assim como os itens de menu relacionados no meu Editar, se tornem habilitados. Para este fim, a plataforma NetBeans torna o UndoRedo.Manager disponível.
- Declare e crie uma instância de um novo UndoRedoManager acima do EditorTopComponent:
private UndoRedo.Manager manager = new UndoRedo.Manager();
- A seguir, sobreponha o método getUndoRedo() no EditorTopComponent:
@Override public UndoRedo getUndoRedo() { return manager; } - No construtor do EditorTopComponent, adicione um KeyListener ao JTextFields e, dentro dos métodos relacionados que você precisa implementar, adicione o 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); } }); - Execute o aplicativo e mostre a funcionalidade Desfazer e Refazer em ação, os botões e também os itens de menu.

A funcionalidade funciona exatamente como esperado. Você pode desejar alterar o KeyListener para que nem TODAS as teclas causem a habilitação da funcionalidade Desfazer/Refazer. Por exemplo, quando Enter é pressionado, você provavelmente não deseja que a funcionalidade Desfazer/Refazer se torne disponível. Portanto, adapte o código acima para se adequar às suas necessidades administrativas.
- Declare e crie uma instância de um novo UndoRedoManager acima do EditorTopComponent:
- Em terceiro lugar, precisamos integrar com a funcionalidade Salvar do NetBeans.
- Por padrão, o botão "Salvar todos" está disponível na barra de ferramentas da plataforma NetBeans. Em nosso cenário atual, não desejamos salvar "todos", por que "todos" se refere a vários documentos. Em nosso caso, temos somente um "documento", que é o editor que estamos reutilizando para todos os nós na hierarquia da árvore. Remova o botão "Salvar todos" e adicione "Salvar" em seu lugar, ao adicionar o seguinte arquivo de camada do módulo StatesEditor:
<folder name="Toolbars"> <folder name="File"> <file name="org-openide-actions-SaveAllAction.instance_hidden"/> <file name="org-openide-actions-SaveAction.instance"/> </folder> </folder> - Defina as dependências na API das caixas de diálogo e na API dos nós.
- Crie um novo Node. Vamos denominá-lo "DummyNode", porque é apenas um Node que adiciona a funcionalidade Salvar ao aplicativo da plataforma NetBeans, isto é, ao criar um novo Node, um que adicione novas implementações do SaveCookie ao seu conjunto de capacidades, o qual é então definido como o Node ativado do TopComponent.
private class DummyNode extends AbstractNode { SaveCookieImpl impl; public DummyNode() { super(Children.LEAF); impl = new SaveCookieImpl(); } public void fire(boolean modified) { if (modified) { //Se o for modificado, //implementamos SaveCookie, //e adicionamos a implementação ao conjunto de cookies, //que define as capacidades do Node, //nesse caso, a capacidade de ser salvo: getCookieSet().assign(SaveCookie.class, impl); } else { //Caso contrário, não fazemos nenhuma atribuição //e SaveCookie não é definido como //uma das capacidades do Node: getCookieSet().assign(SaveCookie.class); } } private class SaveCookieImpl implements SaveCookie { public void save() throws IOException { Confirmation msg = new NotifyDescriptor.Confirmation("Deseja salvar \"" + nameField.getText() + " (" + abbrevField.getText() + ") " + "\"?", NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.QUESTION_MESSAGE); Object result = DialogDisplayer.getDefault().notify(msg); //Quando o usuário clica em "Sim", indicando que deseja realmente salvar, //precisamos desabilitar o botão Salvar e o item de menu Salvar, //de forma que possam ser usados quando a próxima alteração seja feita //no campo de texto: if (NotifyDescriptor.YES_OPTION.equals(result)) { fire(false); //Adicionaremos aqui o código de manipulação de Salvar. } } } } - Declare o Node acima da classe TopComponent :
private DummyNode dummyNode;
Agora, quando você executar o aplicativo, verá um ícone diferente na barra de ferramentas. Ao invés do botão "Salvar todos", você agora tem o botão "Salvar" disponível.
Agora, no construtor do TopComponent, adicione-o aos nós ativados do TopComponent:
setActivatedNodes(new Node[]{dummyNode = new DummyNode()}); - Por padrão, o botão "Salvar todos" está disponível na barra de ferramentas da plataforma NetBeans. Em nosso cenário atual, não desejamos salvar "todos", por que "todos" se refere a vários documentos. Em nosso caso, temos somente um "documento", que é o editor que estamos reutilizando para todos os nós na hierarquia da árvore. Remova o botão "Salvar todos" e adicione "Salvar" em seu lugar, ao adicionar o seguinte arquivo de camada do módulo StatesEditor:
- A seguir, precisamos acionar uma alteração no DummyNode sempre que uma alteração for feita no JTextFields, o qual por sua vez adiciona uma implementação do SaveCookie às capacidades do Node ativado, o qual é nosso "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); } }); Execute o aplicativo e observe a habilitação/desabilitação do botão Salvar:

- A seguir, adicionamos o código JPA para persistência de nossa alteração. Faça isso ao substituir o comentário "//Adicionaremos aqui o código de manipulação de Salvar". O comentário deveria ser substituído pelo código a seguir:
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(); Execute o aplicativo e altere alguns dados. No momento, não temos a funcionalidade "Atualizar", portanto, para ver os dados alterados, reinicie o aplicativo. Por exemplo, aqui a hierarquia da árvore mostra o nome do estado persistido de "Missouri":

- Em quarto lugar, precisamos adicionar a funcionalidade de atualização para o visualizador Estados. Você pode desejar adicionar um Temporizador que periodicamente atualiza o visualizador. No entanto, neste exemplo, nós adicionaremos um item de menu "Atualizar" no nó Raiz, para que o usuário seja capaz de atualizar manualmente o visualizador.
- No pacote principal do módulo StatesViewer, crie um novo Node, que substituirá o AbstractNode que estamos usando atualmente como a raiz dos filhos do visualizador. Observe que também vinculamos uma ação "Atualizar" com nosso novo nó raiz.
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(); } } } - Adicione este método ao StatesTopComponent para atualizar a visualização:
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))); } Execute novamente o aplicativo e observe que você tem um novo nó raiz com uma ação "Atualizar".

- Altere alguns dados, salve-os, chame a ação Atualizar e observe que o visualizador está atualizado.
Agora, substitua o código acima no construtor do StatesTopComponent com uma chamada para o acima. Como você pode ver na parte realçada acima, agora estamos usando nosso StatesRootNode ao invés do AbstractNode. O StatesRootNode inclui a ação "Atualizar", que chama o código acima.
- No pacote principal do módulo StatesViewer, crie um novo Node, que substituirá o AbstractNode que estamos usando atualmente como a raiz dos filhos do visualizador. Observe que também vinculamos uma ação "Atualizar" com nosso novo nó raiz.
Verifique também se BeanTreeView no StatesViewer será ampliada na horizontal e na vertical quando o usuário redimensionar o aplicativo. Verifique isso abrindo a janela, selecionando BeanTreeView e clicando nos botões de seta na barra de ferramentas do construtor da GUI.
Como a janela do editor é aberta quando o aplicativo é iniciado, o LookupListener está disponível no momento da inicialização do aplicativo.
Reescreva esta linha:
result = Utilities.actionsGlobalContext().lookupResult(States.class);
Para:
result = WindowManager.getDefault().findTopComponent("StatesTopComponent").getLookup().lookupResult(States.class);
A string "StatesTopComponent" é a ID do StatesTopComponent, que é uma constante da string que você pode encontrar no código-fonte do StatesTopComponent. Uma desvantagem da abordagem acima é a de que seu novo EditorTopComponent somente funciona se ele puder encontrar um TopComponent com a ID "StatesTopComponent". Isto precisa ser explicitamente documentado para que os desenvolvedores de editores alternativos possam saber que precisam identificar o visualizador TopComponent desta forma, ou é necessário reescrever o modelo de seleção, conforme descrito aqui por Tim Boudreau.
Se usar uma das abordagens acima, verificará que o contexto não se perde ao alternar para o EditorTopComponent, conforme ilustrado abaixo:

No momento, nada acontece quando você clica em OK na caixa de diálogo acima. Na próxima etapa, adicionamos alguns códigos JPA para manipular a persistências de nossas alterações.
O "s" no s.getId() no momento é indefinido. Redefina o resultChanged como segue, após declarar States s; acima da classe, para que o objeto States atual defina o s, o qual é então usado no código de persistência acima para obter a ID do objeto States atual.
@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]");
}
}
Você agora aprendeu como a plataforma NetBeans tem a permissão de manipular as alterações no JTextFields. Sempre que o texto muda, os botões Desfazer e Refazer da plataforma NetBeans são habilitados ou desabilitados. Também, o botão Salvar é corretamente habilitado ou desabilitado, permitindo que o usuário salve os dados alterados no banco de dados.
Criar
Nesta seção, você permite que o usuário crie uma nova entrada no banco de dados.
Clique com o botão direito do mouse no módulo StatesEditor e escolha "Nova ação". Use o assistente Nova ação para criar uma nova ação "Sempre habilitado". A nova ação deve ser exibida na barra de ferramentas e na barra de menus.

Na próxima etapa do assistente, chame a ação NewAction:

Certifique-se de ter disponível um ícone 16x16, que o assistente força que seja selecionado, se for indicado que deseja que a ação seja chamada da barra de ferramentas.
- Na nova ação, deixe que o TopComponent seja aberto com JTextFields vazios:
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(); } }A ação implementa a classe ActionListener que está vinculada ao aplicativo através de entradas no arquivo de camadas, colocado pelo assistente Nova ação. Imagine que fácil será quando transferir seu aplicativo Swing existente para a plataforma NetBeans, já que você simplesmente poderá usar as mesmas classes Action usadas em seu aplicativo original, sem a necessidade de reescrevê-las para estarem em conformidade com as classes Action fornecidas pela plataforma NetBeans!
- No SaveCookie, assegure que um retorno de null indique que a nova entrada está salva, ao invés de uma entrada existente ser atualizada:
public void save() throws IOException { Confirmation msg = new NotifyDescriptor.Confirmation("Deseja salvar \"" + nameField.getText() + " (" + abbrevField.getText() + ") " + "\"?", NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.QUESTION_MESSAGE); Object result = DialogDisplayer.getDefault().notify(msg); //Quando o usuário clica em "Sim", indicando que deseja realmente salvar, //precisamos desabilitar o botão Salvar e o item de menu Salvar, //de forma que possam ser usados quando a próxima alteração seja feita //no campo de texto: 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(); } } } Execute novamente o aplicativo e adicione um novo estado no banco de dados:

Ao atualizar os dados, encontrará novas entradas que são adicionadas no fim da lista, porque elas são ordenadas por seu número de ID. Portanto, "Disneyland" é adicionado no final, ao invés de sua posição alfabética.
No EditorTopComponent, adicione o seguinte método para redefinir o JTextFields e para criar um novo objeto States:
public void resetFields() {
s = new States();
nameField.setText("");
abbrevField.setText("");
}
Excluir
Nesta seção, você permite que o usuário exclua uma entrada selecionada no banco de dados. Usando os conceitos e códigos acima descritos, implemente você mesmo a ação Excluir.
- Crie uma nova ação, DeleteAction. Decida se deseja vinculá-la a um nó Estado ou se deseja vinculá-la à barra de ferramentas, barra de menus, ou ambas. Dependendo de onde estiver vinculada, você precisará usar uma classe diferente da plataforma NetBeans. Leia novamente o tutorial para obter ajuda, especialmente ao examinar como a ação "Novo" foi criada, enquanto a compara com a ação "Atualizar" no nó raiz.
- Obtenha os States atuais, retorne uma caixa de diálogo 'Tem certeza?', e a seguir exclua a entrada. Para obter ajuda neste ponto, leia novamente o tutorial, focando na parte onde a funcionalidade "Salvar" é implementada. Ao invés de salvar, você agora deseja excluir uma entrada do banco de dados.
Screencasts relacionados
O primeiro screencast explica como criar uma visualização na plataforma NetBeans acima de seu banco de dados:
O segundo screencast mostra como criar o editor relacionado. (A ser introduzido).
Consulte também
Isto conclui o tutorial CRUD da plataforma NetBeans. Este documento descreveu como criar um novo aplicativo na plataforma NetBeans com a funcionalidade CRUD para um determinado banco de dados. Para obter mais informações sobre a criação e o desenvolvimento de aplicativos, consulte os seguintes recursos:
