Jpa配置一对多关系
在网上查了很多关于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=?
学生类和老师类都添加类外键的配置,都具备外键的维护,那么我们这里可以通过学生找到老师,也能通过老师找到学生,这是一种双向关系
如果只配置一方,那就是单向的关系,只能通过指定的一方找到另一方