Java-фреймворк для быстрой разработки корпоративных приложений. Позволяет получить полноценный пользовательский интерфейс напрямую из доменной модели – без фронтенд разработки.

В основе приложения – бесшовная интеграция трех ключевых фреймворков:

Vaadin

Java-фреймворк для создания веб-интерфейсов

Hibernate

ORM-фреймворк для работы с базой данных

Spring

Стандарт для разработки современных Java-приложений

Готовый интерфейс — без единой строки фронтенда

VHS превращает аннотированные Java-классы в готовые экраны с таблицами, формами и навигацией — без участия фронтенд-разработчика.

Скриншот Боковое меню и конфигурируемая таблица с поиском, фильтрацией и действиями

Быстрый старт

Ниже приведён минимальный набор файлов, достаточный для запуска приложения с авторизацией и экраном управления пользователями.


[p:spring.datasource.url] = [s:jdbc:postgresql://localhost:5432/vhs-example]
[p:spring.datasource.username] = [s:postgres]
[p:spring.datasource.password] = [s:postgres]
[p:spring.datasource.driver-class-name] = [t:org][s:.][t:postgresql][s:.][t:Driver]

[p:spring.jpa.hibernate.naming.implicit-strategy] =
[t:org][s:.][t:hibernate][s:.][t:boot][s:.][t:model][s:.][t:naming][s:.][t:ImplicitNamingStrategyComponentPathImpl] [p:spring.jpa.hibernate.naming.physical-strategy] =
[t:org][s:.][t:hibernate][s:.][t:boot][s:.][t:model][s:.][t:naming][s:.][t:CamelCaseToUnderscoresNamingStrategy] [p:spring.jpa.hibernate.ddl-auto] = [m:update]
Настройка подключения к базе данных в application.properties

[a:@Theme]([s:"vhs"])
[a:@PageTitle]([s:"VHS Example"])
[a:@SpringBootApplication]
[k:public class] [t:ExampleApplication] [k:implements] [t:AppShellConfigurator] {

    [k:static] [k:void] [m:main]([t:String][] [p:args]) {
        [t:SpringApplication].[m:run]([t:ExampleApplication].[k:class], [p:args]);
    }
}
                
Главный класс, с которого стартует приложение

[a:@Getter]
[a:@Setter]
[a:@Entity]
[a:@Title]([s:"Пользователь"])
[a:@EqualsAndHashCode]([p:of] = [s:"id"])
[k:public class] [t:User] [k:implements] [t:UserDetails] {

    [a:@Id]
    [a:@UuidGenerator]
    [a:@Column]([p:updatable] = [k:false], [p:nullable] = [k:false])
    [a:@Title]([s:"Идентификатор"])
    [k:private] [t:UUID] [v:id];

    [a:@InstanceName]
    [a:@Column]([p:nullable] = [k:false])
    [a:@Title]([s:"Имя"])
    [a:@NotBlank]
    [k:private] [t:String] [v:name];

    [a:@Column]([p:unique] = [k:true], [p:updatable] = [k:false], [p:nullable] = [k:false])
    [a:@Title]([s:"Логин"])
    [a:@NotBlank]
    [k:private] [t:String] [v:username];

    [a:@Password]
    [a:@Column]([p:nullable] = [k:false])
    [a:@Title]([s:"Пароль"])
    [a:@Size]([p:min] = [p:3])
    [k:private] [t:String] [v:password];

    [a:@Override]
    [k:public] [t:Collection]<? [k:extends] [t:GrantedAuthority]> [m:getAuthorities]() {
        [k:return] [t:Collections].[m:emptyList]();
    }
}
                
Класс пользователя, реализующий UserDetails для аутентификации

[a:@Primary]
[a:@Service]
[k:public class] [t:UserService] [k:implements] [t:UserDetailsService] {

    [a:@Autowired]
    [k:private] [t:Dao] [v:dao];

    [a:@PostConstruct]
    [k:void] [m:init]() {
        [k:if] (![v:dao].[m:load]([t:User].[k:class]).[m:exists]()) {
            [m:createAdminUser]();
        }
    }

    [a:@Override]
    [k:public] [t:UserDetails] [m:loadUserByUsername]([t:String] [p:username]) [k:throws] [t:UsernameNotFoundException] {
        [k:return] [v:dao].[m:load]([t:User].[k:class])
                .[m:condition]([t:Condition].[m:equals]([s:"username"], [p:username]))
                .[m:joinAll]()
                .[m:optional]()
                .[m:orElseThrow](() -> [k:new] [t:UsernameNotFoundException]([s:"Пользователь не найден"]));
    }

    [k:private] [k:void] [m:createAdminUser]() {
        [t:User] user = [v:dao].[m:newInstance]([t:User].[k:class]);
        user.[m:setName]([s:"Администратор"]);
        user.[m:setUsername]([s:"admin"]);
        user.[m:setPassword]([s:"admin"]);
        [v:dao].[m:create](user);
    }
}
                
Сервис аутентификации, реализующий UserDetailsService — контракт Spring Security для загрузки пользователя по логину

[a:@Route]([s:"example-app"])
[a:@MenuItem]([p:view] = [t:DataView].[k:class], [p:path] = [s:"users"],
        [p:title] = [s:"Пользователи"], [p:icon] = [t:VaadinIcon].[p:USER_CARD],
        [p:parameters] = [a:@Parameter]([p:clazz] = [t:User].[k:class]))
[k:public class] [t:MainView] [k:extends] [t:VhsLayout] {
}
                
Главный экран и структура навигационного меню

Подключить в свой проект

Версия 0.1.0 пока не опубликована в Maven Central. Когда будет готово – просто добавьте зависимость в pom.xml.

Следите за релизами и обновлениями в нашем Telegram-канале.


[c:<!-- VHS.framework -->]
<[v:dependency]>
    <[v:groupId]>ru.vhsframework</[v:groupId]>
    <[v:artifactId]>vhs</[v:artifactId]>
    <[v:version]>0.1.0</[v:version]>
</[v:dependency]>
                                

Введение

Общие сведения

VHS — интеграционный фреймворк, который объединяет Vaadin (UI), Hibernate (ORM) и Spring (IoC/DI) в единую согласованную архитектуру для быстрой разработки бизнес-приложений на Java.

Писать HTML, XML-шаблоны, CSS-стили или JavaScript не нужно — UI-экраны генерируются автоматически на основе Java-классов и уже включают все необходимые настройки. Никакого генерируемого кода, который нужно поддерживать вручную.

Библиотека имеет богатый набор готовых компонентов для построения сложных UI: с формами, фильтрами, диалогами и валидацией — не выходя за пределы Java-кода. При необходимости каждый компонент можно расширить или заменить собственной реализацией — VHS не ограничивает вас в кастомизации.

Требования

Убедитесь, что в вашем проекте подключены:

  • Java 17+
  • Maven или Gradle
  • Spring Boot 3.x
  • Vaadin 24.x
  • Любая реляционная БД, поддерживаемая Hibernate

Сущности

Общие понятия

Сущность — основной строительный блок любого приложения. Это обычный Java-класс, аннотированный @Entity, поля которого фреймворк автоматически преобразует в UI-компоненты: колонки таблиц, поля форм, фильтры. Никакой дополнительной конфигурации не требуется — достаточно описать класс, и VHS сформирует полноценный CRUD-экран.

Базовый пример

Минимальная сущность требует только аннотации @Entity и поля с @Id. Аннотации VHS добавляются по мере необходимости.


[a:@Entity]
[a:@Title]([s:"Пользователь"])
[k:public class] [t:User] {

    [a:@Id]
    [a:@GeneratedValue]([p:strategy] = [t:GenerationType].[p:IDENTITY])
    [k:private] [t:Long] [v:id];

    [a:@Title]([s:"Имя"])
    [a:@NotBlank]
    [k:private] [t:String] [v:firstName];

    [a:@Title]([s:"Фамилия"])
    [a:@NotBlank]
    [k:private] [t:String] [v:lastName];

    [a:@Title]([s:"Email"])
    [a:@Email]
    [a:@Column]([p:unique] = [k:true])
    [k:private] [t:String] [v:email];

    [a:@Title]([s:"Возраст"])
    [a:@Min]([p:0]) [a:@Max]([p:100])
    [k:private] [k:int] [v:age];

    [a:@InstanceName]
    [k:public] [t:String] [m:getFullName]() {
        [k:return] [v:firstName] + [s:" "] + [v:lastName];
    }
}
                        

Интерфейсы сущностей

VHS предоставляет ряд интерфейсов, которые сущность может реализовать для получения дополнительного поведения в UI — без какой-либо ручной настройки компонентов.

HasZoneId

Если сущность реализует HasZoneId, фреймворк будет отображать все поля даты и времени в часовом поясе, возвращаемом методом getZoneId(). Это удобно, например, когда у каждого пользователя или филиала свой часовой пояс.

HasAvatar

Реализация HasAvatar позволяет фреймворку отображать аватар пользователя в интерфейсе приложения. Если метод getFile() возвращает null, фреймворк автоматически выводит аббревиатуру из имени пользователя на цветном фоне.

Поддерживаемые типы данных

Типы данных

VHS поддерживает широкий набор Java-типов — от примитивов до коллекций и встраиваемых объектов. Каждый тип автоматически получает подходящий UI-компонент.

Тип Описание
Boolean / boolean Логическое значение (истина / ложь)
Символьные
Character / char Одиночный символ Unicode
String Строка символов
Числовые
Byte / byte Целое число от -128 до 127
Short / short Целое число от -32 768 до 32 767
Integer / int Целое число от -2³¹ до 2³¹-1
Long / long Целое число от -2⁶³ до 2⁶³-1
Float / float Число с плавающей точкой одинарной точности
Double / double Число с плавающей точкой двойной точности
BigInteger Целое число произвольной точности
BigDecimal Десятичное число произвольной точности
Дата и время
Date Дата и время (legacy)
Calendar Дата и время (legacy)
Instant Момент времени в UTC
LocalDate Дата без времени и часового пояса
LocalDateTime Дата и время без часового пояса
LocalTime Время без даты и часового пояса
MonthDay Месяц и день без года
OffsetDateTime Дата и время со смещением от UTC
OffsetTime Время со смещением от UTC
Year Год
YearMonth Год и месяц
ZonedDateTime Дата и время с полным часовым поясом
Дополнительные
UUID Уникальный идентификатор (128 бит)
Period Промежуток между датами в годах, месяцах и днях
Duration Продолжительность в секундах и наносекундах
ZoneId Идентификатор часового пояса (например, Europe/Moscow)
Перечисление
Enum Значение из фиксированного набора констант
Ссылка на сущность
Entity Ссылка на связанный JPA-объект
Файл
File Ссылка на загруженный файл
Коллекции
List<Enum> Коллекция enum-значений
List<Entity> Коллекция связанных JPA-сущностей
List<File> Коллекция загруженных файлов
Встраиваемые объекты
Embedded Группа полей, сохраняемая в таблице владельца
Ассоциативный массив
Map<K, V> Коллекция пар «ключ — значение»

Аннотации JPA

VHS не ограничивает использование JPA — все стандартные аннотации маппинга поддерживаются в полном объёме. Таблица служит краткой памяткой по аннотациям маппинга.

Группа Аннотация Описание
Идентификатор @Id Первичный ключ сущности
@EmbeddedId Составной первичный ключ через встраиваемый объект
Версия @Version Поле версии для оптимистичной блокировки
Аудит @CreatedBy Кто создал запись
@CreatedDate Дата создания записи
@LastModifiedBy Кто последний изменил запись
@LastModifiedDate Дата последнего изменения записи
Простое поле @Column Настройка отображения поля в колонку таблицы
Перечисления @Enumerated Способ хранения перечисления в БД
@ElementCollection Коллекция простых значений или перечислений
@CollectionTable Настройка таблицы для хранения коллекции
Ссылка на сущность @JoinColumn Настройка колонки внешнего ключа
@OneToOne Связь «один к одному»
@ManyToOne Связь «многие к одному»
@MapsId Использует внешний ключ связи как первичный ключ сущности
Список объектов @OneToMany Связь «один ко многим»
@ManyToMany Связь «многие ко многим»
@JoinTable Настройка промежуточной таблицы связи
Встраиваемый объект @Embeddable Класс, чьи поля встраиваются в таблицу владельца
@Embedded Встраивает поля дочернего объекта в таблицу владельца
Ассоциативный массив @MapKey Ключ map — поле связанной сущности
@MapKeyColumn Ключ map — простое значение, настройка колонки
@MapKeyJoinColumn Ключ map — ссылка на другую сущность
@MapKeyJoinColumns Ключ map — сущность с составным первичным ключом
@MapKeyEnumerated Ключ map — перечисление, способ хранения
@MapKeyTemporal Ключ map — дата или время, способ хранения

Валидация

Ограничения на значения полей задаются стандартными аннотациями Bean Validation — VHS подхватывает их и отображает ошибки прямо на форме.

Аннотация Описание
@NotNull Значение обязательно для заполнения
@Null Значение должно отсутствовать
Логические
@AssertTrue Значение должно быть true
@AssertFalse Значение должно быть false
Строки
@NotBlank Строка не должна быть пустой или состоять только из пробелов
@NotEmpty Строка или коллекция не должна быть пустой
@Email Строка должна быть валидным адресом электронной почты
@Pattern Строка должна соответствовать регулярному выражению
@Size Длина строки или размер коллекции должны быть в заданном диапазоне
Числовые
@Digits Число должно соответствовать заданному формату цифр
@Min Число должно быть не меньше указанного значения
@Max Число должно быть не больше указанного значения
@DecimalMin Десятичное число должно быть не меньше указанного значения
@DecimalMax Десятичное число должно быть не больше указанного значения
@Positive Число должно быть строго положительным
@PositiveOrZero Число должно быть положительным или равным нулю
@Negative Число должно быть строго отрицательным
@NegativeOrZero Число должно быть отрицательным или равным нулю
Дата и время
@Past Дата должна быть в прошлом
@PastOrPresent Дата должна быть в прошлом или настоящем
@Future Дата должна быть в будущем
@FutureOrPresent Дата должна быть в будущем или настоящем
Коллекции и Maps
@NotEmpty Коллекция или ассоциативный массив не должен быть пустой
@Size Размер коллекции или ассоциативного массива должен быть в заданном диапазоне

Настройка форм

Аннотации отображения полей

Фреймворк автоматически генерирует формы на основе полей класса. Управлять их отображением и поведением можно с помощью аннотаций, которые размещаются над нужным полем или классом.

Аннотация Описание
@Title Задает отображаемое название поля или класса в форме.

[a:@Title]([s:"Пользователь"])
[k:public] [k:class] [t:User] {
    [c:...]
}
                                        

[a:@Title]([s:"Фамилия"])
[k:private] [t:String] [v:surname];
                                        
@Placeholder Задает подсказку внутри поля, исчезающую при вводе.

[a:@Placeholder]([s:"Иванов"])
[k:private] [t:String] [v:surname];
                                        
@HelperText Отображает вспомогательный текст под полем.

[a:@HelperText]([s:"Будьте внимательны при заполнении"])
[k:private] [t:String] [v:surname];
                                        
@Tooltip Отображает всплывающую подсказку при наведении.

[a:@Tooltip]([s:"Указывайте реальный номер телефона"])
[k:private] [t:String] [v:phone];
                                        
@Mask Накладывает маску ввода на поле. Использует библиотеку imaskjs.

[a:@Mask]([s:"+{7} (000) 000-00-00"])
[k:private] [t:String] [v:phone];
                                        
@Password Скрывает введенное значение и автоматически хэширует его через PasswordEncoder перед сохранением.

[a:@Password]
[k:private] [t:String] [v:password];
                                        
@Url Добавляет кнопку перехода на форме и отображает кликабельное значение в таблице.

[a:@Url]
[k:private] [t:String] [v:website];
                                        
@GridColumns Задает список колонок, отображаемых в табличном представлении. Можно указать как на классе, так и на поле.

[a:@GridColumns]({[s:"id"], [s:"name"], [s:"age"]})
[k:private] [t:List]<[t:User]> [v:users];
                                        

[a:@GridColumns]({[s:"id"], [s:"name"], [s:"age"]})
[k:public] [k:class] [t:User] {
    [c:...]
}
                                        

Текстовое представление сущностей

По умолчанию все перечисления и сущности на форме отображаются с использованием toString. Вы можете переопределить это поведение, указав данную аннотацию над полем или методом. Такой метод может принимать любые Spring-бины.

Аннотация Примеры
@InstanceName

[a:@InstanceName]
[k:private] [t:String] [v:name];
                                        

[a:@InstanceName]
[k:public] [t:String] [m:getInstanceName]() {
    [k:return] [s:"User: "] + [v:name];
}
                                        

Условное поведение полей (SpEL)

Для описания условий используется Spring Expression Language (SpEL). Выражение вычисляется в контексте текущей сущности — поля класса доступны по имени напрямую.

Обычно зависимые переменные, указанные в условии, определяются автоматически. Но вы можете указать их явно в аннотации в параметре dependentFields.

Аннотация Описание
@ReadOnlyWhen Аннотированное поле будет отображаться в режиме readonly при выполнении заданного условия

[k:private] [k:boolean] [v:confirmed];

[a:@ReadOnlyWhen]([s:"!confirmed"])
[k:private] [t:String] [v:phone];
                                        
@DisabledWhen Аннотированное поле будет отображаться в режиме disabled при выполнении заданного условия

[k:private] [k:int] [v:age];

[a:@DisabledWhen]([s:"age < 18"])
[k:private] [t:String] [v:passportNumber];
                                        
@VisibleWhen Аннотированное поле будет отображаться на форме только при выполнении заданного условия

[k:private] [t:PaymentType] [v:type];

[a:@VisibleWhen]([s:"type == T(io.vhs.PaymentType).CARD"])
[k:private] [t:String] [v:cardNumber];
                                        
@OnValueChange Позволяет выполнить SpEL-выражение при изменении значения в поле

[a:@OnValueChange]([s:"sum = a + b"])
[k:private] [k:int] [v:a];

[a:@OnValueChange]([s:"sum = a + b"])
[k:private] [k:int] [v:b];

[k:private] [k:int] [v:sum];
                                        

Кастомизация компонентов

В реализуемом классе быть конструктор, принимающий 1 параметр наследуемый от MetaField<T, V>. Сама аннотация может быть указана как над полем, так и над классом-типом — в последнем случае применяется ко всем полям этого типа.

Отображение на форме

Позволяет заменить стандартный компонент поля на кастомный.


[a:@VaadinField]([t:PaymentTypeField].[k:class])
[k:private] [t:PaymentType] [v:type];
                        

[a:@RequiredArgsConstructor]
[k:public] [k:class] [t:PaymentTypeField]<[t:T]> [k:extends] [t:RadioButtonGroup]<[t:PaymentType]> {

    [k:private] [k:final] [t:EnumMetaField]<[t:T], [t:PaymentType]> [v:field];

    [a:@PostConstruct]
    [k:void] [m:init]() {
        [m:setItems]([v:field].[m:getEnumConstants]());
        [m:setItemLabelGenerator]([t:PaymentType]::[m:getLabel]);
        [m:addThemeVariants]([t:RadioGroupVariant].[p:LUMO_VERTICAL]);
    }
}
                        

Отображение в таблице

Задаёт кастомный рендерер для ячеек табличного представления.


[a:@VaadinRenderer]([t:PaymentTypeRenderer].[k:class])
[k:private] [t:PaymentType] [v:type];
                        

[a:@Component]
[a:@PrototypeScope]
[k:public] [k:class] [t:PaymentTypeRenderer]<[t:T]> [k:extends] [t:ComponentRenderer]<[t:Span], [t:T]> {

    [k:public] [m:PaymentTypeRenderer]([t:EnumMetaField]<[t:T], [t:PaymentType]> [p:field]) {
        [k:super]([p:entity] -> {
            [t:PaymentType] type = [p:field].[m:getGetter]().[m:apply]([p:entity]);
            [k:return] [k:new] [t:Span](type != [k:null] ? type.[m:getLabel]() : [s:""]);
        }
    }
}
                        

Валидация

Подключает кастомный валидатор к полю формы.


[a:@VaadinValidator]([t:PaymentTypeValidator].[k:class])
[k:private] [t:PaymentType] [v:type];
                        

[a:@Component]
[a:@PrototypeScope]
[k:public] [k:class] [t:PaymentTypeValidator]<[t:T]>
        [k:extends] [t:BaseValidator]<[t:T], [t:PaymentType], [t:EnumMetaField]<[t:T], [t:PaymentType]>> {

    [k:public] [m:PaymentTypeValidator]([t:EnumMetaField]<[t:T], [t:PaymentType]> [p:field]) {
        [k:super]([p:field]);
    }

    [a:@Override]
    [k:public] [k:void] [m:bindTo]([t:Binder].[t:BindingBuilder]<[t:T], [t:PaymentType]> [p:bindingBuilder]) {
        [k:super].[m:bindTo]([p:bindingBuilder]);
        [p:bindingBuilder].[m:withValidator]([p:type] -> [p:type] != [t:PaymentType].[p:CASH],
                [s:"Оплата наличными недоступна"]);
    }
}
                        

Доступ к данным

Общие понятия

VHS использует EntityManager для сохранения сущностей через встроенный компонент Dao.

При необходимости вы можете определить и использовать стандартные CRUD-репозитории — так же, как это принято в Spring Data.

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

Если ваша сущность содержит метод с аннотацией @PostConstruct, используйте dao.newInstance() для корректной инициализации. Такой метод может принимать любое количество Spring-бинов.


[k:public] [k:class] [t:User] {

    [k:private] [t:String] [v:name];

    [a:@PostConstruct]
    [k:void] [m:init]([t:MyService] [p:service]) {
        [k:this].[v:name] = [p:service].[m:randomName]();
    }
}
                        

[c:// Создание сущности с @PostConstruct]
[t:User] a = [v:dao].[m:newInstance]([t:User].[k:class]);

[c:// Создание сущности без @PostConstruct]
[t:User] b = [k:new] [t:User]();
                        

Сохранение сущностей

Если ваша сущность реализует Persistable или EntityInformation либо содержит поле @Id / @Version — вы можете использовать универсальный метод save. Он автоматически определит состояние сущности и вызовет persist или merge.

Подробнее о стратегиях определения состояния сущности смотрите в документации Spring Data JPA.


[c:// Сохранение сущности]
[v:dao].[m:save]([p:entity]);
                        

Как альтернатива, вы можете явно указать операцию:


[c:// Создание новой сущности]
[v:dao].[m:create]([p:entity]);

[c:// Обновление существующей сущности]
[v:dao].[m:update]([p:entity]);

[c:// Удаление существующей сущности]
[v:dao].[m:delete]([p:entity]);
                        

Выборка данных

Dao предоставляет fluent API для построения запросов непосредственно из Java-кода — без написания JPQL или SQL вручную.

Запрос строится цепочкой методов: фильтрация, подгрузка связанных сущностей, сортировка и ограничение выборки. В конце вызывается терминальный метод для получения списка или одной записи.


[t:List]<[t:Customer]> customers = [v:dao].[m:load]([t:Customer].[k:class])
        .[m:condition]([t:Condition].[m:startsWith]([s:"name"], [s:"Алекс"]))
        .[m:condition]([t:Condition].[m:isNotTrue]([s:"blocked"]))
        .[m:condition]([t:Condition].[m:or](
                 [t:Condition].[m:greaterThanOrEquals]([s:"age"], [p:18]),
                 [t:Condition].[m:in]([s:"id"], [t:List].[m:of]([p:1], [p:2], [p:3]))))
        .[m:join]([s:"orders"])
        .[m:limit]([p:10])
        .[m:orderBy]([s:"id"], [t:Sort].[t:Direction].[p:ASC])
        .[m:list]();
                        

Условия выборки

Полный список условий, доступных для построения запросов с использованием Condition:

Метод Описание
equals(field, value) Поле равно значению
notEquals(field, value) Поле не равно значению
isNull(field) Поле равно null
isNotNull(field) Поле не равно null
in(field, values) Поле входит в список значений
notIn(field, values) Поле не входит в список значений
Булевые
isTrue(field) Поле равно true
isNotTrue(field) Поле не равно true
isFalse(field) Поле равно false
isNotFalse(field) Поле не равно false
Сравнение чисел и дат
lessThan(field, value) Поле меньше значения
greaterThan(field, value) Поле больше значения
lessThanOrEquals(field, value) Поле меньше или равно значению
greaterThanOrEquals(field, value) Поле больше или равно значению
between(field, low, high) Поле находится в диапазоне от low до high включительно
notBetween(field, low, high) Поле находится вне диапазона от low до high
Строки
equalsIgnoreCase(field, string) Поле равно значению без учёта регистра
notEqualsIgnoreCase(field, string) Поле не равно значению без учёта регистра
isEmpty(field) Поле пустое или null
isNotEmpty(field) Поле не пустое и не null
isBlank(field) Поле пустое, null или состоит только из пробелов
isNotBlank(field) Поле не пустое, не null и содержит символы помимо пробелов
contains(field, string) Поле содержит подстроку
notContains(field, string) Поле не содержит подстроку
containsIgnoreCase(field, string) Поле содержит подстроку без учёта регистра
notContainsIgnoreCase(field, string) Поле не содержит подстроку без учёта регистра
startsWith(field, string) Поле начинается с указанной строки
notStartsWith(field, string) Поле не начинается с указанной строки
startsWithIgnoreCase(field, string) Поле начинается с указанной строки без учёта регистра
notStartsWithIgnoreCase(field, string) Поле не начинается с указанной строки без учёта регистра
endsWith(field, string) Поле заканчивается на указанную строку
notEndsWith(field, string) Поле не заканчивается на указанную строку
endsWithIgnoreCase(field, string) Поле заканчивается на указанную строку без учёта регистра
notEndsWithIgnoreCase(field, string) Поле не заканчивается на указанную строку без учёта регистра
like(field, pattern) Поле соответствует паттерну like
notLike(field, pattern) Поле не соответствует паттерну like
likeIgnoreCase(field, pattern) Поле соответствует паттерну без учёта регистра
notLikeIgnoreCase(field, pattern) Поле не соответствует паттерну без учёта регистра
Коллекции
containsAny(field, values) Поле содержит хотя бы одно из значений
containsAll(field, values) Поле содержит все указанные значения
containsNone(field, values) Поле не содержит ни одного из указанных значений
isEmpty(field) Коллекция пуста
isNotEmpty(field) Коллекция не пуста
Логические
and(conditions) Объединяет условия логическим и
or(conditions) Объединяет условия логическим или
not(condition) Инвертирует условие

Работа с файлами

Общие сведения

Для работы с файлами необходимо создать собственную сущность, реализующую интерфейс io.vhs.storage.File. После этого такой тип можно использовать в любых сущностях как обычное поле или коллекцию файлов. VHS автоматически создаст компонент загрузки, предпросмотр, скачивание и удаление файлов.

Описание сущности файла

Минимальное требование — реализовать интерфейс File. Остальная структура сущности определяется приложением и используемым способом хранения.


[a:@Entity]
[k:public class] [t:MyFile] [k:implements] [t:File] {
    [c:...]
}
                            

Использование собственного хранилища

По умолчанию VHS использует LocalFileStorage, сохраняющий файлы в локальной файловой системе. При необходимости можно указать собственную реализацию хранилища с помощью аннотации @FileStorage.

Аннотация может быть размещена как над классом файла, так и над отдельным полем:


[a:@Entity]
[a:@FileStorage]([t:MyFileStorage].[k:class])
[k:public class] [t:MyFile] [k:implements] [t:File] {
    [c:...]
}
                            

[a:@FileStorage]([t:MyFileStorage].[k:class])
[k:private] [t:MyFile] [v:file];
                            

Аннотация, указанная над полем, имеет приоритет над аннотацией, размещённой на классе. Это позволяет использовать разные хранилища для одной и той же сущности файла в зависимости от конкретного поля.

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

Навигация и меню

Для определения структуры приложения создайте класс, унаследованный от VhsLayout, и пометьте его аннотацией @Route. Навигационное меню формируется с помощью аннотаций @MenuItem, размещённых на этом же классе.


[a:@Route]([s:"/my-app"])
[a:@MenuItem]([p:view] = [t:DataView].[k:class], [p:path] = [s:"/users"],
          [p:title] = [s:"Пользователи"], [p:icon] = [t:VaadinIcon].[p:USER_CARD],
          [p:parameters] = [a:@Parameter]([p:clazz] = [t:User].[k:class]))
[k:public class] [t:MainView] [k:extends] [t:VhsLayout] {
}

Каждый @MenuItem описывает один пункт меню. Доступные параметры:

Параметр Описание
view Компонент-представление, которое будет открыто при переходе
path URL-путь для навигации
title Отображаемое название пункта меню
icon Иконка пункта меню из набора VaadinIcon
parameters Массив параметров, передаваемый в представление
subMenu Вложенные пункты меню @SubMenuItem

Ограничение доступа

Если вы определяете кастомный компонент, вы можете ограничить к нему доступ с помощью стандартных привычных аннотаций:

Аннотация Описание
@PreAuthorize Проверка условия доступа через SpEL
@Secured Ограничение доступа по названию роли
@RolesAllowed Ограничение доступа по роли в стиле Jakarta EE

[a:@PreAuthorize]([s:"hasRole('ADMIN')"])
[k:public class] [t:AdminView] [k:extends] [t:Div] {
    [c:...]
}
                        

Развертывание приложения

Общие сведения

С точки зрения развёртывания VHS не вносит никаких изменений — приложение собирается и запускается как обычное Vaadin-приложение на Spring Boot. Подробнее см. в документации Vaadin.

Политика в отношении обработки персональных данных

  1. Общие положения

    Настоящая политика обработки персональных данных составлена в соответствии с требованиями Федерального закона от 27.07.2006 № 152-ФЗ «О персональных данных» (далее — Закон о персональных данных) и определяет порядок обработки персональных данных и меры по обеспечению безопасности персональных данных, предпринимаемые Хохловым Александром Николаевичем (далее — Оператор).

    1. Оператор ставит своей важнейшей целью и условием осуществления своей деятельности соблюдение прав и свобод человека и гражданина при обработке его персональных данных, в том числе защиты прав на неприкосновенность частной жизни, личную и семейную тайну.
    2. Настоящая политика Оператора в отношении обработки персональных данных (далее — Политика) применяется ко всей информации, которую Оператор может получить о посетителях веб-сайта https://vhsframework.ru.
  2. Основные понятия, используемые в Политике
    1. Автоматизированная обработка персональных данных — обработка персональных данных с помощью средств вычислительной техники.
    2. Блокирование персональных данных — временное прекращение обработки персональных данных (за исключением случаев, если обработка необходима для уточнения персональных данных).
    3. Веб-сайт — совокупность графических и информационных материалов, а также программ для ЭВМ и баз данных, обеспечивающих их доступность в сети интернет по сетевому адресу https://vhsframework.ru.
    4. Информационная система персональных данных — совокупность содержащихся в базах данных персональных данных и обеспечивающих их обработку информационных технологий и технических средств.
    5. Обезличивание персональных данных — действия, в результате которых невозможно определить без использования дополнительной информации принадлежность персональных данных конкретному Пользователю или иному субъекту персональных данных.
    6. Обработка персональных данных — любое действие (операция) или совокупность действий (операций), совершаемых с использованием средств автоматизации или без использования таких средств с персональными данными, включая сбор, запись, систематизацию, накопление, хранение, уточнение (обновление, изменение), извлечение, использование, передачу (распространение, предоставление, доступ), обезличивание, блокирование, удаление, уничтожение персональных данных.
    7. Оператор — государственный орган, муниципальный орган, юридическое или физическое лицо, самостоятельно или совместно с другими лицами организующие и/или осуществляющие обработку персональных данных, а также определяющие цели обработки персональных данных, состав персональных данных, подлежащих обработке, действия (операции), совершаемые с персональными данными.
    8. Персональные данные — любая информация, относящаяся прямо или косвенно к определенному или определяемому Пользователю веб-сайта https://vhsframework.ru.
    9. Персональные данные, разрешенные субъектом персональных данных для распространения, — персональные данные, доступ неограниченного круга лиц к которым предоставлен субъектом персональных данных путем дачи согласия на обработку персональных данных, разрешенных субъектом персональных данных для распространения в порядке, предусмотренном Законом о персональных данных (далее — персональные данные, разрешенные для распространения).
    10. Пользователь — любой посетитель веб-сайта https://vhsframework.ru.
    11. Предоставление персональных данных — действия, направленные на раскрытие персональных данных определенному лицу или определенному кругу лиц.
    12. Распространение персональных данных — любые действия, направленные на раскрытие персональных данных неопределенному кругу лиц (передача персональных данных) или на ознакомление с персональными данными неограниченного круга лиц, в том числе обнародование персональных данных в средствах массовой информации, размещение в информационно-телекоммуникационных сетях или предоставление доступа к персональным данным каким-либо иным способом.
    13. Трансграничная передача персональных данных — передача персональных данных на территорию иностранного государства органу власти иностранного государства, иностранному физическому или иностранному юридическому лицу.
    14. Уничтожение персональных данных — любые действия, в результате которых персональные данные уничтожаются безвозвратно с невозможностью дальнейшего восстановления содержания персональных данных в информационной системе персональных данных и/или уничтожаются материальные носители персональных данных.
  3. Основные права и обязанности Оператора
    1. Оператор имеет право:
      • получать от субъекта персональных данных достоверные информацию и/или документы, содержащие персональные данные;
      • в случае отзыва субъектом персональных данных согласия на обработку персональных данных, а также направления обращения с требованием о прекращении обработки персональных данных, Оператор вправе продолжить обработку персональных данных без согласия субъекта персональных данных при наличии оснований, указанных в Законе о персональных данных;
      • самостоятельно определять состав и перечень мер, необходимых и достаточных для обеспечения выполнения обязанностей, предусмотренных Законом о персональных данных и принятыми в соответствии с ним нормативными правовыми актами, если иное не предусмотрено Законом о персональных данных или другими федеральными законами.
    2. Оператор обязан:
      • предоставлять субъекту персональных данных по его просьбе информацию, касающуюся обработки его персональных данных;
      • организовывать обработку персональных данных в порядке, установленном действующим законодательством РФ;
      • отвечать на обращения и запросы субъектов персональных данных и их законных представителей в соответствии с требованиями Закона о персональных данных;
      • сообщать в уполномоченный орган по защите прав субъектов персональных данных по запросу этого органа необходимую информацию в течение 10 дней с даты получения такого запроса;
      • публиковать или иным образом обеспечивать неограниченный доступ к настоящей Политике в отношении обработки персональных данных;
      • принимать правовые, организационные и технические меры для защиты персональных данных от неправомерного или случайного доступа к ним, уничтожения, изменения, блокирования, копирования, предоставления, распространения персональных данных, а также от иных неправомерных действий в отношении персональных данных;
      • прекратить передачу (распространение, предоставление, доступ) персональных данных, прекратить обработку и уничтожить персональные данные в порядке и случаях, предусмотренных Законом о персональных данных;
      • исполнять иные обязанности, предусмотренные Законом о персональных данных.
  4. Основные права и обязанности субъектов персональных данных
    1. Субъекты персональных данных имеют право:
      • получать информацию, касающуюся обработки его персональных данных, за исключением случаев, предусмотренных федеральными законами. Сведения предоставляются субъекту персональных данных Оператором в доступной форме, и в них не должны содержаться персональные данные, относящиеся к другим субъектам персональных данных, за исключением случаев, когда имеются законные основания для раскрытия таких персональных данных. Перечень информации и порядок ее получения установлен Законом о персональных данных;
      • требовать от оператора уточнения его персональных данных, их блокирования или уничтожения в случае, если персональные данные являются неполными, устаревшими, неточными, незаконно полученными или не являются необходимыми для заявленной цели обработки, а также принимать предусмотренные законом меры по защите своих прав;
      • выдвигать условие предварительного согласия при обработке персональных данных в целях продвижения на рынке товаров, работ и услуг;
      • на отзыв согласия на обработку персональных данных, а также на направление требования о прекращении обработки персональных данных;
      • обжаловать в уполномоченный орган по защите прав субъектов персональных данных или в судебном порядке неправомерные действия или бездействие Оператора при обработке его персональных данных;
      • на осуществление иных прав, предусмотренных законодательством РФ.
    2. Субъекты персональных данных обязаны:
      • предоставлять Оператору достоверные данные о себе;
      • сообщать Оператору об уточнении (обновлении, изменении) своих персональных данных.
    3. Лица, передавшие Оператору недостоверные сведения о себе, либо сведения о другом субъекте персональных данных без согласия последнего, несут ответственность в соответствии с законодательством РФ.
  5. Принципы обработки персональных данных
    1. Обработка персональных данных осуществляется на законной и справедливой основе.
    2. Обработка персональных данных ограничивается достижением конкретных, заранее определенных и законных целей. Не допускается обработка персональных данных, несовместимая с целями сбора персональных данных.
    3. Не допускается объединение баз данных, содержащих персональные данные, обработка которых осуществляется в целях, несовместимых между собой.
    4. Обработке подлежат только персональные данные, которые отвечают целям их обработки.
    5. Содержание и объем обрабатываемых персональных данных соответствуют заявленным целям обработки. Не допускается избыточность обрабатываемых персональных данных по отношению к заявленным целям их обработки.
    6. При обработке персональных данных обеспечивается точность персональных данных, их достаточность, а в необходимых случаях и актуальность по отношению к целям обработки персональных данных. Оператор принимает необходимые меры и/или обеспечивает их принятие по удалению или уточнению неполных или неточных данных.
    7. Хранение персональных данных осуществляется в форме, позволяющей определить субъекта персональных данных, не дольше, чем этого требуют цели обработки персональных данных, если срок хранения персональных данных не установлен федеральным законом, договором, стороной которого, выгодоприобретателем или поручителем по которому является субъект персональных данных. Обрабатываемые персональные данные уничтожаются либо обезличиваются по достижении целей обработки или в случае утраты необходимости в достижении этих целей, если иное не предусмотрено федеральным законом.
  6. Цели обработки персональных данных
    Цель обработки Предоставление доступа Пользователю к сервисам, информации и/или материалам, содержащимся на веб-сайте
    Персональные данные
    • фамилия, имя, отчество
    • электронный адрес
    • номера телефонов
    Правовые основания Федеральный закон «Об информации, информационных технологиях и о защите информации» от 27.07.2006 N 149-ФЗ
    Виды обработки
    персональных данных
    • Сбор, запись, систематизация, накопление, хранение, уничтожение и обезличивание персональных данных
    • Отправка информационных писем на адрес электронной почты
  7. Условия обработки персональных данных
    1. Обработка персональных данных осуществляется с согласия субъекта персональных данных на обработку его персональных данных.
    2. Обработка персональных данных необходима для достижения целей, предусмотренных международным договором Российской Федерации или законом, для осуществления возложенных законодательством Российской Федерации на оператора функций, полномочий и обязанностей.
    3. Обработка персональных данных необходима для осуществления правосудия, исполнения судебного акта, акта другого органа или должностного лица, подлежащих исполнению в соответствии с законодательством Российской Федерации об исполнительном производстве.
    4. Обработка персональных данных необходима для исполнения договора, стороной которого либо выгодоприобретателем или поручителем по которому является субъект персональных данных, а также для заключения договора по инициативе субъекта персональных данных или договора, по которому субъект персональных данных будет являться выгодоприобретателем или поручителем.
    5. Обработка персональных данных необходима для осуществления прав и законных интересов оператора или третьих лиц либо для достижения общественно значимых целей при условии, что при этом не нарушаются права и свободы субъекта персональных данных.
    6. Осуществляется обработка персональных данных, доступ неограниченного круга лиц к которым предоставлен субъектом персональных данных либо по его просьбе (далее — общедоступные персональные данные).
    7. Осуществляется обработка персональных данных, подлежащих опубликованию или обязательному раскрытию в соответствии с федеральным законом.
  8. Порядок сбора, хранения, передачи и других видов обработки персональных данных

    Безопасность персональных данных, которые обрабатываются Оператором, обеспечивается путем реализации правовых, организационных и технических мер, необходимых для выполнения в полном объеме требований действующего законодательства в области защиты персональных данных.

    1. Оператор обеспечивает сохранность персональных данных и принимает все возможные меры, исключающие доступ к персональным данным неуполномоченных лиц.
    2. Персональные данные Пользователя никогда, ни при каких условиях не будут переданы третьим лицам, за исключением случаев, связанных с исполнением действующего законодательства либо в случае, если субъектом персональных данных дано согласие Оператору на передачу данных третьему лицу для исполнения обязательств по гражданско-правовому договору.
    3. В случае выявления неточностей в персональных данных, Пользователь может актуализировать их самостоятельно, путем направления Оператору уведомление на адрес электронной почты Оператора mail@vhsframework.ru с пометкой «Актуализация персональных данных».
    4. Срок обработки персональных данных определяется достижением целей, для которых были собраны персональные данные, если иной срок не предусмотрен договором или действующим законодательством.
    5. Пользователь может в любой момент отозвать свое согласие на обработку персональных данных, направив Оператору уведомление посредством электронной почты на электронный адрес Оператора mail@vhsframework.ru с пометкой «Отзыв согласия на обработку персональных данных».
    6. Вся информация, которая собирается сторонними сервисами, в том числе платежными системами, средствами связи и другими поставщиками услуг, хранится и обрабатывается указанными лицами (Операторами) в соответствии с их Пользовательским соглашением и Политикой конфиденциальности. Субъект персональных данных и/или с указанными документами. Оператор не несет ответственность за действия третьих лиц, в том числе указанных в настоящем пункте поставщиков услуг.
    7. Установленные субъектом персональных данных запреты на передачу (кроме предоставления доступа), а также на обработку или условия обработки (кроме получения доступа) персональных данных, разрешенных для распространения, не действуют в случаях обработки персональных данных в государственных, общественных и иных публичных интересах, определенных законодательством РФ.
    8. Оператор при обработке персональных данных обеспечивает конфиденциальность персональных данных.
    9. Оператор осуществляет хранение персональных данных в форме, позволяющей определить субъекта персональных данных, не дольше, чем этого требуют цели обработки персональных данных, если срок хранения персональных данных не установлен федеральным законом, договором, стороной которого, выгодоприобретателем или поручителем по которому является субъект персональных данных.
    10. Условием прекращения обработки персональных данных может являться достижение целей обработки персональных данных, истечение срока действия согласия субъекта персональных данных, отзыв согласия субъектом персональных данных или требование о прекращении обработки персональных данных, а также выявление неправомерной обработки персональных данных.
  9. Перечень действий, производимых Оператором с полученными персональными данными
    1. Оператор осуществляет сбор, запись, систематизацию, накопление, хранение, уточнение (обновление, изменение), извлечение, использование, передачу (распространение, предоставление, доступ), обезличивание, блокирование, удаление и уничтожение персональных данных.
    2. Оператор осуществляет автоматизированную обработку персональных данных с получением и/или передачей полученной информации по информационно-телекоммуникационным сетям или без таковой.
  10. Трансграничная передача персональных данных
    1. Оператор до начала осуществления деятельности по трансграничной передаче персональных данных обязан уведомить уполномоченный орган по защите прав субъектов персональных данных о своем намерении осуществлять трансграничную передачу персональных данных (такое уведомление направляется отдельно от уведомления о намерении осуществлять обработку персональных данных).
    2. Оператор до подачи вышеуказанного уведомления обязан получить от органов власти иностранного государства, иностранных физических лиц, иностранных юридических лиц, которым планируется трансграничная передача персональных данных, соответствующие сведения.
  11. Конфиденциальность персональных данных

    Оператор и иные лица, получившие доступ к персональным данным, обязаны не раскрывать третьим лицам и не распространять персональные данные без согласия субъекта персональных данных, если иное не предусмотрено федеральным законом.

  12. Заключительные положения
    1. Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты mail@vhsframework.ru.
    2. В данном документе будут отражены любые изменения политики обработки персональных данных Оператором. Политика действует бессрочно до замены ее новой версией.
    3. Актуальная версия Политики в свободном доступе расположена в сети Интернет по адресу https://vhsframework.ru/privacy/.