当前位置: 首页 > 知识库问答 >
问题:

为什么需要PostPersist来设置一个子实体的id?

戴建义
2023-03-14

我试图实现双向一对一映射,并从父实体(Project.java)插入子实体(ProjectDetails.java)。但是,实体管理器正在尝试插入null作为子实体(ProjectDetails)的id。

错误日志:

[EL Fine]: sql: 2019-08-19 01:16:50.969--ClientSession(1320691525)--Connection(926343068)--INSERT INTO project (name) VALUES (?)
    bind => [Project]
[EL Fine]: sql: 2019-08-19 01:16:50.973--ClientSession(1320691525)--Connection(926343068)--SELECT @@IDENTITY
[EL Fine]: sql: 2019-08-19 01:16:50.983--ClientSession(1320691525)--Connection(926343068)--INSERT INTO project_details (project_id, details) VALUES (?, ?)
    bind => [null, Details]
[EL Fine]: sql: 2019-08-19 01:16:50.986--ClientSession(1320691525)--SELECT 1
[EL Warning]: 2019-08-19 01:16:50.991--UnitOfWork(1746098804)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Column 'project_id' cannot be null
Error Code: 1048

我已经尝试从@OneToOne中删除insertable=false和updatable=false,但是它给我一个错误,同一个列不能被引用两次。

我有以下实体类。

类别:项目

package com.example.playground.domain.dbo;

import com.example.playground.jsonviews.BasicView;
import com.example.playground.jsonviews.ProjectView;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.Data;
import lombok.ToString;
import org.eclipse.persistence.annotations.JoinFetch;
import org.eclipse.persistence.annotations.JoinFetchType;

import javax.persistence.*;

@JsonView(BasicView.class)
@Data
@Entity
@Table(name = "project")
public class Project {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="project_id")
    private Integer projectId;

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

    @ToString.Exclude
    @JsonView(ProjectView.class)
    @OneToOne(mappedBy = "project", cascade = CascadeType.ALL, optional = false)
    private ProjectDetails projectDetails;

}

类:ProjectDetails

package com.example.playground.domain.dbo;

import com.example.playground.jsonviews.BasicView;
import com.example.playground.jsonviews.ProjectDetailsView;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.Data;
import lombok.ToString;

import javax.persistence.*;

@JsonView(BasicView.class)
@Data
@Entity
@Table(name = "project_details")
public class ProjectDetails {

    @Id
    @Column(name = "project_id")
    private Integer projectId;

    @ToString.Exclude
    @JsonView(ProjectDetailsView.class)
    @OneToOne
    @JoinColumn(name = "project_id", nullable = false, insertable = false, updatable = false)
    private Project project;

    @Column(name = "details")
    private String details;


}

类别:项目控制器

package com.example.playground.web;

import com.example.playground.domain.dbo.Project;
import com.example.playground.jsonviews.ProjectView;
import com.example.playground.service.ProjectService;
import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/projects")
public class ProjectController {

    @Autowired
    private ProjectService projectService;

    @GetMapping("/{projectId}")
    @JsonView(ProjectView.class)
    public ResponseEntity<Project> getProject(@PathVariable Integer projectId){
        Project project = projectService.getProject(projectId);
        return ResponseEntity.ok(project);
    }

    @PostMapping
    @JsonView(ProjectView.class)
    public ResponseEntity<Project> createProject(@RequestBody Project projectDTO){
        Project project =  projectService.createProject(projectDTO);
        return ResponseEntity.ok(project);
    }


}

类项目服务

package com.example.playground.service;

import com.example.playground.domain.dbo.Project;

public interface ProjectService {
    Project createProject(Project projectDTO);

    Project getProject(Integer projectId);
}

类ProjectServiceImpl

package com.example.playground.impl.service;

import com.example.playground.domain.dbo.Project;
import com.example.playground.repository.ProjectRepository;
import com.example.playground.service.ProjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ProjectServiceImpl implements ProjectService {

    @Autowired
    private ProjectRepository projectRepository;

    @Transactional
    @Override
    public Project createProject(Project projectDTO) {
        projectDTO.getProjectDetails().setProject(projectDTO);
        return projectRepository.saveAndFlush(projectDTO);
    }

    @Override
    public Project getProject(Integer projectId) {
        return projectRepository.findById(projectId).get();
    }
}

JPAConfig

package com.example.playground.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement(mode= AdviceMode.ASPECTJ)
public class JPAConfig{

    @Bean("dataSource")
    @ConfigurationProperties(prefix = "db1")
    public DataSource getDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean("entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean getEntityManager(@Qualifier("dataSource") DataSource dataSource){
        EclipseLinkJpaVendorAdapter adapter = new EclipseLinkJpaVendorAdapter();
        LocalContainerEntityManagerFactoryBean em =  new LocalContainerEntityManagerFactoryBean();
        em.setPackagesToScan("com.example.playground.domain.dbo");
        em.setDataSource(dataSource);
        em.setJpaVendorAdapter(adapter);
        em.setPersistenceUnitName("persistenceUnit");
        em.setJpaPropertyMap(getVendorProperties());
        return em;
    }

    @Bean(name = "transactionManager")
    public JpaTransactionManager
    transactionManager(@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory)
    {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

    protected Map<String, Object> getVendorProperties()
    {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("eclipselink.ddl-generation", "none");
        map.put("eclipselink.ddl-generation.output-mode", "database");
        map.put("eclipselink.weaving", "static");
        map.put("eclipselink.logging.level.sql", "FINE");
        map.put("eclipselink.logging.parameters", "true");
        map.put(
                "eclipselink.target-database",
                "org.eclipse.persistence.platform.database.SQLServerPlatform");
        return map;
    }

}

还有pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>playground</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Java-Spring-Boot-Playground</name>
    <description>Java playground.</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.1.9.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa</artifactId>
            <version>2.7.4</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jdbc -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
            <version>9.0.21</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

编辑:工作正常,但仍在寻找解释

如果我将下面的代码添加到我的项目类中,它会像预期的那样工作。

  @PostPersist
    public void fillIds(){
        projectDetails.setProjectId(this.projectId);
    }

购买为什么需要postPerst?如果关系标记为oneToOne,JPA不是应该自动填充这些值吗?有更好的方法吗?

共有1个答案

阳勇
2023-03-14

JPA遵循了您的指示:您有两个到“project_id”的映射,一个有值,一个是只读的。这意味着JPA必须从基本ID映射“projectId”中提取值,您将其保留为null,从而将null插入字段。

这是一个常见的问题,JPA中有许多解决方案。首先是将@ID映射标记为只读(可插入/可更新=false),并让关系映射控制值。

不过,JPA 2.0引入了其他解决方案。对于同样的设置,您可以使用@MapsId注释标记关系。这告诉JPA关系外键值将用于指定的ID映射,并且会按照您的期望为您设置它,而无需postPereld方法

JPA 2.0中的另一种选择是您可以将OneToOne标记为ID映射,并从类中删除Project ectId属性。这里显示了一个更复杂的示例

 类似资料:
  • 问题内容: 这对您来说可能听起来很愚蠢,但是为什么我需要在s中定义一个空的构造函数? 我看到的每个教程都说:每个实体都需要一个空的构造函数。 但是Java总是给您一个默认的不可见的空构造函数(如果您不重新定义一个的话)。 谢谢。 我认为这是一个语义问题。我所理解的“需求”是书面的。 含义:始终在您的实体中编写一个空的构造函数。 例: 但是,当您不重新定义Java时,Java总是会为您提供一个空的构

  • 考虑类<代码> OuterClass < />代码> <代码> InnerClass < /代码> 第二个类,它试图扩展

  • 我将把我的Android项目从Ant转换为Gradle。 我的Eclipse工作区非常简单: 当我添加构建时。MyApp中的gradle文件,我想引用我的Android库项目: 当我运行gradle build时,出现了一个错误“Project with path”:在根项目中找不到MyApp AndroidLibrary“,我在Google上搜索了一下,发现我需要在我的工作区目录中设置一个“se

  • 问题内容: 我一直在研究,发现从2.1开始就可以使用实体图。 但是我还不了解实体图的优点。 我知道使用实体图的优点之一是我们只能在整个实体中指定要获取的数据,但是如果我们要整个实体,还有其他理由使用实体图吗?还是仅在要检索部分数据时才应使用实体图? 当我们使用实体图时,它还有其他目的或优点,我想知道。 问题答案: 在Jpa中,休眠与关联关系一直是性能的问题。 一次又一次地在事务中延迟加载关联会导致

  • 问题内容: 总是有很多与独立实体有关的问题! 首先,它们经常导致hibernate。是的,还有另一个持久性提供程序,它们不抛出异常,但是我认为它们在一致性方面存在一些问题。考虑我们有和实体,从到那里的引用()必须为非null。 我们开始了会话,加载了实例,然后关闭了会话。之后,我们尝试获取对的引用。并假设另一笔交易只是删除了我们和实例。因此,当我们从数据库查询时,我们找不到合适的实例并获取! 因此

  • 以我的拙见,关于“什么是单子”这个著名问题的答案,尤其是投票最多的答案,试图解释什么是单子,而没有明确解释为什么单子是真正必要的。它们能被解释为一个问题的解决方案吗?