Passei bastante tempo trabalhando com o Couchbase e aplicativos Web usando o incrível Java SDK, mas você sabia que também é possível criar aplicativos de desktop que usam o Couchbase para armazenar dados? As estruturas de desktop Java, como o JavaFX, podem usar o SDK Java do Couchbase, mas provavelmente não é uma boa ideia usar um SDK destinado ao servidor em seu aplicativo voltado para o cliente. Em vez disso, você pode usar a solução móvel do Couchbase para criar aplicativos de desktop voltados para o cliente. Ela usa as mesmas APIs do Android, mas foi projetada com os desktops em mente.
Vamos dar uma olhada na criação de um aplicativo de desktop simples usando JavaFX, Couchbase Lite e até mesmo o Couchbase Sync Gateway para sincronizar esses dados entre computadores.
Os requisitos
Existem alguns requisitos que devem ser atendidos para que esse projeto seja bem-sucedido.
- JDK 1.7+
- Maven
- Gateway de sincronização do Couchbase
Este projeto usará o Maven para reunir nossas dependências e criar um arquivo JAR. Embora o Sync Gateway não seja realmente um requisito, ele o será se você desejar adicionar suporte à sincronização ao seu aplicativo.
Criação de um novo projeto JavaFX com o Maven
Precisamos criar um projeto Maven básico. Isso pode ser feito em um IDE de sua escolha ou manualmente. Essencialmente, precisaremos da seguinte estrutura de arquivos e diretórios:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
src principal java com couchbase CouchbaseSingleton.java Principal.java Todo.java TodoFXController.java recursos TodoFX.fxml pom.xml |
Entraremos em detalhes sobre o conteúdo de cada arquivo quando começarmos a desenvolver. Por enquanto, precisamos configurar nosso Maven pom.xml para que ele obtenha as dependências necessárias.
Abra o arquivo pom.xml e inclua o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
com.couchbase couchbase-javafx-exemplo 4.0.0 Couchbase JavaFX Exemplo frasco 1.0 com.couchbase.leve couchbase-leve-java 1.3.0 com.couchbase.leve couchbase-leve-java-sqlcipher 1.3.0 junção junção 4.12 teste com.zenjava javafx-mentor-plug-in 8.5.0 com.couchbase.Principal org.apache.mentor.plugins mentor-compilador-plug-in 3.3 1.7 1.7 mentor-montagem-plug-in 2.2.1 frasco-com-dependências verdadeiro lib/ com.couchbase.Principal fazer-montagem pacote único |
Sem entrar em muitos detalhes desnecessários no arquivo Maven acima, queremos prestar atenção a algumas coisas em particular.
1 2 3 4 5 6 7 |
com.couchbase.leve couchbase-leve-java 1.3.0 |
A dependência acima diz que estamos incluindo o Couchbase Lite em nosso projeto. Lembre-se de que o Couchbase Lite é um banco de dados local que não fica em um servidor em algum lugar. Os dados são armazenados localmente e o componente é empacotado em seu aplicativo.
Também queremos prestar atenção ao seguinte plug-in:
1 2 3 4 5 6 7 8 9 10 |
com.zenjava javafx-mentor-plug-in 8.5.0 com.couchbase.Principal |
O plug-in acima serve para criar um projeto JavaFX. É claro que essa criação de projeto é muito mais fácil quando se usa um IDE como o IntelliJ, embora não seja necessário.
Criando a classe singleton do Couchbase
Antes de nos dedicarmos a criar a interface do usuário e os controladores para o nosso projeto JavaFX, vamos nos preocupar com a forma como os dados serão tratados.
Para simplificar, é uma ótima ideia criar uma classe singleton para gerenciar os dados em todo o nosso projeto. Isso também funciona muito bem ao configurar ouvintes de dados para evitar a necessidade de escrever consultas em todos os lugares do aplicativo. Vamos abrir a seção src/main/java/com/couchbase/CouchbaseSingleton.java e inclua o código a seguir. Depois, vamos detalhá-lo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
pacote com.couchbase; importação com.couchbase.leve.*; importação com.couchbase.leve.replicador.Replicação; importação java.rede.URL; importação java.util.ArrayList; importação java.util.HashMap; importação java.util.Iterador; importação java.util.Mapa; público classe CouchbaseSingleton { privado Gerente gerente; privado Banco de dados banco de dados; privado Replicação pushReplication; privado Replicação pullReplication; privado estático CouchbaseSingleton instância = nulo; privado CouchbaseSingleton() { tentar { este.gerente = novo Gerente(novo JavaContext("dados"), Gerente.DEFAULT_OPTIONS); este.banco de dados = este.gerente.getDatabase("fx-project"); Ver todoView = banco de dados.getView("todos"); todoView.setMap(novo Mapeador() { @Substituir público vazio mapa(Mapa<Cordas, Objeto> documento, Emissor emissor) { emissor.emitir(documento.obter("_id"), documento); } }, "1"); } captura (Exceção e) { e.printStackTrace(); } } público estático CouchbaseSingleton getInstance() { se(instância == nulo) { instância = novo CouchbaseSingleton(); } retorno instância; } público Banco de dados getDatabase() { retorno este.banco de dados; } público vazio startReplication(URL portal, booleano contínuo) { este.pushReplication = este.banco de dados.createPushReplication(portal); este.pullReplication = este.banco de dados.createPullReplication(portal); este.pushReplication.setContinuous(contínuo); este.pullReplication.setContinuous(contínuo); este.pushReplication.iniciar(); este.pullReplication.iniciar(); } público vazio stopReplication() { este.pushReplication.parar(); este.pullReplication.parar(); } público Todo salvar(Todo todo) { Mapa<Cordas, Objeto> propriedades = novo HashMap<Cordas, Objeto>(); Documento documento = este.banco de dados.createDocument(); propriedades.colocar("tipo", "todo"); propriedades.colocar("título", todo.getTitle()); propriedades.colocar("description" (descrição), todo.getDescription()); tentar { todo.setDocumentId(documento.putProperties(propriedades).getDocument().getId()); } captura (Exceção e) { e.printStackTrace(); } retorno todo; } público ArrayList consulta() { ArrayList resultados = novo ArrayList(); tentar { Ver todoView = este.banco de dados.getView("todos"); Consulta consulta = todoView.createQuery(); QueryEnumerator resultado = consulta.executar(); Documento documento = nulo; para (Iterador ele = resultado; ele.hasNext(); ) { QueryRow fila = ele.próxima(); documento = fila.getDocument(); resultados.adicionar(novo Todo(documento.getId(), (Cordas) documento.getProperty("título"), (Cordas) documento.getProperty("description" (descrição)))); } } captura (Exceção e) { e.printStackTrace(); } retorno resultados; } } |
O que foi dito acima foi muito difícil de assimilar, mas foi necessário para evitar confusão.
Dentro do CouchbaseSingleton
existem quatro variáveis privadas. O gerenciador de banco de dados nos permitirá abrir nosso banco de dados e também criá-lo. Os objetos de replicação são responsáveis pela sincronização em qualquer direção.
No construtor
criamos e abrimos um banco de dados chamado projeto fx e configurar uma visualização que usaremos para consultar os dados. Essa visualização chamada todos
emitirá um par de valores-chave de id de documento e documento para cada documento armazenado no banco de dados local. O construtor
é privado porque o instanciamos por meio do método estático getInstance
.
Embora não abordaremos a sincronização até o final do guia, é uma boa ideia estabelecer a base. Essencialmente, queremos apenas definir que teremos uma replicação contínua de e para um determinado URL de gateway de sincronização. Também queremos poder interromper a replicação quando o aplicativo for fechado. Isso nos leva aos nossos métodos para salvar e carregar dados.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
público Todo salvar(Todo todo) { Mapa<Cordas, Objeto> propriedades = novo HashMap<Cordas, Objeto>(); Documento documento = este.banco de dados.createDocument(); propriedades.colocar("tipo", "todo"); propriedades.colocar("título", todo.getTitle()); propriedades.colocar("description" (descrição), todo.getDescription()); tentar { todo.setDocumentId(documento.putProperties(propriedades).getDocument().getId()); } captura (Exceção e) { e.printStackTrace(); } retorno todo; } |
Nosso salvar
receberá um método Todo
e salvá-lo no banco de dados. O resultado disso será um objeto Todo
que contém o ID do documento que é retornado ao método de chamada. Esse Todo
é simples. Ela aceita informações básicas como id, título e descrição, e tem os métodos getter e setter apropriados que correspondem. Para referência, ela se parece com o seguinte e existe na seção src/main/java/com/couchbase/Todo.java arquivo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
pacote com.couchbase; importação java.util.*; público classe Todo { privado Cordas documentId; privado Cordas título; privado Cordas descrição; Todo(Cordas documentId, Cordas título, Cordas descrição) { este.documentId = documentId; este.título = título; este.descrição = descrição; } Todo(Cordas título, Cordas descrição) { este.documentId = UUID.UUUID aleatório().toString(); este.título = título; este.descrição = descrição; } público vazio setDocumentId(Cordas documentId) { este.documentId = documentId; } público Cordas getDocumentId() { retorno este.documentId; } público Cordas getTitle() { retorno este.título; } público Cordas getDescription() { retorno este.descrição; } } |
Isso nos deixa com nossa última função relacionada a dados, a consulta
função.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
público ArrayList consulta() { ArrayList resultados = novo ArrayList(); tentar { Ver todoView = este.banco de dados.getView("todos"); Consulta consulta = todoView.createQuery(); QueryEnumerator resultado = consulta.executar(); Documento documento = nulo; para (Iterador ele = resultado; ele.hasNext(); ) { QueryRow fila = ele.próxima(); documento = fila.getDocument(); resultados.adicionar(novo Todo(documento.getId(), (Cordas) documento.getProperty("título"), (Cordas) documento.getProperty("description" (descrição)))); } } captura (Exceção e) { e.printStackTrace(); } retorno resultados; } |
Lembra-se da visualização que criamos? Desta vez, estamos consultando-a. O conjunto de resultados será carregado em uma matriz de Todo
objetos. Isso traz nossa camada de dados permitindo que nos concentremos no desenvolvimento real do aplicativo.
Projetando o aplicativo de desktop
Embora não seja obrigatório, o aplicativo JavaFX, Criador de cenasO recurso de controle de interface do usuário (UI), torna muito simples a criação de uma UI gráfica e da classe controladora correspondente. Se você optar por não usá-lo, abra o arquivo src/main/resources/TodoFX.fxml e inclua a seguinte marcação XML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
<!--?xml versão="1.0" codificação="UTF-8"?--> <!--?importação javafx.cena.controle.*?--> <!--?importação java.lang.*?--> <!--?importação javafx.cena.layout.*?--> <área de texto> <Botão fx:id="fxSave" layoutX="530.0" layoutY="365.0" mnemonicParsing="false" (falso) prefHeight="25.0" largura prefWidth="60.0" texto="Salvar" /> </crianças> </Painel> </código> </pré> <p>O acima marcação vontade dar nós a IU que aparência como o seguintes:</p> <p><imagem src="/wp-content/original-assets/2016/september/using-couchbase-in-a-javafx-desktop-application/todo-javafx-example.png" /></p> <p>Nada também fantasia em o acima, correto?</p> <p>Nós ter a simples IU com a lista, dois entrada campos, e a salvar botão, como descrito em o XML marcação. Em o marcação nós também referência <código>com.couchbase.TodoFXController</código>. Isso é o lógica que vontade ser vinculado para o particular FX visualização. Aberto o projeto' no arquivo src/main/java/com/couchbase/TodoFXController.java e inclua o seguinte: <code> pacote com.couchbase; importar com.couchbase.lite.*; import com.couchbase.lite.Database.ChangeListener; importar javafx.application.Platform; importar javafx.collections.ObservableList; importar javafx.event.ActionEvent; importar javafx.event.EventHandler; importar javafx.fxml.FXML; importar javafx.fxml.Initializable; importar javafx.scene.control.*; importar javafx.util.Callback; importar java.net.URL; importar java.util.*; public class TodoFXController implements Initializable { private CouchbaseSingleton couchbase; @FXML private TextField fxTitle; @FXML private TextArea fxDescription; @FXML private ListView fxListView; @FXML Botão privado fxSave; @Override public void initialize(URL fxmlFileLocation, ResourceBundle resources) { tente { this.couchbase = CouchbaseSingleton.getInstance(); fxListView.getItems().addAll(this.couchbase.query()); this.couchbase.getDatabase().addChangeListener(new ChangeListener() { @Override public void changed(Database.ChangeEvent event) { for(int i = 0; i < event.getChanges().size(); i++) { final Document retrievedDocument = couchbase.getDatabase().getDocument(event.getChanges().get(i).getDocumentId()); Platform.runLater(new Runnable() { @Override public void run() { int documentIndex = indexOfByDocumentId(retrievedDocument.getId(), fxListView.getItems()); for(int j = 0; j -1) { fxListView.getItems().remove(documentIndex); } } else { Se (documentIndex == -1) { fxListView.getItems().add(new Todo(retrievedDocument.getId(), (String) retrievedDocument.getProperty("title"), (String) retrievedDocument.getProperty("description"))); } else { fxListView.getItems().remove(documentIndex); fxListView.getItems().add(new Todo(retrievedDocument.getId(), (String) retrievedDocument.getProperty("title"), (String) retrievedDocument.getProperty("description"))); } } } }); } } }); } catch (Exception e) { e.printStackTrace(); } fxListView.setCellFactory(new Callback<ListView, ListCell>(){ @Override public ListCell call(ListView p) { ListCell cell = new ListCell(){ @Override protected void updateItem(Todo t, boolean bln) { super.updateItem(t, bln); Se (t != null) { setText(t.getTitle()); } } }; retornar célula; } }); fxSave.setOnAction(new EventHandler() { @Override public void handle(ActionEvent e) { Se(!fxTitle.getText().equals("") && !fxDescription.getText().equals("")) { fxListView.getItems().add(couchbase.save(new Todo(fxTitle.getText(), fxDescription.getText()))); fxTitle.setText(""); fxDescription.setText(""); } else { Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("Missing Information"); alert.setHeaderText(null); alert.setContentText("Tanto um título quanto uma descrição são necessários para este exemplo."); alert.showAndWait(); } } }); } private int indexOfByDocumentId(String needle, ObservableList haystack) { int result = -1; for(int i = 0; i < haystack.size(); i++) { Se(haystack.get(i).getDocumentId().equals(needle)) { resultado = i; pausa; } } resultado do retorno; } } Há muito o que entender no que diz respeito ao código acima, portanto, nós' re indo para quebra ele para baixo.</p><pré> <código> @FXML privado TextField fxTitle; @FXML privado TextArea fxDescrição; @FXML privado Visualização de lista fxListView; @FXML privado Botão fxSave; </código> </pré> <p>Lembre-se o FXML arquivo? Cada de o acima variáveis são mapeado para o componentes de que arquivo. No entanto, o que nós realmente cuidados sobre é o uso de o <código>Inicializável</código> interface que nós'está implementando. Isso requer uma função initialize na qual nós'll configuração nosso componente e banco de dados ouvintes.</p><pré> <código> fxListView.setCellFactory(novo Retorno de chamada<Visualização de lista<Todo>, ListCell<Todo>>(){ @Substituir público ListCell<Todo> chamada(Visualização de lista<Todo> p) { ListCell<Todo> célula = novo ListCell<Todo>(){ @Substituir protegida vazio updateItem(Todo t, booleano bln) { super.updateItem(t, bln); se (t != nulo) { setText(t.getTitle()); } } }; retorno célula; } }); </código> </pré> <p>Porque nósSe estivermos usando uma classe Todo personalizada em nossa lista, precisamos configurar como as linhas da lista exibem os dados. Por padrão, eles são strings, mas, na verdade, queremos extrair o título de qualquer um dos nossos elementos de dados e mostrá-lo em vez disso.<code> this.couchbase = CouchbaseSingleton.getInstance(); fxListView.getItems().addAll(this.couchbase.query()); No exemplo acima, estamos obtendo a instância do banco de dados aberto, realizando uma consulta e adicionando todos os dados à lista FX que está na tela. Isso é feito uma única vez, quando o aplicativo é carregado. Todos os futuros carregamentos de dados são feitos por meio de um ouvinte de dados. O ouvinte de alterações no banco de dados escutará todas as alterações nos dados enquanto o aplicativo estiver aberto. As alterações podem ocorrer em massa, portanto, percorremos as alterações e recuperamos os documentos. Se o documento for novo, adicione-o à lista. Se o documento tiver um indicador de exclusão, remova-o da lista. Se o documento for uma alteração em um documento existente, remova o antigo e adicione o novo à lista. Tudo isso é feito em um Por fim, temos nosso botão Salvar que tem seu próprio evento de clique. Se for clicado e os elementos de entrada forem preenchidos, os dados serão salvos no banco de dados. Até o momento, temos um aplicativo de desktop off-line que salva dados no Couchbase Lite. Agora podemos nos preocupar com a sincronização/replicação de dados. Sincronização de dados entre o desktop e o servidorA próxima parte é fácil graças ao Couchbase Mobile. Você precisará do Couchbase Sync Gateway instalado e em execução antes de começarmos a mexer no código do aplicativo para desktop. Com o Sync Gateway instalado, crie o seguinte arquivo de configuração básica: <code> { "log":["CRUD+", "REST+", "Changes+", "Attach+"], "bancos de dados": { "fx-example": { "servidor": "morsa:", "sync":` function (doc) { channel (doc.channels); } `, "users": { "GUEST": { "disabled": falso, "admin_channels": ["*"] } } } } } O arquivo de configuração acima é praticamente o mais básico possível. Crie uma partição <p>Saltando voltar em o JavaFX aplicativo. Aberto o projetodo arquivo src/main/java/com/couchbase/Main.java e inclua o seguinte: <code> this.couchbase = CouchbaseSingleton.getInstance(); this.couchbase.startReplication(new URL("http://localhost:4984/fx-example/"), true); O acima deve ser incluído no método ConclusãoVocê acabou de ver como criar um aplicativo JavaFX simples que armazena dados no Couchbase Lite e sincroniza os dados. Para executar esse aplicativo usando o Maven e a linha de comando, execute o seguinte: <code> mvn jfx:run Como o Couchbase é tão flexível, esse mesmo aplicativo pode ser estendido para dispositivos móveis com pouquíssimas alterações no código do aplicativo. Se você' d como para download o completo projeto em este guia, ele pode ser encontrado em GitHub <a href="https://github.com/couchbaselabs/couchbase-lite-javafx-example">aqui</a>.</p></área de texto> |