Spring Boot 2.0.4 + Hibernate 5 - извлечение ленивых коллекций вне области контроллера без вызова геттеров - PullRequest
0 голосов
/ 07 февраля 2019

У меня веб-приложение с весенней загрузкой и гибкой реализацией Spring JPA.У меня отношения один-много между моими сущностями, использующими аннотацию столбца соединения.Чтобы достичь этого, у технологического субъекта есть член List.Я отметил этот список как ленивый инициализированный.Все хорошо, но как только элемент управления выходит за пределы контроллера, hibernate запускает вызовы для отложенного сбора (List), не вызывая его.Это вызывает огромную задержку при загрузке веб-страницы.Не уверен, почему спящий режим запускает ленивые коллекции за пределами области действия контроллера.Я пытался использовать Hibernate.initialize и плагин улучшения байт-кода Maven, но, похоже, ничего не работает.Пожалуйста, помогите

Сущности

@Entity
@Table(name="EmergentTechnologies")
public class EmergentTechnology implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="ID")
    private int id;

    @OneToMany
    @JoinColumn(name="ETID")
    @Basic(fetch = FetchType.LAZY)
    private List<Artifact> artifacts;

@Entity
@Table(name="Artifacts")
public class Artifact implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="ID")
    private int id;

    @Column(name="Analyst")
    private String analyst;

    @Column(name="ArtifactType")
    private String artifactType;

свойства приложения

spring.mvc.favicon.enabled=false
logging.level.com.boeing.etl=INFO
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true
spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cloud-connectors</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>sqljdbc4</artifactId>
            <version>4.0</version>
        </dependency> 
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>    
                            <execution>
                                    <goals>
                                        <goal>build-info</goal>
                                    </goals>
                            </execution>
                        </executions>
            </plugin>
            <plugin>
                <groupId>org.hibernate.orm.tooling</groupId>
                <artifactId>hibernate-enhance-maven-plugin</artifactId>
                <version>${hibernate.version}</version>
                <executions>
                    <execution>
                        <configuration>
                            <failOnError>true</failOnError>
                            <enableLazyInitialization>true</enableLazyInitialization>
                        </configuration>
                        <goals>
                            <goal>enhance</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Контроллер

@RequestMapping(value = "/artifacts", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<List<EmergentTechnology>> getAllArtifactsByEmergentTech(@RequestParam("id") String id) {
        logger.info("Enter getAllArtifactsByEmergentTech");
        int emergentTechId=Integer.parseInt(id);
        List<EmergentTechnology> emergentTechnology = emergingTechLibService.getAllArtifactsByEmergentTech(emergentTechId);
        logger.info("Exit getAllArtifactsByEmergentTech");
        return ResponseEntity.status(HttpStatus.OK).body(emergentTechnology);
    }

Сервис

@Override
    public List<EmergentTechnology> getAllArtifactsByEmergentTech(int emergentTechId) {
        logger.info("Enter getAllArtifactsByEmergentTech");
        List<EmergentTechnology> emergentTechnologies=emergentTechnologyRepository.getAllArtifactsById(emergentTechId);
        //Optional<EmergentTechnology> emergentTechnology=emergentTechnologyRepository.findById(emergentTechId);
        //emergentTechnologies.add(emergentTechnology.get());
        logger.info("After calling findAllArtifactsById()");
        return emergentTechnologies;
    }

Репозиторий

@Override
    public List<EmergentTechnology> getAllArtifactsById(int emergentTechId) {
        logger.info("Enter getEmergentTechnologies");
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<EmergentTechnology> criteriaQuery = criteriaBuilder.createQuery(EmergentTechnology.class);
        Root<EmergentTechnology> root = criteriaQuery.from(EmergentTechnology.class);
        criteriaQuery.select(root);
        criteriaQuery.where(criteriaBuilder.equal(root.<Integer>get("id"), emergentTechId));
        List<EmergentTechnology> emergingTechnologies = entityManager.createQuery(criteriaQuery).getResultList();
        logger.info("Exit getEmergentTechnologies");
        return emergingTechnologies;
    }

Загрузка всех артефактов, когда управление выходит из контроллера (согласно журналам приложений)

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

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

Теперь ответ: Когдаваш контроллер имеет в качестве возврата список, завернутый в ResponseEntity.Аннотация @ResponseBody означает, что возвращаемое значение метода будет составлять тело ответа HTTP.Поскольку в ответе не допускаются никакие объекты Java, они должны быть сериализованы в формат, подходящий для приложений REST, а именно JSON или XML (это будет зависеть от значений атрибута yield аннотации RequestMapping и типа контента, принятого клиентом),Из-за этого процесса сериализации будут вызываться методы получения из объектов EmergentTechnology (управляемых Hibernate, поэтому они проксируются).Этот вызов вызовет выборку списка артефактов.

Итак, вам нужно сопоставить вашу сущность с VO (избегая пары) в сервисе EmergingTechLibService (я рекомендую MapStruct ) избегатьпризвать к получателю артефактов

0 голосов
/ 07 февраля 2019

У вас, похоже, проблема 1 + N.Вы можете заставить Hibernate загружать все данные в одном запросе.Для этого вы можете использовать fetch join с вашими критериями.

@Override
public List<EmergentTechnology> getAllArtifactsById(int emergentTechId) {
    logger.info("Enter getEmergentTechnologies");
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<EmergentTechnology> criteriaQuery = criteriaBuilder.createQuery(EmergentTechnology.class);
    Root<EmergentTechnology> root = criteriaQuery.from(EmergentTechnology.class);

    // NEW CODE 
    root.fetch("artifacts", JoinType.INNER);

    criteriaQuery.select(root);
    criteriaQuery.where(criteriaBuilder.equal(root.<Integer>get("id"), emergentTechId));
    List<EmergentTechnology> emergingTechnologies = entityManager.createQuery(criteriaQuery).getResultList();
    logger.info("Exit getEmergentTechnologies");
    return emergingTechnologies;
}

Другой вариант, чтобы hibernate получал все данные одним запросом, - это использование именованного графа сущностей .

Хотя я не уверен, что это решит все ваши проблемы, вы все равно должны увидеть улучшение производительности.

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

Еще одним вариантом является использование кэша запросов гибернации .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...