在網上查了很多關於jpa的一對多表關聯的操作,踩了很多坑,今天終於解決了
下面上一下我自己的代碼,記錄一下
老師和學生(一對多關系)
首先搭建環境,添加依賴包
<?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> <groupId>com.lyf</groupId> <artifactId>one-to-more</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.22</version> </dependency> </dependencies> </project>
編寫數據庫配置文件
spring.datasource.url=jdbc:mysql://localhost:3306/jpa?serverTimezone=UTC spring.datasource.username=root spring.datasource.password=08186912 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.database=mysql
實體類
package com.lyf.pojo; import lombok.Data; import javax.persistence.*; /** * @Date:2019-04-12 * @Description:com.lyf.pojo * @version:1.0 */ @Data @Entity @Table(name = "tb_student") public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "s_id") private Long sId; @Column(name = "s_name") private String sName; /** * 多個學生對應一個老師 * 注解形式配置多對一 * 1,配置表關系 * 2,配置外鍵 */ @ManyToOne(targetEntity = Teacher.class) @JoinColumn(name = "s_t_id",referencedColumnName = "t_id") private Teacher teacher; }
package com.lyf.pojo;
import lombok.Data;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
/**
* @Date:2019-04-12
* @Description:教師和學生是一對多
*/
@Data
@Entity
@Table(name = "tb_teacher")
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "t_id")
private Long tId;
@Column(name = "t_name")
private String tName;
//配置老師和學生一對多
/**
* 注解配置多表關系
* 1,聲名關系
* 2,配置外鍵,或者中間表
* OneToMany配置一對多
* targetEntity設置對應的實體類的類型
* JoinColumn 配置外鍵
* name:外鍵的名稱,
* referencedColumnName參照的主表的主鍵字段名稱
*/
@OneToMany(targetEntity = Student.class)
@JoinColumn(name = "s_t_id",referencedColumnName = "t_id")
private Set<Student> students = new HashSet<>();
}
springboot啟動類(引導類)
package com.lyf; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @Date:2019-04-12 * @Description:com.lyf * @version:1.0 */ @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class,args); } }
啟動引導類,查看數據庫會發現表生成成功
dao層代碼就不上了,繼承JpaRepository就行了
接下來我們進行保存操作
package com.lyf; import com.lyf.dao.StudentDao; import com.lyf.dao.TeacherDao; import com.lyf.pojo.Student; import com.lyf.pojo.Teacher; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * @Date:2019-04-12 * @Description:com.lyf * @version:1.0 */ @SpringBootTest @RunWith(SpringRunner.class) public class OneToMoreTest { @Autowired private TeacherDao teacherDao; @Autowired private StudentDao studentDao; @Test public void addTest(){ Student student = new Student(); student.setSName("老籃孩i"); Teacher teacher = new Teacher(); teacher.setTName("劉老師"); //關聯學生和老師,添加學生信息時,還需添加外鍵的值 student.setTeacher(teacher); studentDao.save(student); teacherDao.save(teacher); } }
結果報錯了,發現我是先保存的學生信息,再保存的老師信息,此時數據庫中並沒有老師的信息,給學生關聯老師信息肯定是有問題的
報錯信息
org.springframework.dao.InvalidDataAccessApiUsageException:
org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance - save the transient instance before flushing : com.lyf.pojo.Student.teacher -> com.lyf.pojo.Teacher;
nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance - save the transient instance before flushing :
com.lyf.pojo.Student.teacher -> com.lyf.pojo.Teacher
學生表記錄插入了,老師表是空的
改成
package com.lyf; import com.lyf.dao.StudentDao; import com.lyf.dao.TeacherDao; import com.lyf.pojo.Student; import com.lyf.pojo.Teacher; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * @Date:2019-04-12 * @Description:com.lyf * @version:1.0 */ @SpringBootTest @RunWith(SpringRunner.class) public class OneToMoreTest { @Autowired private TeacherDao teacherDao; @Autowired private StudentDao studentDao; @Test public void addTest(){ Student student = new Student(); student.setSName("老籃孩i"); Teacher teacher = new Teacher(); teacher.setTName("劉老師"); //關聯學生和老師,添加學生信息時,還需添加外鍵的值 student.setTeacher(teacher); //要先保存主表信息 teacherDao.save(teacher); studentDao.save(student); } }
控制台信息,很顯然成功了
Hibernate: alter table tb_student add constraint FKsojy7bicq68v21slcq9mtwtou foreign key (s_t_id) references tb_teacher (t_id) 2019-04-12 23:29:42.036 INFO 10980 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2019-04-12 23:29:42.748 INFO 10980 --- [ main] com.lyf.OneToMoreTest : Started OneToMoreTest in 7.77 seconds (JVM running for 9.806) Hibernate: insert into tb_teacher (t_name) values (?) Hibernate: insert into tb_student (s_name, s_t_id) values (?, ?)
查看數據庫也沒有問題
同樣我們通過Teacher表也能完成關聯操作,保存也是沒有問題的
@Test public void addTest1(){ Student student = new Student(); student.setSName("老籃孩i1"); Teacher teacher = new Teacher(); teacher.setTName("劉老師1"); //通過主表來添加關聯 teacher.getStudents().add(student); studentDao.save(student); teacherDao.save(teacher); }
控制打印信息
Hibernate: insert into tb_student (s_name, s_t_id) values (?, ?) Hibernate: insert into tb_teacher (t_name) values (?) Hibernate: update tb_student set s_t_id=? where s_id=?
學生類和老師類都添加類外鍵的配置,都具備外鍵的維護,那么我們這里可以通過學生找到老師,也能通過老師找到學生,這是一種雙向關系
如果只配置一方,那就是單向的關系,只能通過指定的一方找到另一方