JPA/Hibernate 在 Spring Boot 应用程序中插入不存在的表

Posted

技术标签:

【中文标题】JPA/Hibernate 在 Spring Boot 应用程序中插入不存在的表【英文标题】:JPA/Hibernate inserting into non existing table in spring boot application 【发布时间】:2016-07-14 13:31:14 【问题描述】:

我是 spring jpa/boot 的新手。我在 Spring Boot 应用程序中使用 JPA / Hibernate 在 StudentStudentCourse 之间创建了一对多关系。

数据库表是student和student_course。

当我运行SchoolApplication.java 时,它失败并出现错误“表或视图不存在”。

这是一个有效错误,因为我还没有创建表 student_course_list 并且只想在 studentstudent_course 表中插入数据。

请您告知为什么 hibernate 正在寻找这个额外的表以及如何解决这个问题?

日志

Hibernate: 
insert 
into
    student_course_list
    (student, course_list) 
values
    (?, ?)

Caused by: org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:242) ~[spring-orm-4.2.6.RELEASE.jar:4.2.6.RELEASE]

SchoolApplication.java

package com.school;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.Transactional;

import com.school.domain.Student;
import com.school.domain.StudentCourse;
import com.school.service.StudentServiceInterface;

@EntityScan(basePackages="com.school.domain")
@SpringBootApplication
public class SchoolApplication implements CommandLineRunner 

    @Autowired
    protected StudentServiceInterface studentService;

    public static void main(String[] args) 
        SpringApplication.run(SchoolApplication.class, args);
    

    @Override
    @Transactional
    public void run(String... strings) throws Exception 

        StudentCourse sc1 = new StudentCourse();
        sc1.setCourseId(new Long(1));
        sc1.setEndDate(null);

        StudentCourse sc2 = new StudentCourse();
        sc2.setCourseId(new Long(2));
        sc2.setEndDate(null);

        //Course List
        List<StudentCourse> studentCourse = new ArrayList();

        studentCourse.add(sc1);
        studentCourse.add(sc2);

        Student student = new Student();
        student.setFirstName("test first");
        student.setLastName("test last");
        student.setCourseList(studentCourse);

        studentService.saveStudent(student);


学生

@Entity
@Table(name="student")
public class Student 

    @Id
    @Column(name="student_id")
    @GeneratedValue(strategy=GenerationType.AUTO, generator="student_seq_gen")
    @SequenceGenerator(name="student_seq_gen", sequenceName="SEQ_STUDENT")
    private Long studentId;

    @OneToMany( targetEntity=StudentCourse.class, cascade = CascadeType.ALL )
    private List<StudentCourse> courseList;

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

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

    @Column(name="gender")
    private char gender;

    @Column(name="date_of_birth")
    private Date dateOfBirth;

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

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

    // getter and setters
    .....
    .....

学生课程

@Entity
@Table(name="student_course")
public class StudentCourse 

    @Id
    @Column(name="student_course_id")
    @GeneratedValue(strategy=GenerationType.AUTO, generator="student_course_seq_gen")
    @SequenceGenerator(name="student_course_seq_gen", sequenceName="SEQ_STUDENT_COURSE")
    private Long studentCourseId;

    @Column(name="student_id")
    private Long studentId;

    @Column(name="course_id")
    private Long courseId;

    @Column(name="end_date")
    private Date endDate;

    // getter and setters
    .....
    .....

更新:

StudentCourse.java:

@Entity
@Table(name="student_course")
public class StudentCourse 

    @Id
    @Column(name="student_course_id")
    @GeneratedValue(strategy=GenerationType.AUTO, generator="student_course_seq_gen")
    @SequenceGenerator(name="student_course_seq_gen", sequenceName="SEQ_STUDENT_COURSE")
    private Long studentCourseId;

    @JoinColumn(name="student_id", nullable = false, updatable = false, insertable = true)
    @ManyToOne
    private Student student;

    @Column(name="course_id")
    private Long courseId;

    @Column(name="end_date")
    private Date endDate;

【问题讨论】:

应用程序未扫描您的实体。你在使用 Spring Data JPA 存储库吗? @Rae,我正在使用 jpa 依赖,-----------org.springframework.bootspring-boot- starter-data-jpa--------- 我应该使用 ------------org.springframework.data groupId> spring-data-jpa1.10.2.RELEASE------- 代替? 你还没有告诉hibernate如何持久化这个关系,所以它假设它需要一个额外的表。您的StudentCourse 中只有Long studentId,与Long courseId 相同。这些应该是StudentCourse 对象,然后您可以将mappedBy=student` 添加到@OneToMany @M.Deinum,抱歉,我没听懂。请您详细回答一下吗? 已经有一个答案解释了这一点。 【参考方案1】:

您没有为您的关系定义JoinColumn(或JoinTable 对应ManyToMany)。 Hibernate 正在尝试为您的 student.courseList 关系属性创建一个表。

如下更改您的学生班级:

// You shouldn't need targetEntity, as your collection has the TargetEntity.
// this parameter is only needed if the Generic type is not present 
// (e.g. private List courseList)
@OneToMany( cascade = CascadeType.ALL, mappedBy = "student" )
private List<StudentCourse> courseList;

如下更改您的 StudentCourse 课程:

// Replace studentId with a Relationship
@JoinColumn(name="student_id")
@ManyToOne
private Student student;

这将告诉 Hibernate Student -> StudentCourse 的关系由 Student 拥有,并由 student_course 表中的 student_id 列映射。

【讨论】:

它的工作但没有填充 student_course 表中的 student_id。 您是否在StudentCourse? 中设置了Student 能否提供您正在做和看到的示例? 是的,我已经进行了建议的更改。此外,添加了一些额外的属性(nullable = false、updatable = false、insertable = true)请参阅更新的 StudentCourse.java。它将数据插入到 student 和 student_course 表中,但 student_id(外键)没有被插入。 保存学生前需要在StudentCourse中设置学生。喜欢sc1.setStudent(student)。您可能还应该使 StudentCourse.course 也成为关系。使用相同的模式。 另一个注意,您设置的可为空等属性在运行时不会被验证。它们用于休眠 ddl 生成。如果要在运行时进行验证,需要添加java.persistence.validation注解,如@NotNull【参考方案2】:

您并没有告诉您的应用程序扫描实体。在您的主类上添加 @EntityScan 注释。例如

@SpringBootApplication
@EntityScan(basePackages="com.example.model", "com.example.student.model")
public class SchoolApplication implements CommandLineRunner 
    ...

顺便说一句,@ComponentScan 是多余的,@SpringBootApplication 就足够了。如果您要查看 SpringBootApplication 类的内部,您会在那里看到 @ComponentScan

【讨论】:

为什么需要添加 EntityScan?因为他的 run 方法中有一个事务?或者是什么原因? 我已经添加了@EntityScan 但仍然遇到同样的错误 这不是必需的,只要 @SpringBootApplication 在您的实体的父包中并且您的依赖项列表中有 Spring JPA 库。

以上是关于JPA/Hibernate 在 Spring Boot 应用程序中插入不存在的表的主要内容,如果未能解决你的问题,请参考以下文章

JPA---Spring-data-JPA---Hibernate

Spring Boot / JPA / Hibernate,如何根据 Spring 配置文件切换数据库供应商?

spring-data-jpa+hibernate 各种缓存的配置演示

Spring Security JDBC 和 Hibernate JPA

使用 Spring JPA / Hibernate 进行条件插入

spring.jpa.hibernate.ddl-auto 属性在 Spring 中是如何工作的?