有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JavaSpringBoot2.0.4+Hibernate5在不调用getter的情况下获取控制器范围外的惰性集合

我有一个SpringBootWeb应用程序,它使用SpringJPA的hibernate实现。我使用连接列注释处理实体之间的一对多关系。为了实现这一点,技术实体有一个成员列表。我已将此列表标记为已初始化。一切都很好,但一旦控件移到控制器外部,hibernate就会触发对惰性集合(列表)的调用,而不调用它。这会导致加载网页的巨大延迟。不确定为什么hibernate在控制器范围外触发惰性集合。我试过使用Hibernate。初始化和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

波姆。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) 个答案

  1. # 1 楼答案

    你似乎有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通过一个查询获取所有数据的选项是使用named entity graph

    虽然我不确定这会解决你所有的问题,但你仍然应该看到性能的提高

    此外,根据数据的性质(在我看来,这是一组不会太大、也不会经常改变的信息),您可以充分利用spring caching,它非常适合您的用例

    还有一个选项是使用hibernate query cache

  2. # 2 楼答案

    回答之前有一件事:不要将实体作为rest服务的响应返回,因为您正在将服务的使用者耦合到数据库,因此这是一种不好的做法

    现在答案是:当你的控制器返回一个包含在ResponseEntity中的列表时@ResponseBody注释意味着方法的返回值将构成HTTP响应的主体。由于响应中不允许使用Java对象,因此必须将它们序列化为适合REST应用程序的格式,即JSON或XML(这将取决于RequestMapping注释的products属性的值,以及客户端接受的内容类型)。由于这个系列化过程,将调用EmergentTechnology对象(由Hibernate管理,因此它们是代理的)中的getter方法。此调用将导致获取工件列表

    因此,您需要做的是将实体映射到服务EmergingTechLibService(我建议MapStruct)中的VO(避免耦合)中,避免调用工件的获取者