Jpa对象关联映射关系三种关系
数据库表和表之间的关系、表和表之间都是通过外键维护关系的;
对象和对象之间的关系:
1. 一对多:部门员工 班级学生
2. 多对一:员工部门
3. 多对多:项目员工(拆解成一对多 双向)
4. 一对一:一夫一妻 一个公司 一个注册地址
一对多关系映射–OneToMany
项目结构:
application.properties
# 连接数据库的四大参数
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/jpa
spring.datasource.username=postgres
spring.datasource.password=123456
# jpa相关配置
# 开发阶段可以 设置为true,开启逆向工程,在实际上线运行阶段,实体类和底层的数据库表都是已经存在,应该设置成false
# 数据库和java映射 正向工程 逆向工程
# 逆向工程: 存在数据库的表,然后数据库表可以生成对应实体类
# 正向工程: 先是存在实体类,然后根据实体类,生成底层的表
spring.jpa.generate-ddl=true
# create: 设置为create,每次运行程序都会将原来的数据表删除,然后重新创建一个表
# create-drop: 每次床架你个数据表,数据表使用完毕之后,将数据表再次删除
# none: 将功能不生效
# update: 如果你设定的实体类发生了改变,数据表会更新
# 如果数据库当中有数据表,就使用原来的表,没有数据表,就会创建一个数据表。也是在开发当中使用
# validate: 实体类和数据表进行校验,如果属性或个数不一致,就会出异常
spring.jpa.hibernate.ddl-auto=update
# 操作实体对象的时候,会为我们生成sql语句: false 不生成sql语句
spring.jpa.show-sql=true
# 指定数据库的类型
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL10Dialect
# 上线运行时只有spring.jpa.database-platform是真实有效的
dao
1.ClazzDao —班级
public interface ClazzDao extends JpaRepository<Clazz,Integer> {
}
2.StudentDao —学生
public interface StudentDao extends JpaRepository<Student,Integer> {
}
entity
1.班级信息
@Getter
@Setter
@Entity(name = "t_clazz")
public class Clazz {//new Clazz
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int cid;
@Column
private String cname;
//表示一对多 表示一个班级有多个学生对象
@OneToMany(mappedBy = "clz",fetch = FetchType.EAGER,cascade = CascadeType.ALL)//mapperBy 创建一和多的映射关系,值是对方的属性名称
private List<Student> list;
}
2.学生信息
@Getter
@Setter
@ToString
@Entity(name = "t_student")//外键表,子表
public class Student {//new Student
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer sid;
@Column
private String sname;
//表示多对一 多个学生对象属于同一个班级
@ManyToOne
@JoinColumn(name="cid")//指定了外键列:外键列没有指定name属性,默认名称clz
private Clazz clz;
}
测试一对多关系的增删改查接口
@SpringBootTest(classes = JpaOnetomanyApplication.class)
class JpaOnetomanyApplicationTests {
@Autowired
ClazzDao clazzDao;
@Autowired
StudentDao studentDao;
@Test
void contextLoads() {
System.out.println("初始化数据库表");
}
/**
* 添加操作
* 添加班级信息
* 添加学生信息
*/
@Test
void testAdd(){
Clazz clazz = new Clazz();
clazz.setCname("2班");
clazzDao.save(clazz);
}
@Test
void testAddStu(){
//添加学生信息,没有设置学生所属的班级信息
// Student student = new Student();
// student.setSname("zhangsan");//student 这个对象没有设置外键时,外键允许为null
// studentDao.save(student);
//添加学生信息,设置学生所属的班级信息
Student student1 = new Student();
student1.setSname("lisi");
//创建一个Clazz对象,设置该对象的cid值
Clazz clazz = new Clazz();
clazz.setCid(8);//setCid只能来源于t_clazz表中的数据,否则会报错
student1.setClz(clazz);
studentDao.save(student1);
}
/**
* 更新班级信息
*/
@Test
public void testUpdateClz(){
//查询出来修改的班级信息
Clazz clz = new Clazz();
clz.setCid(15);
clz.setCname("元宵节1");
clazzDao.save(clz);
}
/**
* 更新学生信息
*/
@Test
public void testUpdateStu(){
//更新学生信息
Student student2 = new Student();
student2.setSid(12);
//重新分配名称
student2.setSname("lisisi");
//准备班级信息
Clazz clz = new Clazz();
clz.setCid(8);
//给student重新分配班级信息
student2.setClz(clz);
studentDao.save(student2);
}
/**
* 查询信息
* 查询班级信息
* 查询学生信息
*/
//查询班级信息 关联查询了学生信息:onetomany 是懒加载查询
@Test
public void testFindCla(){
//查询所有的班级信息
List<Clazz> clazzes = clazzDao.findAll();
for(Clazz clazz:clazzes){
/**
* select * from t_clazz clazz0_
* 查询班级信息,并没有查询班级中学生的信息
*
* 懒加载:查询班级信息并没有发送select语句查询班级当中的学生信息,这样的查询就是懒加载查询
* 只有当使用到学生信息的时候,才会发送select查询学生信息
* 立即加载:查询班级信息立即发送select语句查询学生信息
* 总结:
* 一般情况下查询一的一方,不需要立即加载。性能降低,建议使用默认值
*
*
*/
// System.out.println(clazz.getCid()+", "+clazz.getCname());
//查询班级中的学生信息
/**
* 查询班级中的学生信息 no Session异常信息
* 原因:
* (1)查询班级信息,当班级信息查询完毕,dao层结束后,session就会关闭
* (2)再次调用dao层的方法查询student信息,此时就会没有session对象,所以出现一个no Session异常
* 解决no Session问题:
* (1)设置立即加载 @OneToMany(mappedBy = "clz",fetch = FetchType.EAGER)//mapperBy 创建一和多的映射关系,值是对方的属性名称
* (2)延长session的生命周期
*/
List<Student> students = clazz.getList();
for(Student student:students){
System.out.println(student);
}
System.out.println(clazz.getCid()+", "+clazz.getCname());
}
}
/**
* mamyToOne 立即加载
* 总结: 查询多的一方,立即加载一的一方,立即加载
*/
@Test
public void testFindStudents(){
//查询学生信息
List<Student> students = studentDao.findAll();
for(Student student:students){
//没有使用学生所属的班级信息,但是班级信息也查询了:立即加载
System.out.println(student.getSid()+", "+student.getSname()+", "+student.getClz().getCid());
}
}
/**
* 删除操作:
* 删除班级信息:
* 主键表主键字段可能引用
* 也可能没有引用
* 删除学生信息:
*
*/
@Test
public void deleteClz(){
//删除班级信息,班级当中没有学生(主键表中主键没有被引用)
// Clazz clazz = new Clazz();
// clazz.setCid(15);
// clazzDao.delete(clazz);
clazzDao.deleteById(15);
}
@Test
public void deleteClz2(){
//删除班级信息,班级当中有学生(主键表中主键被引用)
/**
* 直接删除班级表删除失败
* 原因:主键表当中的数据已经被引用了
* 如何删除?
* (1)级联删除:删除班级信息的时候,级联操作学生信息
* 设置:
* cascade = CascadeType.ALL
* (2)先断开主外键连接:再去删除主键表当中的数据
* step1:断开主外键连接 update
* step2:执行一个删除操作
* 遇到的问题:no Session
* 解决:添加一个拦截器,延长session的生命周期
*
*/
clazzDao.deleteById(13);
}
/**
* 删除外键表(子表)的数据,直接删除就ok
*/
@Test
public void deleteStudent(){
//删除学生信息,不用考虑cid的值,直接删除就ok
// Student student = new Student();
// student.setSid(10);
// studentDao.delete(student);
//删除没有成功,受到立即加载的影响
studentDao.deleteById(12);
}
/**
* 添加班级信息同时添加学生信息
*/
@Test
public void addClZStudent(){
//创建班级信息
Clazz clazz = new Clazz();
clazz.setCname("java高级就业班");
//创建学生信息
Student student = new Student();
student.setSname("zhangsan");
Student student1 = new Student();
student1.setSname("lisi");
//将学生信息添加到集合
List<Student> students = new ArrayList<>();
students.add(student1);
students.add(student);
//创建班级学生的关联关系
clazz.setList(students);
//创建学生和班级的关联关系 student维护外键关系,多的一方维护外键关系
student.setClz(clazz);
student1.setClz(clazz);
//保存班级信息 没有创建双向关联 数据成功保存,但是外键没有维护,外键的值都是空值
clazzDao.save(clazz);
}
}