LINUX.ORG.RU

Сообщения totik

 

Kubuntu + chromium + youtube

Поставила Kubuntu 20.10, попыталась накатить chromium из репозитория, но он подтянулся из snap. Когда захожу на youtube вижу такую картину: https://i.imgur.com/JKkDTki.png

Драйвер для видеокарты (RTX2070) используется nvidia-driver-455. В firefox при этом все работает как надо.

Кто-нибудь сталивался с подобным?

 , , , ,

totik ()

Совместимость планок памяти

Когда я собирала себе комп на Ryzen7 я приобрела для него одну планку памяти Kingston DDR4 16Gb 3466 MHz pc-27600 HyperX FURY Black (HX434C19FB/16). Сейчас ее стало не хватать, решила прикуть еще такой же, но точно такой же в продаже нет. Нашла Kingston DDR4 16Gb 3466 MHz pc-27700 HyperX FURY Black (HX434C16FB3/16).

Тут pc-27600, а там pc-27700. Заведутся ли вместе? Чем чревато?

 

totik ()

Оптимизация потоков данных

Имеется один сервер с СУБД (mysql/postgres) и 50 хостов. На каждом хосте запущено по 5-10 процессов, которые работают по следующей схеме: часть вычитывают все данные из определенных таблиц, часть только по определенному критерию. Затем все процессы реагируя на изменения данных подкачивают эти самые изменения.

Данные могут меняться раз в 4 секунды. Пока трудно спрогнозировать какой процент данных будет меняться и какой будет объем, но есть опасения о том, вытянет ли сервер нагрузку.

Хотелось бы оптимизировать потоки данных и избавиться от множественных обращений в рамках одного хоста.

Есть ли какие-нибудь решения? C++, Qt. Решения для джавы тоже будут интересны, для общего развития.

 , , ,

totik ()

Шаблонизатор для xml и JavaScript

Приветствую. Мне нужно собирать инсталлятор с помощью QtInstallerFramework из одних исходников для разных заказчиков. Большая часть схожа, но есть различия, в одних и тех же файлах xml и qs (JavaScript) могут присутствовать различные куски кода. Сборка вызывается из консоли вручную или в будущем с помощью jenkins. Не хочется иметь несколько наборов практически одних и тех же файлов, как решение в голову приходит использование шаблонизатора общего назначения. Есть ли такой? Или есть решение лучше?

 

totik ()

Уведомления о наличие изменений в репозиториях на экране блокировки

Работаю над какими-то личными проектами на рабочем месте (после основной работы) я недерко забываю коммитить или пушить изменения, из-за чего не могу продолжить разработку, придя домой. Имеются ли утилиты, которые при попытке заблокировать экран или выйти из системы, оповещают о наличие незакомиченных/незапушенных изменений в указанных в настойках репозиториях? Я пользуюсь plasma, но при наличии такой приблуды для других сред, готова была бы перейти на другую DE.

 ,

totik ()

Жизнь на последнем этаже

Привет всем. В данный момент я проживаю на девятом этаже. Каждый вечер я слышу как соседи сверху занимаются ёбгой, качают пресс, слушают Лепса. В данный момент я планирую покупку квартиры в прагма-сити. Варианты на последнем этаже смотрятся очень привлекательно, ибо никакие маргиналы больше меня не потревожат. Только вот беспокоит технический этаж, не сменится шум от соседей на шум от насосов и электродвигателей? Менеджер утверждает, что потому последний этаж и стоит дороже, ибо тишина гарантирована. Но мы то с вами понимаем, что менеджеру лишь бы продать.

В общем, у кого какой опыт проживания на последнем этаже, делитесь. Если есть проживающие в других ЖК прагмы, вообще здорово.

 

totik ()

Изменить scope стандартного бина

Имеется spring boot приложение, работающее с различными БД через JPA2 (hibernate, spring-data).

Во время работы приложения сервера баз данных могут меняться (как адреса, так и сами субд), должна выполняться валидация существующих БД средствами hibernate и создание схемы по желанию пользователя.

То есть задание статических параметров DataSource не годится. spring.jpa.hibernate.ddl-auto анализируется при создании бина то ли entityManagerFactory, то ли entityManagerFactoryBuilder, если мне не изменяет память.

Вопрос первый: Нормальный ли это подход, если я изменю scope этого бина с singleton на prototype и при изменении параметров подключения просто буду получать новый экземпляр этого класса?

Минус, который я вижу: нельзя будет использовать Autowired в singleton-бинах для бинов производных от entityManagerFactory, в том числе и PersistentContext. Придется получать эти бины через мой бин, производящий изменения в DataSource и пересоздание entityManagerFactory.

Вопрос второй: Как мне используя spring-boot с конфигурированием контекста через Java-класс изменить только scope для конкретного стандартного бина? Если описывать в Java-классе конфигурации этот бин, задавая через аннотации scope, то придется указывать тип, пробрасывать параметры, а мне не хотелось бы этим заниматься.

 , ,

totik ()

Посоветуйте литературу по JavaScript

Всех приветствую. Некоторое время назад мне захотелось освоить Java в качестве универсального языка, на котором можно и настольное приложение написать и серверное. Я прочитала книгу Герберта Шилдта по Java 8, но этого ожидаемо оказалось мало для того, чтобы начать кодить что-то полезное. Следующей книгой стала Spring4 для профессионалов. После ее прочтения минимальный набор необходимых технологий был освоен и я стала писать что-то выполняющее полезную работу. Сейчас заканчиваю небольшой сайт (spring mvc, thymeleaf, bootstrap). Он работает, выглядит строго, запросы генерирует на мой взгляд эффективные. Но он нифига не удобный. WEB 0.9, елки-палки. Не хватает асинхронности для валидации форм, обновления результатов фильтрации при изменении параметров. Надо изучать JavaScript и какой-нибудь универсальный фреймворк. Пары статей не хватит, ибо говнокодить не хочется. Но и слишком глубоко погружаться не хотелось бы, ибо всё-таки мне больше интересен бэкэнд, а не фронтэнд. Посоветуйте литературу, желательно на русском языке, которая как в моем случае с джавой даст минимальный набор навыков и знаний, необходимый для осмысленной разработки. Как я уже поняла, одного языка недостаточно, нужен и фреймворк, потому желательно, чтобы книга, посвященная JavaScript охватывала и какой-нибудь универсальный фреймворк, с примерами применения на практике.

 

totik ()

Эффективные запросы на JPQL

Все пытаюсь научиться эффективно работать с JPA, да выходит не очень =)

У меня есть такая вот модель:

@Entity
public class Organization extends AbstractIdentifiable implements Serializable {
    @Column(nullable = false)
    private String title;

    @ManyToMany(mappedBy = "organizations")
    private Set<Member> members = new HashSet<>();

    @ManyToMany(mappedBy = "customerOrganization")
    private Set<Video> videos = new HashSet<>();

    @ManyToOne
    @JoinColumn
    private Playlist playlist;
}

@Entity
public class Video extends AbstractIdentifiable implements Serializable {
    @ManyToOne
    private Organization customerOrganization;

    @OneToMany(mappedBy = "video")
    private Set<ShownVideo> shownVideos = new HashSet<>();
}

@Entity
public class ShownVideo extends AbstractIdentifiable implements Serializable {
    @ManyToOne
    @JoinColumn(nullable = false)
    private Video video;
}

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

Данную задачу решаю с помощью spring-data:

public interface OrganizationRepository extends JpaRepository<Organization, Long> {
    @Query("SELECT new OrganizationCount(o, COUNT(sv)) " +
           "FROM Organization o JOIN o.members m " +
           "LEFT JOIN o.videos v " +
           "LEFT JOIN v.shownVideos sv " +
           "WHERE m = :member " +
           "GROUP BY o " +
           "ORDER BY o.title")
    public List<OrganizationCount> organizationVideoShowCountsByMember(@Param(value = "member") Member member);

}

public class OrganizationCount {
    private Organization organization;
    private long count;

    public OrganizationCount(Organization organization, long count) {
        this.organization = organization;
        this.count = count;
    }

    public Organization getOrganization() {
        return organization;
    }

    public long getCount() {
        return count;
    }
}

Данная задача решена, но очень неэффективно. Сначала выполняется корректный запрос на получение статистики:

Hibernate: select organizati0_.id as col_0_0_, count(shownvideo3_.id) as col_1_0_ from Organization organizati0_ left outer join Video videos1_ on organizati0_.id=videos1_.customerOrganization_id left outer join Video video2_ on videos1_.id=video2_.id left outer join shown_video shownvideo3_ on video2_.id=shownvideo3_.video_id group by organizati0_.id order by organizati0_.title
А затем на каждую полученную строку выполняется запрос, на получение организации:
Hibernate: select organizati0_.id as id1_3_0_, organizati0_.playlist_id as playlist3_3_0_, organizati0_.title as title2_3_0_, playlist1_.id as id1_5_1_, playlist1_.title as title2_5_1_ from Organization organizati0_ left outer join Playlist playlist1_ on organizati0_.playlist_id=playlist1_.id where organizati0_.id=?
Hibernate: select organizati0_.id as id1_3_0_, organizati0_.playlist_id as playlist3_3_0_, organizati0_.title as title2_3_0_, playlist1_.id as id1_5_1_, playlist1_.title as title2_5_1_ from Organization organizati0_ left outer join Playlist playlist1_ on organizati0_.playlist_id=playlist1_.id where organizati0_.id=?
В примере у меня их всего два. Но если будет 10 организаций будет 10 запросов. Это неимоверно тупо, учитывая то, что в первом запросе есть все колонки для формирования объекта организации. Подскажите как забороть эту ересь?

Дополнено: Если вместо

SELECT new com.helan.adkiosk.webserver.controllers.data.aggreate.OrganizationCount(o, COUNT(sv))"
писать так
SELECT o, COUNT(sv)
тогда число запросов меняется:
Hibernate: select organizati0_.id as col_0_0_, count(shownvideo3_.id) as col_1_0_, organizati0_.id as id1_3_, organizati0_.playlist_id as playlist3_3_, organizati0_.title as title2_3_ from Organization organizati0_ left outer join Video videos1_ on organizati0_.id=videos1_.customerOrganization_id left outer join Video video2_ on videos1_.id=video2_.id left outer join shown_video shownvideo3_ on video2_.id=shownvideo3_.video_id group by organizati0_.id order by organizati0_.title
Hibernate: select playlist0_.id as id1_5_0_, playlist0_.title as title2_5_0_ from Playlist playlist0_ where playlist0_.id=?

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

Но и тут косяк, ибо потом для организаций, для которых имеются плейлисты производится выборка этих плейлистов. Они мне в принципе не нужны в том месте и их можно было бы не выбирать вовсе. В сущности Organization

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private Playlist playlist;
меняла FetchType.LAZY на FetchType.EAGER - ничего не изменилось.

 , ,

totik ()

No suitable driver found for jdbc:postgresql

Имеется spring-mvc проект. На локальной системе через mvn tomcat7:run запускается без проблем. При попытке деплоя на удаленный сервер (centos7/tomcat7 из репозитория) получаю следующее:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/spring/datasource-tx-jpa.xml]: Invocation of init method failed; nested exce
<------>at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1634)
<------>at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
<------>at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
<------>at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
<------>at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
<------>at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
<------>at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:220)
<------>at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1018)
<------>at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:988)
<------>at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:579)
<------>at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:546)
<------>at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:707)
<------>at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:680)
<------>at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
<------>at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
<------>at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354)
<------>... 76 more
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to open JDBC Connection for DDL execution
<------>at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:396)
<------>at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:371)
<------>at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:336)
<------>at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1692)
<------>at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1630)
<------>... 91 more
Caused by: org.hibernate.exception.JDBCConnectionException: Unable to open JDBC Connection for DDL execution
<------>at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:115)
<------>at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
<------>at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
<------>at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
<------>at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:69)
<------>at org.hibernate.tool.schema.internal.exec.ImprovedExtractionContextImpl.getJdbcConnection(ImprovedExtractionContextImpl.java:60)
<------>at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.extractMetadata(SequenceInformationExtractorLegacyImpl.java:40)
<------>at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.initializeSequences(DatabaseInformationImpl.java:65)
<------>at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.<init>(DatabaseInformationImpl.java:59)
<------>at org.hibernate.tool.schema.internal.Helper.buildDatabaseInformation(Helper.java:132)
<------>at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:61)
<------>at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:191)
<------>at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72)
<------>at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310)
<------>at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467)
<------>at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939)
<------>at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
<------>at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:360)
<------>at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:384)
<------>... 95 more
Caused by: java.sql.SQLException: No suitable driver found for jdbc:postgresql://192.168.0.101/test
<------>at java.sql.DriverManager.getConnection(DriverManager.java:689)
<------>at java.sql.DriverManager.getConnection(DriverManager.java:208)
<------>at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriverManager(DriverManagerDataSource.java:153)
<------>at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriver(DriverManagerDataSource.java:144)
<------>at org.springframework.jdbc.datasource.AbstractDriverBasedDataSource.getConnectionFromDriver(AbstractDriverBasedDataSource.java:196)
<------>at org.springframework.jdbc.datasource.AbstractDriverBasedDataSource.getConnection(AbstractDriverBasedDataSource.java:159)
<------>at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
<------>at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180)
<------>at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:43)
<------>... 109 more

Конфиги одинаковые. В pom.xml прописан драйвер, пробовала разные версии, на десктопе работает, на сервере нет:

        <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.4.1212</version>
        </dependency>
jar вместе с другими зависимостями попадает в /lib каталог проекта:
[root@dev lib]# pwd
/usr/share/tomcat/webapps/ROOT/WEB-INF/lib
[root@dev lib]# find . -name "*postgresql*"
./postgresql-9.4.1212.jar
[root@dev lib]# 
В чем может быть проблема? Tomcat из репов centos привносит свою магию. Сначала билась над тем, что hibernate-validator шестой ветки с ним несовместим из-за старого tomcat-el, теперь вот это. Гуглёж ничего не дал.

 , , , ,

totik ()

Вынести в фрагмент Thymeleaf часть формы

В моем проекте две JPA сущности имеют свойство типа Organization с разными именами:

@Entity
public class Video extends AbstractIdentifiable implements Serializable {

    @ManyToOne
    @JsonIgnore
    @JoinColumn(nullable = false)
    private Organization customerOrganization;

    public Organization getCustomerOrganization() {
        return customerOrganization;
    }

    public void setCustomerOrganization(Organization customerOrganization) {
        this.customerOrganization = customerOrganization;
    }

}
@Entity
public class Location extends AbstractIdentifiable implements Serializable {

    @ManyToOne
    @JoinColumn(nullable = false)
    @JsonIgnore
    private Organization organization;

    public Organization getOrganization() {
        return organization;
    }

    public void setOrganization(Organization organization) {
        this.organization = organization;
    }

}

Лишнее удалено. У меня есть форма в шаблоне thymeleaf для Location, привожу часть, ответственную за редактирование Organization:

<div class="col-md-4 mb-3">
    <label for="organization" th:text="#{organization}"/>
    <select class="form-control" id="organization" name="organization"
            th:attrappend="class=${#fields.hasErrors('organization') ? ' is-invalid' : ''}"
            th:field="*{organization}">
        <option th:each="organization: ${organizations}"
                th:value="${organization.id}"
                th:text="${organization.title}"/>
    </select>
    <div class="invalid-feedback" th:if="${#fields.hasErrors('organization')}">
        <p th:each="error: ${#fields.errors('organization')}" th:text="${error}"/>
    </div>
</div>

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

Вышло у меня следующее: Приведенный выше блок заменен на:

<div th:replace="~{forms :: organization('organization', *{organization})}"/>

Сам фрагмент:

<div th:fragment="organization(fieldName, organization)" class="col-md-4 mb-3">
    <label th:for="${fieldName}" th:text="#{organization}"/>
    <select th:id="${fieldName}" th:name="${fieldName}"
            th:attrappend="class=${#fields.hasErrors(fieldName) ? ' is-invalid' : ''}"
            th:field="${organization}" class="form-control">
        <option th:each="organization: ${organizations}" th:object="${organization}"
                th:value="*{id}"
                th:text="*{title}"/>
    </select>
    <div class="invalid-feedback" th:if="${#fields.hasErrors(${fieldName})}">
        <p th:each="error: ${#fields.errors(${fieldName})}" th:text="${error}"/>
    </div>
</div>

В результате выполнения получаю ошибку:

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'organization' available as request attribute

Проблема с th:field, ибо он в фрагменте он не видит форму, как я поняла. Подскажите, как забороть сие? Может быть для многократного использования шаблонов themeleaf в формах есть другой способ, помимо фрагментов?

 , , ,

totik ()

Аналог OMV для CentOS

Посоветуйте легковесную веб-морду, которая позволит мониторить состояние моего домашнего сервера (он же NAS) под управлением CentOS.

Для Debian есть OpenMediaVault. Для меня это как из пушки по воробьям, по сути кроме мониторинга ресурсов (cpu, RAM, HDD, net) и процессов, температуры ничего не нужно. Хотелось бы, чтобы был не только мониторинг, но и логирование. Например, сменила я кулер, и увидела как изменилась кривая графика.

Видела CWP, но оно очень тяжеловесное и как я поняла для больше управления хостингом. Если вам известны и комбайны типа OMV, давайте их тоже до кучи, для общего развития ознакомлюсь.

 ,

totik ()

Named entity graph сразу для двух списков портит данные

Есть у меня такая БД. В JPA сущностях описана так. Playlist:

@Entity
@NamedEntityGraph(
        name = "Playlist.full",
        attributeNodes = {@NamedAttributeNode(value = "playlistItems"), @NamedAttributeNode(value = "organizations")})
public class Playlist implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @NotBlank
    @Column(nullable = false)
    private String title;

    @OneToMany(mappedBy = "playlist")
    @OrderBy("number ASC")
    @JsonIgnore
    private List<PlaylistItem> playlistItems = new ArrayList<>();

    @OneToMany(mappedBy = "playlist")
    @JsonIgnore
    private Set<Organization> organizations = new HashSet<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<PlaylistItem> getPlaylistItems() {
        return playlistItems;
    }

    public void setPlaylistItems(List<PlaylistItem> playlistItems) {
        this.playlistItems = playlistItems;
    }

    public Set<Organization> getOrganizations() {
        return organizations;
    }

    public void setOrganizations(Set<Organization> organizations) {
        this.organizations = organizations;
    }
}

PlaylistItem:

@Entity(name = "playlist_item")
public class PlaylistItem extends AbstractIdentifiable implements Serializable {

    @Column(nullable = false)
    private Integer number;

    @JoinColumn(nullable = false)
    @ManyToOne
    @JsonIgnore
    private Video video;

    @JoinColumn(nullable = false)
    @ManyToOne
    @JsonIgnore
    private Playlist playlist;

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public Video getVideo() {
        return video;
    }

    public void setVideo(Video video) {
        this.video = video;
    }

    public Playlist getPlaylist() {
        return playlist;
    }

    public void setPlaylist(Playlist playlist) {
        this.playlist = playlist;
    }
}

Organization:

@Entity
public class Organization extends AbstractIdentifiable implements Serializable {

    @Column(nullable = false)
    @NotBlank
    private String title;

    @OneToMany(mappedBy = "organization")
    @JsonIgnore
    private Set<Location> locations = new HashSet<>();

    @JsonIgnore
    @ManyToMany(mappedBy = "organizations")
    private Set<Member> members = new HashSet<>();

    @ManyToOne
    @JoinColumn
    @JsonIgnore
    private Playlist playlist;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Set<Location> getLocations() {
        return locations;
    }

    public void setLocations(Set<Location> locations) {
        this.locations = locations;
    }

    public Set<Member> getMembers() {
        return members;
    }

    public void setMembers(Set<Member> members) {
        this.members = members;
    }

    public Playlist getPlaylist() {
        return playlist;
    }

    public void setPlaylist(Playlist playlist) {
        this.playlist = playlist;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Organization that = (Organization) o;
        return Objects.equals(getId(), that.getId()) &&
                Objects.equals(title, that.title);
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), title);
    }
}

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

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

Разрабатывая на C++ я привыкла реализовывать выборку данных минимизируя число запросов, избегая запросы в циклах, чтобы по возможности их число не зависело от данных. Думаю все согласятся, что это правильный подход.

Ленивая загрузка данных сразу отпадает в этом случае, потому я использую named entity graphs. Мне казалось, что это панацея, но при загрузке сущности Playlist по id я получаю невалидные данные.

Сервисный класс получения данных:

@Transactional
@Service(value = "dataService")
public class DataServiceImpl implements DataService {

    private static final Logger logger = Logger.getLogger(DataServiceImpl.class);
    private static final String hint = "javax.persistence.fetchgraph";

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public <T> T get(Class<T> entityClass, Long id) {
        return entityManager.find(entityClass, id);
    }

    @Override
    public <T> T get(Class<T> entityClass, Long id, String entityGraph) {
        Map<String,Object> hints = new HashMap<>();
        hints.put(hint, entityManager.getEntityGraph(entityGraph));
        return entityManager.find(entityClass, id, hints);
    }

    @Override
    public <T> List<T> getAll(Class<T> entityClass) {
        return getAll(entityClass, null);
    }

    @Override
    public <T> List<T> getAll(Class<T> entityClass, String entityGraph) {
        CriteriaQuery<T> cq = entityManager.getCriteriaBuilder().createQuery(entityClass);
        Root<T> rootEntry = cq.from(entityClass);
        CriteriaQuery<T> all = cq.select(rootEntry);
        TypedQuery<T> allQuery = entityManager.createQuery(all);
        if (entityGraph != null) {
            allQuery.setHint(hint, entityManager.getEntityGraph(entityGraph));
        }
        return allQuery.getResultList();
    }

    @Override
    public <T> T getByColumn(Class<T> entityClass, String field, Object value) {
        try {
            CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
            CriteriaQuery<T> criteria = criteriaBuilder.createQuery(entityClass);
            Root<T> root = criteria.from(entityClass);
            criteria.select(root);
            criteria.where(criteriaBuilder.equal(root.get(field), value));
            return entityManager.createQuery(criteria).getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    @Override
    public <T> List<T> getListByColumn(Class<T> entityClass, String field, Object value) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<T> cq = criteriaBuilder.createQuery(entityClass);
        Root<T> rootEntry = cq.from(entityClass);
        CriteriaQuery<T> all = cq.select(rootEntry).where(criteriaBuilder.equal(rootEntry.get(field), value));
        TypedQuery<T> allQuery = entityManager.createQuery(all);
        return allQuery.getResultList();
    }

    @Override
    public void persist(Object entity) {
        entityManager.persist(entity);
    }

    @Override
    public Object merge(Object entity) {
        return entityManager.merge(entity);
    }

    @Override
    public void remove(Object entity) {
        entityManager.remove(entityManager.merge(entity));
    }
}

Давайте рассмотрим второй случай, получение списка организаций и элементов по выбранному плейлисту:

    @RequestMapping(value = "/{id}/items/edit", method = RequestMethod.GET)
    public String itemsEdit(@PathVariable(name = "id") Long id, Model model) {
        if (!model.containsAttribute("playlist")) {
            Playlist playlist = dataService.get(Playlist.class, id, "Playlist.full");
            if (playlist == null) {
                //TODO: отобразить сообщение
                return "redirect:/admin/playlists";
            } else {
                model.addAttribute("playlist", playlist);
            }
        }
        model.addAttribute("videos", dataService.getAll(Video.class));
        return "admin/playlists/items";
    }

В итоге формируется hibernate генерирует такой запрос:

select playlist0_.id as id1_5_0_, playlist0_.title as title2_5_0_, organizati1_.playlist_id as playlist3_3_1_, organizati1_.id as id1_3_1_, organizati1_.id as id1_3_2_, organizati1_.playlist_id as playlist3_3_2_, organizati1_.title as title2_3_2_, playlistit2_.playlist_id as playlist3_6_3_, playlistit2_.id as id1_6_3_, playlistit2_.id as id1_6_4_, playlistit2_.number as number2_6_4_, playlistit2_.playlist_id as playlist3_6_4_, playlistit2_.video_id as video_id4_6_4_ 
from Playlist playlist0_ 
left outer join Organization organizati1_ on playlist0_.id=organizati1_.playlist_id 
left outer join playlist_item playlistit2_ on playlist0_.id=playlistit2_.playlist_id 
where playlist0_.id=? order by playlistit2_.number asc

Для плейлиста, у которого есть две организации и два элемента плейлиста я получаю сущность Playlist, у которой есть четыре организации и четыре плейлиста. Причина понятна, сначала мы выбираем плейлист - это одна запись. Потом делаем left join организаций, выходит две записи. Затем делаем left join для полученных для полученных двух записей еще двух записей.

Как правильнее в данной ситуации с помощью JPA получить корректные данные минимумом запросов и телодвижений?

Я попробовала так:

@Entity
@NamedEntityGraphs({
        @NamedEntityGraph(
                name = "Playlist.organizations",
                attributeNodes = {@NamedAttributeNode(value = "organizations")}),
        @NamedEntityGraph(
                name = "Playlist.playlistItems",
                attributeNodes = {@NamedAttributeNode(value = "playlistItems", subgraph = "video")},
                subgraphs = {@NamedSubgraph(name = "video", attributeNodes = @NamedAttributeNode("video"))})
})
public class Playlist implements Serializable {
...

И вместо:

            Playlist playlist = dataService.get(Playlist.class, id, "Playlist.full");
Пишем:
            Playlist playlist = dataService.get(Playlist.class, id, "Playlist.organizations");
            playlist = dataService.get(Playlist.class, id, "Playlist.playlistItems");
Тогда выходит то, что нужно. Но это как-то костыльно выглядит.

Теперь первый случай, отображение списка плейлистов с числом организаций и элементов. Проблема та же из-за двух left join'ов. Но вопрос такой: как правильно запросить только число этих связанных сущностей? На каждый случай писать свои запросы с помощью NamedQuery или CriteriaBuilder? Или же есть способ используя сущности JPA, но без фактического выкачивания данных?

 , , , ,

totik ()

Легковесные контейнеры

У меня есть домашний сервачок, который раздает по сети фото, видео, музыку. На нем стоял арч и в течении трех лет все было отлично, пока я не обновила его находясь в отпуске (да, глупый поступок). На связь он уже не вышел. Теперь думаю сменить его на что-то более стабильное: centos или debian.

Еще одна проблема, которую я хотела бы решить. Пока я пыталась подобрать софт, мной были установлены и удалены plex, ampache, subsonic и еще куча софта. Что-то не тянуло необходимые зависимости, их я доустанавливала сама, а после удаления основного пакета, оставались эти потерянные зависимости и прочие потроха в виде конфигов.

Посоветуйте инструмент, для создания контейнеров, в рамках которых можно было бы гадить зависимостями, пакетами, конфигами и прочим, но при удалении контейнера, чтобы сама система оставалось девственно чистой. VirtualBox - слишком жирно, хотелось бы, чтобы ядро, окружение, уже установленные в системе пакеты расшаривались между контейнерами, а сами контейнеры были изолированы между друг другом.

Ну и какой дистрибутив лучше подойдет под это дело: centos или debian. Другие не рассматриваю, потому что мне кажется эти два достаточно хороши, чтобы не обращать внимание на другие.

 , ,

totik ()

Редактирование сущности через форму

Есть сущность JPA:

@Entity
public class Video extends AbstractIdentifiable implements Serializable {

    @Column(nullable = true)
    @Temporal(value = TemporalType.TIMESTAMP)
    @JsonFormat(shape=JsonFormat.Shape.STRING, pattern=Format.DATETIME_FORMAT)
    private Calendar uploadDatetime;

    @ManyToOne
    @JsonIgnore
    @JoinColumn(nullable = false)
    private Member uploadMember;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String extension;

    @OneToMany(mappedBy = "video")
    @JsonIgnore
    private Set<VideoDisplay> videoDisplays = new HashSet<>();

    public Calendar getUploadDatetime() {
        return uploadDatetime;
    }

    public void setUploadDatetime(Calendar uploadDatetime) {
        this.uploadDatetime = uploadDatetime;
    }

    public Member getUploadMember() {
        return uploadMember;
    }

    public void setUploadMember(Member uploadMember) {
        this.uploadMember = uploadMember;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getExtension() {
        return extension;
    }

    public void setExtension(String extension) {
        this.extension = extension;
    }

    public Set<VideoDisplay> getVideoDisplays() {
        return videoDisplays;
    }

    public void setVideoDisplays(Set<VideoDisplay> videoDisplays) {
        this.videoDisplays = videoDisplays;
    }
}

Хочу через форму дать пользователю возможность редактировать title. Controller:

    @RequestMapping(value = "/{id}/edit", method = RequestMethod.GET)
    public String edit(@PathVariable(name = "id") Long id, Model model) {
        if (!model.containsAttribute("video")) {
            Video video = dataService.get(Video.class, id);
            if (video == null) {
                //TODO: отобразить сообщение
                return "redirect:/admin/videos";
            } else {
                model.addAttribute("video", video);
            }
        }
        return "admin/videos/edit";
    }

    @RequestMapping(value = "/{id}/edit", method = RequestMethod.POST)
    public String editPerform(@PathVariable(name = "id") Long id, @Valid Video video, BindingResult bindingResult, Model model) {
        if (bindingResult.hasErrors()) {
            return edit(id, model);
        }
        dataService.merge(video);
        return ("redirect:/admin/videos");
    }

Template:

<div class="card-body" th:fragment="form_edit">
        <form class="needs-validation" novalidate method="post" th:action="@{${#httpServletRequest.requestURI}}" th:object="${video}">
            <div class="form-row">
                <div class="col-md-4 mb-3">
                    <label for="title" th:text="#{title}"/>
                    <input type="text" class="form-control" th:attrappend="class=${#fields.hasErrors('title') ? ' is-invalid' : ''}" id="title" th:placeholder="#{title}" th:field="*{title}" required/>
                    <div class="invalid-feedback" th:if="${#fields.hasErrors('title')}">
                        <p th:each="error: ${#fields.errors('title')}" th:text="${error}"/>
                    </div>
                </div>
                <div class="col-md-4 mb-3">
                    <label for="extension" th:text="#{extension}"/>
                    <input readonly type="text" class="form-control" th:attrappend="class=${#fields.hasErrors('extension') ? ' is-invalid' : ''}" id="extension" th:placeholder="#{extension}" th:field="*{extension}" required/>
                    <div class="invalid-feedback" th:if="${#fields.hasErrors('extension')}">
                        <p th:each="error: ${#fields.errors('extension')}" th:text="${error}"/>
                    </div>
                </div>
            </div>
            <button class="btn btn-primary" type="submit" th:text="#{submit}"/>
        </form>
    </div>

В итоге в editPerform приходит сущность c заполненными только title и extension и как следствие выполняется update с null колонками:

update Video set extension=?, title=?, uploadDatetime=?, uploadMember_id=? where id=?
Есть ли способ обновлять только те поля, которые заданы в форме? Или заполнять объект сущностью из базы, устанавливать ему значения из объект пришедшего из формы и затем уже сохранять его?

 , ,

totik ()

Уровень доступа к данным в веб-сервере (java, spting-mvc, jpa)

В рамках изучения java пишу небольшой сайт / restful-api-сервер c использованием spring-mvc, jpa, hibernate. В данной книге, да и во многих статьях в интернетах слой доступа к данным предлагают размазывать по множеству интерфейсов. Как правило в примерах приводится работа с одной единственной сущностью, например Contact, для работы с которой описывается интерфейс ContactService с набором методов аля удалить, обновить, найти по колонке, вернуть все, класс ContactServiceImpl - реализация обращающаяся к методам ContactRepository - интерфейс, наследуемый от CrudRepository<Contact, Long>. Получается как минимум два интерфейса и один класс на сущность. А если у меня этих сущностей тридцать штук? Подскажите best practices по реализации уровня доступа к данным. Может быть есть менее многословный подход?

Я бы описала бы один интерфейс с шаблонными методами для CRUD и при необходимости добавляла бы в него методы для сложных запросов, в реализации этого интерфейса использовала бы EntityManager напрямую. Но чувствую за такой подход меня закидали бы тухлыми помидорами. Почему?

 , , , ,

totik ()

spring, hibernate, postgresql

У меня есть проект, в котором используется сабж. Имеются сущности с маппингами через аннотации. В сущностях используется enum postgresql. Если я пытаюсь разворачивать БД с помощью свойства hibernate.hbm2ddl.auto, то это заканчивается ошибкой, ибо перечислений нет и hibernate не может генерировать для них DDL. Есть предварительно создать в БД вручную перечисления, про развертывание проходит корректно. Вопрос 1: может ли hibernate автоматически генерировать ddl и для перечислений? Вопрос 2: как мне при выполнении развертывания БД предварительно вызвать скрипт, в котором будут прописано создание перечислений?

 , , ,

totik ()

Java и desktop

Я конечно понимаю, что Java на десктоп ориентированна меньше, чем на другие области применения, однако отсутствие возможности централизованно управлять стилем отображения и особенно шрифтами swing/javafx на мой взгляд какое-то недоразумение.

Подход применяемый в gtk и qt позволяет работать в разных приложениях, отображаемых в едином стиле. Изменил глобальные настройки отображения - все подхватили по умолчанию. Если приложению требуется какая-либо кастомизация, разработчик реализует ее.

Если речь идет о swing, то подобное поведение наблюдается только при использовании GTKLookAndFeel. И ведь можно даже задать его использование по умолчанию с помощью Dswing.defaultlaf в _JAVA_OPTIONS.

Малину портят приложения, которые принудительно устанавливают стиль отображения, например DBVis. GTKLookAndFeel не доступен принципиально, выбирать приходится между Metal и несколькими убогими темами в самом приложении. Хорошо, будь по вашему, беру Metal. Но что это за убожество вместо шрифтов? Само приложение позволяет поменять шрифты только для таблиц и редактора, так что приходится наслаждаться мелкими для моего разрешения, но полужирными шрифтами в меню. Неужели нет никакой возможности это исправить?

С javafx как я понимаю все еще хуже. Нет никакого аналога Dswing.defaultlaf. Хочешь чтобы твое приложение выглядело стильно? Интегрируй в него темку. Хочешь, чтобы все javafx приложения выглядели стильно? Мечтай, это невозможно.

Ну как так то? Доколе? Если я в чем-то не права и просто не умею готовить джаву, научите.

Пример: https://ibb.co/e8Y6e7

 , , ,

totik ()

Покупка ноутбука с доставкой из Москвы

Решилась я на покупку ноутбука с 1070 и 4K монитором. Сначала выбор мой пал на Acer Predator 17 G9-793-7488 NH.Q19ER.001 тут https://www.onno.ru/sankt-peterburg/witem/197960/ Стоил он 108к, что неприлично дешево для такой машинки, но пока я набирала номер телефона магазина, его купили.

Сейчас остановилась на ACER PREDATOR 17 G9-793-70DL тут http://techrex.ru/product/noutbuk-acer-predator-g9-793-72ks-intel-core-i7-670...

Созвонилась в продавцом, говорит что ноут в заводской упаковке. Переводишь на карту его бухгалтера деньги (ХА!) или переводом на юр. лицо и получаешь доставкой СДЭКом. Есть шанс получить ноут после ремонта, но можно попросить его сфоткать коробку в пленке. Остается проблема с битыми пикселями. Продавец говорит, что тогда можно решать с ним вопрос, типа отправишь обратно ноут, а я тебе деньги. Но что-то мне подсказывает, что это сказка. Еще есть опасность, что деньги уйдут и продавец исчезнет. Однако этот ИП работает уже три года. Отзывы большей частью хорошие, но есть парочка о том, что им продали ноуты после ремонта. Но, как и я говорила, фото ноута в пленке может как-то уберечь в этой ситуации.

В общем, что вы думаете о выбранном мной ноутбуке и о покупке с доставкой из другого города? Кто-нибудь из лоровцев покупал что-нибудь таким образом в магазине techrex.ru? С какими проблемами сталкивались?

 

totik ()

Захотелось шустренький ноутбук

Мой ноутбук HP DM4 2000er в эти зимние каникулы окончательно меня выбесил задумчивостью, маленьким разрешением и убогой клавиатурой и я задумалась о том, чтобы продать его, десктоп, моник и вместо всего этого разнообразия железа купить один нормальный ноут.

Требования простые: SSD+HDD, от 16 Gb оперативы включительно, четырехядерник, разрешение от FullHD включительно, более менее нормальная видеокарта. Intel+Nvidia.

Меня и поскромнее характеристики устроили бы, но не хотелось бы менять ноут ближайшие лет пять.

С такими характеристиками подходит что-то типа такого: https://market.yandex.ru/product/1731065630/spec?track=tabs или такого https://market.yandex.ru/product/1731065589/spec?track=tabs или такого https://market.yandex.ru/product/13776071/spec?track=tabs

Дороговато, в связи с чем вопрос: в СПб реально купить технику в честную рассрочку, без навязывания страховок и прочего?

Что лучше для игр: 1060, 1050 или 980M?

Видела на пикабушечке пост о том, как в одной сети по продаже техники на выставочном ноутбуке считали биткоины. Как проверить, что ноут действительно новый? Мне в голову пришло только посмотреть SMART. Можно ли модифицировать время работы в SMART? Насколько показательна эта информация? Можно ли включить ноутбук, запустить считалку биткоинов и отключить жесткие диски, чтобы не тикало время? Каким должно быть время жизни в SMART на новом ноутбуке?

Кто-нибудь встречал ноутбуки с нормальной полноразмерной клавиатурой с не только цифровым блоком, но и блоком home+delete+pageup+pagedown? При кодинге на ноуте мне его очень не хватает. Так как ноутбук я таскать в метро не собираюсь, размер и вес его не важен для меня.

Спасибо за внимание.

 

totik ()

RSS подписка на новые темы