Novidades do CUBA 7
Há três anos, anunciamos a segunda versão principal do framework disponível ao público. CUBA 6 foi a versão que mudou o jogo - o licenciamento passou do proprietário para o Apache 2.0. Naqueles dias, não podíamos nem imaginar onde isso traria a estrutura a longo prazo. A comunidade CUBA começou a crescer exponencialmente, por isso aprendemos muitas maneiras possíveis (e às vezes impossíveis) de como os desenvolvedores usam a estrutura. Agora, estamos felizes em anunciar o CUBA 7 , que, esperamos, tornará o desenvolvimento mais coerente e alegre para todos os membros da comunidade, daqueles que estão iniciando sua jornada em CUBA e Java, para desenvolvedores empresariais qualificados e especialistas em Java.

Obviamente, grande parte do sucesso do CUBA devemos ao CUBA Studio . Ele simplificou notavelmente a rotina empresarial Java exagerada, em muitos lugares a base para fazer configurações triviais nos designers visuais: não é necessário conhecer a Persistence API ou Gradle ou mesmo Spring para desenvolver um aplicativo CRUD completo e rico em recursos - o Studio fará isso para você
O Studio era um aplicativo Web separado e esse fato causou algumas limitações significativas:
- Antes de tudo, o Studio não era um IDE completo, então os desenvolvedores tiveram que alternar entre o Studio e o IntelliJ IDEA ou Eclipse para desenvolver a lógica de negócios e se beneficiar de uma navegação conveniente, conclusão de código e outras coisas essenciais, o que era irritante.
- Em segundo lugar, essa simplicidade mágica foi construída sobre uma enorme análise e geração de código-fonte. Melhorar os recursos de geração de código significaria avançar para o desenvolvimento de um IDE completo - um empreendimento muito ambicioso.
Decidimos nos apoiar no ombro de outro gigante para superar essas limitações. O Studio foi mesclado no IntelliJ IDEA pela JetBrains. Agora você pode instalá-lo como um plug-in para o seu IntelliJ IDEA ou fazer o download como um pacote independente.

Isso abre novos horizontes:
- Outros idiomas da JVM suportam (e Kotlin em primeiro lugar)
- Implementação a quente aprimorada
- Navegação intuitiva por todo o projeto
- Dicas mais inteligentes e geradores de código
Atualmente, o novo Studio está em desenvolvimento ativo: estamos portando recursos da versão antiga. O plano de curto prazo também é reimplementar designers baseados na Web usando a interface do usuário IntelliJ nativa e melhorar a experiência de navegação do projeto.
Atualização da pilha
Tradicionalmente, a pilha subjacente também foi atualizada principalmente, por exemplo, Java 8/11, Vaadin 8, Spring 5.

Por padrão, novos projetos usam o Java 8, mas você pode especificar a versão do Java incluindo a seguinte cláusula no arquivo build.gradle:
subprojects { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }
A atualização para o Vaadin 8 foi um grande desafio por causa de grandes mudanças na API de ligação de dados do Vaadin. Felizmente, o CUBA abstrai desenvolvedores dos componentes internos do Vaadin, envolvendo-o em sua própria camada de API. A equipe do CUBA fez um ótimo trabalho reimplementando os componentes internos, mantendo sua própria API intocada. Isso significa que a compatibilidade é totalmente salva e você pode se beneficiar do Vaadin 8 logo após migrar um projeto para o CUBA 7 sem nenhuma refatoração.
A lista completa de dependências atualizadas está disponível nas notas de versão oficiais.
API de novas telas
Esta seção também pode ser chamada de "API das primeiras telas" - pois o CUBA nunca teve nenhuma API declarada oficialmente na camada de cliente da web. Vem da história da estrutura e de certas suposições feitas na primeira etapa:
- Abordagem centrada no declarativo - tudo o que pode ser descrito declarativamente, deve ser declarado em um descritor de tela e não codificado em seu controlador
- As telas padrão (navegador e editor) fornecem funcionalidade genérica concreta e não é necessário modificá-la
Desde que os primeiros mil membros se juntaram à nossa comunidade, percebemos o quão grande é a variedade de requisitos para telas CRUD "padrão" - muito além do conjunto de recursos inicialmente projetado. No entanto, por um longo tempo, conseguimos lidar com solicitações de comportamento personalizado, mesmo sem uma camada de API - graças a outra suposição do primeiro estágio - Open Inheritance. Herança efetivamente aberta significa que você pode substituir qualquer método público ou protegido de uma classe subjacente para adaptar seu comportamento ao que você precisa. Isso pode parecer uma cura para todas as doenças, mas, na verdade, não oferece nem um contrato de curto prazo: e se o método substituído for renomeado, excluído ou simplesmente nunca usado nas futuras versões do framework?

Então, em resposta à crescente demanda da comunidade, decidimos introduzir uma nova API de telas. A API fornece pontos de extensão claros e de longo prazo, sem mágica declarativa oculta, flexível e muito fácil de usar.
Declaração de tela
No CUBA 7, a declaração de tela é extremamente simples:
@UiController("new-screen") // screen id public class NewScreen extends Screen { }
A partir do exemplo acima, podemos ver que o identificador de tela é definido explicitamente logo acima da classe do controlador. Em outras palavras, o ID da tela e a classe do controlador agora correspondem exclusivamente um ao outro. Portanto, boas notícias, agora as telas podem ser endereçadas diretamente por sua classe de controlador de maneira segura:
@Inject private ScreenBuilders screenBuilders; @Subscribe private void onBeforeClose(BeforeCloseEvent event) { screenBuilders.screen(this) .withScreenClass(SomeConfirmationScreen.class) .build() .show(); }
O descritor de tela se torna uma parte complementar em vez de obrigatória. O layout pode ser criado programaticamente ou declarado como um descritor de tela xml, definido pela anotação @UiDescriptor sobre a classe do controlador. Isso torna os controladores e o layout muito mais fáceis de ler e entender - essa abordagem é muito semelhante à usada no desenvolvimento do Android.
Antes, também era necessário registrar um descritor de tela no arquivo web-screens.xml e atribuir um identificador a ele. No CUBA 7, esse arquivo é mantido por motivos de compatibilidade; no entanto, a criação de telas de uma nova maneira não exige esse registro.
Ciclo de vida das telas
A nova API apresenta eventos de ciclo de vida de tela claros e auto-explicativos:
- Init
- Afterinit
- Beforeshow
- Pós-show
- Antes de fechar
- Afterclose
Todos os eventos relacionados à tela no CUBA 7 podem ser registrados da seguinte maneira:
@UiController("new-screen") public class NewScreen extends Screen { @Subscribe private void onInit(InitEvent event) { } @Subscribe private void onBeforeShow(BeforeShowEvent event) { } }
Comparando a nova API com a abordagem antiga, você pode ver que não estamos substituindo métodos de gancho, chamados obscuramente na hierarquia das classes pai, mas definindo a lógica em pontos predefinidos claros do ciclo de vida da tela.
Manipulação de Eventos e Delegados Funcionais
Na seção anterior, aprendemos como se inscrever nos eventos do ciclo de vida. Então, e os outros componentes? Ainda devemos dispersar todos os ouvintes necessários na inicialização da tela como nas versões 6.x? A nova API é muito uniforme, portanto, inscrever-se em outros eventos é absolutamente semelhante aos do ciclo de vida.
Vamos dar um exemplo simples com dois elementos da interface do usuário: um botão e um campo de moeda, para que seu descritor xml se pareça com:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="msg://caption" messagesPack="com.company.demo.web"> <layout> <hbox spacing="true"> <currencyField id="currencyField" currency="$" currencyLabelPosition="LEFT"/> <button id="calcPriceBtn" caption="Calculate Price"/> </hbox> </layout> </window>
Ao clicar no botão, chamamos o serviço de middleware retornando um número, que vai para o campo de moeda. O campo da moeda deve mudar seu estilo, dependendo do valor do preço.
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private PricingService pricingService; @Inject private CurrencyField<BigDecimal> currencyField; @Subscribe("calcPriceBtn") private void onCalcPriceBtnClick(Button.ClickEvent event) { currencyField.setValue(pricingService.calculatePrice()); } @Subscribe("currencyField") private void onPriceChange(HasValue.ValueChangeEvent<BigDecimal> event) { BigDecimal price = pricingService.calculatePrice(); currencyField.setStyleName(getStyleNameByPrice(price)); } private String getStyleNameByPrice(BigDecimal price) { ... } }
No exemplo acima, podemos ver dois manipuladores de eventos: um é chamado quando o botão é clicado e outro é executado quando o campo da moeda altera seu valor - tão simples quanto isso.
Agora, vamos imaginar que precisamos validar nosso preço e verificar se seu valor é positivo. A maneira direta seria adicionar um validador durante a inicialização da tela:
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private CurrencyField<BigDecimal> currencyField; @Subscribe private void onInit(InitEvent event) { currencyField.addValidator(value -> { if (value.compareTo(BigDecimal.ZERO) <= 0) throw new ValidationException("Price should be greater than zero"); }); } }
Em aplicações do mundo real, um ponto de entrada na tela geralmente fica cheio com esse tipo de inicializador de elementos de tela. Para resolver esse problema, o CUBA fornece a anotação útil @Install
. Vamos ver como isso pode ajudar no nosso caso:
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private CurrencyField<BigDecimal> currencyField; @Install(to = "currencyField", subject = "validator") private void currencyFieldValidator(BigDecimal value) { if (value.compareTo(BigDecimal.ZERO) <= 0) throw new ValidationException("Price should be greater than zero"); } }
De fato, delegamos a lógica de validação do nosso campo de moeda ao método currencyFieldValidator em nossa tela. Isso pode parecer um pouco complicado, no entanto, os desenvolvedores adotam esse recurso surpreendentemente rápido.
Criadores de tela / notificações / caixas de diálogo

O CUBA 7 também apresenta um conjunto de componentes úteis com APIs fluentes:
O ScreenBuilders combina fábricas fluentes para gerar pesquisas padrão, editores e telas personalizadas. O exemplo abaixo mostra como você pode abrir uma tela da outra. Observe que o método build () retorna a instância de tela do tipo certo, sem a necessidade de convertê-lo sem segurança.
CurrencyConversions currencyConversions = screenBuilders.screen (isso)
.withScreenClass (CurrencyConversions.class)
.withLaunchMode (OpenMode.DIALOG)
.build ();
currencyConversions.setBaseCurrency (Currency.EUR);
currencyConversions.show ();
O componente Telas fornece uma abstração de nível inferior para criar e mostrar telas em vez de ScreenBuilders . Ele também fornece acesso às informações sobre todas as telas abertas no aplicativo CUBA ( Screens # getOpenedScreens ), caso você precise iterá- las.
Os componentes Notificações e Diálogos apresentam interfaces auto-explicativas convenientes. Aqui está um exemplo para criar e mostrar uma caixa de diálogo e uma notificação:
dialogs.createOptionDialog ()
.withCaption ("Minha primeira caixa de diálogo")
.withMessage ("Gostaria de agradecer à equipe CUBA?")
.withActions (
novo DialogAction (DialogAction.Type.YES) .withHandler (e ->
Notifications.create ()
.withCaption ("Obrigado!")
.withDescription ("Agradecemos a todos os membros da comunidade")
.withPosition (Notifications.Position.MIDDLE_CENTER)
.withHideDelayMs (3000)
.show ()),
novo DialogAction (DialogAction.Type.CANCEL)
)
.show ();
Ligação de dados
O CUBA permite o desenvolvimento extremamente rápido de UIs de backoffice, não apenas fornecendo ferramentas visuais avançadas com amplos recursos de geração de código, mas também por um rico conjunto de componentes sensíveis a dados disponíveis imediatamente. Esses componentes precisam apenas saber com quais dados eles trabalham e o restante será gerenciado automaticamente, por exemplo, listas de pesquisa, campos selecionadores, várias grades com operações CRUD e assim por diante.
Antes da ligação de dados da versão 7 ser implementada por meio das chamadas fontes de dados - objetos que envolvem uma única entidade ou uma coleção de entidades para vinculá-los reativamente a componentes com reconhecimento de dados. Essa abordagem funcionou muito bem, no entanto, em termos de implementação, era um monólito. A arquitetura monolítica normalmente causa problemas com sua personalização, portanto, no CUBA 7, esse pedregulho sólido foi dividido em três componentes de dados:
- O carregador de dados é um provedor de dados para contêineres de dados. Os carregadores de dados não mantêm dados, apenas passam todos os parâmetros de consulta necessários para um armazenamento de dados e alimentam os contêineres com o conjunto de dados resultante.
- O contêiner de dados mantém os dados carregados (uma única entidade ou várias entidades) e os fornece aos componentes com reconhecimento de dados de maneira reativa: todas as alterações das entidades agrupadas são expostas aos componentes de UI correspondentes e vice-versa, todas as alterações em os componentes da interface do usuário levarão às alterações correspondentes em seu contêiner de dados.
- O contexto de dados é um poderoso gerenciador de modificação de dados que rastreia alterações e confirma todas as entidades modificadas. Uma entidade pode ser mesclada em um contexto de dados, para fornecer uma cópia da entidade original com a única diferença, mas muito importante: todas as modificações da entidade resultante e todas as entidades que ela referencia (incluindo coleções) serão rastreadas, armazenadas e cometido em conformidade.
Os componentes de dados podem ser declarados em descritores de tela ou instanciados programaticamente usando uma fábrica especializada - DataComponents .
Diversos
Ufff, as partes mais significativas da nova API de telas são descritas, então deixe-me listar brevemente outros recursos importantes na camada de cliente da Web:
- Histórico e navegação de URL . Esse recurso resolve um problema muito comum do SPA com o botão "voltar" em um navegador da Web, fornece uma maneira fácil de atribuir rotas às telas de aplicativos e permite que uma API reflita o estado atual de uma tela em sua URL.
- Formulário em vez de FieldGroup . O FieldGroup é um componente com reconhecimento de dados para mostrar e modificar campos de uma única entidade. Ele infere a interface do usuário real mostrada para um campo em tempo de execução. Em outras palavras, se você tiver um campo Data em sua entidade, ele será mostrado como um DateField . No entanto, se você deseja operar com esse campo programaticamente, precisará injetar esse campo no controlador de tela e convertê-lo no tipo certo manualmente ( DateField em nosso exemplo ). Posteriormente, alteramos nosso tipo de campo para outro e nosso aplicativo falha no tempo de execução ... O formulário soluciona esse problema por declaração explícita de tipo de campo. Encontre mais informações sobre esse novo componente aqui .
- A integração de componentes JavaScript de terceiros é significativamente simplificada, siga a documentação para incorporar componentes JavaScript personalizados em um aplicativo CUBA.
- Agora, os atributos HTML / CSS podem ser facilmente definidos diretamente no descritor de tela xml ou definidos programaticamente. Encontre mais informações aqui .
Recursos de middleware
O bloco anterior sobre a nova API de telas era maior do que eu esperava, portanto, nesta seção, vou tentar ser legal!
Evento alterado da entidade
Evento alterado de entidade é um evento de aplicativo Spring que é disparado quando sua entidade foi para um repositório de dados, foi inserido fisicamente e está a uma polegada de ser confirmado. Aqui você pode fornecer algumas verificações adicionais (por exemplo, verificar a disponibilidade do produto em estoque antes de confirmar um pedido) e modificá-lo (por exemplo, recalcular totais) antes de ficar visível para outras transações (é claro, com nível de isolamento confirmado por leitura). Você também pode usar esse evento como a última chance de interromper a confirmação da transação, lançando uma exceção - que pode ser útil em alguns casos de canto.
Também há uma maneira de capturar o Evento Alterado da Entidade logo após a confirmação.
Siga este capítulo da documentação para ver um exemplo.
Transactional Data Manager
Ao desenvolver um aplicativo, normalmente operamos com entidades desanexadas - aquelas que não são gerenciadas por nenhuma transação. No entanto, nem sempre é possível trabalhar com entidades desanexadas, especialmente ao tentar atender aos requisitos do ACID - esse é o caso quando você pode usar o gerenciador de dados transacionais. Parece muito semelhante ao gerenciador de dados comum, mas difere nos seguintes aspectos:
- Ele pode ingressar na transação existente (no caso de ser chamado no contexto transacional) ou criar sua própria transação.
- Ele não possui método de confirmação , mas existe o método save, que não leva à confirmação imediata, mas aguarda até que a transação anexada seja confirmada.
Encontre um exemplo de como usá-lo aqui .
Retornos de chamada do ciclo de vida da JPA
Por fim, o CUBA 7 suporta retornos de chamada do ciclo de vida JPA. Para não replicar informações bem escritas sobre para que esses retornos de chamada podem ser usados, deixe-me compartilhar este link , que cobre completamente o assunto.
E a compatibilidade?

Uma pergunta justa para qualquer versão importante, especialmente quando há tantas mudanças aparentemente difíceis! Nós projetamos todos esses novos recursos e APIs tendo em mente a compatibilidade com versões anteriores:
- A API de telas antigas é suportada no CUBA 7 e é implementada por meio da nova versão oculta :)
- Também fornecemos adaptadores para a ligação de dados antigos, que continuam trabalhando para as telas antiquadas.
Portanto, boas notícias, o caminho de migração da versão 6 para 7 deve ser bastante direto.
Conclusão
Concluindo esta visão geral técnica, gostaria de mencionar que existem outras inovações importantes, especialmente no licenciamento:
- O limite de 10 entidades para o Studio acabou
- Os relatórios, BPM, Gráficos e Mapas e complementos de pesquisa de texto completo agora são gratuitos e de código aberto.
- A versão comercial do Studio oferece conforto extra de desenvolvimento com designers visuais para entidades, telas, menus e outros elementos da plataforma, enquanto a versão gratuita se concentra em trabalhar com código
- Não se esqueça que para as versões 6.x e mais antigas dos termos de licenciamento da Platform e Studio permanecem os mesmos!
Finalmente, gostaria de agradecer novamente aos membros da comunidade por todo o apoio e feedback. Espero que você ame a versão 7! A lista completa de alterações está tradicionalmente disponível nas notas de versão .