1
votes

I am new to spring jpa/boot. I've created one-to-many relationship between Student and StudentCourse using JPA / Hibernate in a Spring boot application.

The database tables are student and student_course.

When I run the SchoolApplication.java, its failing with the error, 'table or view does not exist'.

It is a valid error as I have not created the table, student_course_list and only want to insert data in student and student_course tables.

Please can you advise why hibernate is looking for this additional table and how to resolve this issue?

Log

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);
}
}

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
    .....
    .....

StudentCourse

@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
    .....
    .....

UPDATE:

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;
2
Your entities were not being scanned by the application. Are you using Spring Data JPA repository?Rae Burawes
@Rae, I am using jpa dependency, -----------<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>--------- shall i use ------------<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.10.2.RELEASE</version> </dependency>------- instead?Muhammad
You haven't told hibernate how to persist the relation so it assumes it needs an additional table. You only have Long studentId in your StudentCourse the same for Long courseId. Those should be a Student and Course object and then you can add mappedBy=student` onto the @OneToMany.M. Deinum
@M.Deinum, sorry, I didn't get it. please can you elaborate in an answer?Muhammad
There is already an answer explaining just that.M. Deinum

2 Answers

0
votes

You didn't define a JoinColumn (or JoinTable for ManyToMany) for your relationship. Hibernate is trying to create a table your student.courseList relationship property.

Change your Student class as follows:

// 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;

Change your StudentCourse class as follows:

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

This will tell Hibernate that the Relationship for Student -> StudentCourse is owned by Student and mapped by the student_id column in the student_course table.

0
votes

You're not telling your application to scan for the entities. On your main class add the @EntityScan annotation. e.g.

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

BTW, @ComponentScan is redundant, @SpringBootApplication is enough. If you're gonna look inside the SpringBootApplication class you'll see @ComponentScan there.