springdatajpa处理表间关系
表间关系
一对一:一对多的特殊情况
一对多:
一的一方:主表,
多的一方:从表,
外键:需要从表上新建一列作为主键,它的取值来源于主表的主键(一的一端)
多对多:
中间表:中间表中最少应该由两个字段组成,这两个字作为外键指向两张表的主键,又组成了联合主键
实体类的关系
包含关系:可以通过包含关系描述表间关系
继承关系
分析步骤
1、明确表关系
2、确定表关系(描述 外键|中间表)
3、编写实体类,在实体类中描述表关系(包含关系)
4、配置映射关系
案例分析
一对多关系和多对一双向关系
分析案例:
1、明确表关系:一对多关系
2、确定表关系(描述 外键中间表) 主表:用户表 从表:订单表 在从表上添加外键
3、编写实体类:在实体类中描述表关系(包含关系);用户:在用户实体类中包含一个订单的集合;订单:在订单实体中包含一个用户的对象
4、配置映射关系:使用jpa注解配置一对多映射关系(难点)
- person.java
//配置用户和订单之间一对多的关系
/*
* 使用注解的形式配置多表关系
* 1、声明关系
* @OneToMany(targetEntity = Orders.class)
* targetEntity:对方对象的字节码
* 2、配置外键(中间表)
* @JoinColumn(name = “person_id”,referencedColumnName = “person_id”)
* name:外键字段名称
* referencedColumnName:参照的主表的主键字段名称
*
* 在用户实体类上(主表一的一方)添加了外键配置,所以对于客户而言,也具备了维护外键的作用
* */
@Entity
@Data
@Table(name = "person")
public class Person {
@Id
@Column(name = "person_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer personId;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "gender")
private Integer gender;
@Basic
@Column(name = "person_addr")
private String personAddr;
@Basic
@Column(name = "birthday")
private Date birthday;
//配置用户和订单之间一对多的关系
/*
* 使用注解的形式配置多表关系
* 1、声明关系
* @OneToMany(targetEntity = Orders.class)
* targetEntity:对方对象的字节码
* 2、配置外键(中间表)
* @JoinColumn(name = "person_id",referencedColumnName = "person_id")
* name:外键字段名称
* referencedColumnName:参照的主表的主键字段名称
*
* 在用户实体类上(主表一的一方)添加了外键配置,所以对于客户而言,也具备了维护外键的作用
* */
@OneToMany(targetEntity = Orders.class)
@JoinColumn(name = "person_id",referencedColumnName = "person_id")
private Set<Orders> orders=new HashSet<>();
}
- orders.java
一定要把从表的主键给删除掉(一点要删除掉,不然会报Repeated column in mapping for entity: com.jpa.model.Orders column: person_id (should be mapped with insert=“false” update=“false”)的错误)
//配置orders和person之间多对一的关系
/*
*使用注解的形式配置多对一
* 1、声明表关系
* @ManyToOne(targetEntity =Person.class)
* targetEntity:对方对象的字节码
* 2、配置外键(多对多使用中间表来配置)
* 配置到了多的一方,就在多的一方维护外键
* */
@Entity
@Data
@Table(name = "orders")
public class Orders {
@Id
@Column(name = "order_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer orderId;
// @Basic
// @Column(name = "person_id")
// private Integer personId;
@Basic
@Column(name = "total_price")
private Double totalPrice;
@Basic
@Column(name = "address")
private String address;
//配置orders和person之间多对一的关系
/*
*使用注解的形式配置多对一
* 1、声明表关系
* @ManyToOne(targetEntity =Person.class)
* targetEntity:对方对象的字节码
* 2、配置外键(多对多使用中间表来配置)
* 配置到了多的一方,就在多的一方维护外键
* */
@ManyToOne(targetEntity =Person.class)
@JoinColumn(name = "person_id",referencedColumnName = "person_id")
private Person person;
}
- 测试类
先保存一的一端在保存多的一端
//保存一个客户一个联系人
/*
* 效果:用户和订单作为独立的数据保存到数据库中
* 订单的外键为空
* 原因是实体类中没有配置关系
* */
@Test
@Transactional
@Rollback(value = false)
public void testAdd(){
//创建一个用户和一个订单
Person person = new Person();
person.setName("张豆豆");
person.setPersonAddr("河南");
person.setBirthday(new Date());
person.setGender(0);
Orders orders = new Orders();
orders.setAddress("天水");
orders.setTotalPrice(548.0);
//将用户表和订单表联系起来( //将球员加到球队)
//从用户的角度看,发送两条insert语句,发送一条更新语句更新数据库(更新外键)
//由于我们配置了用户到订单的关系,用户可以对主键进行维护
person.getOrders().add(orders);
//放弃主键维护的方式进行关联(配置订单到用户的关系[多对一的关系])
orders.setPerson(person);//这种方式级联不起作用
personDao.save(person);
ordersDao.save(orders);
}
测试一对多查询和多对一查询
测试一对多查询和多对一查询必须是单向关联的绝对不能多向关联
- person 对orders 一对多 person表作为主表,orders表作为从表
- Person.java
@Data
@Entity
@Table(name = "person")
public class Person {
@Id
@Column(name = "person_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer personId;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "gender")
private Integer gender;
@Basic
@Column(name = "person_addr")
private String personAddr;
@Basic
@Column(name = "birthday")
private Date birthday;
//配置用户和订单之间一对多的关系
/*
* 使用注解的形式配置多表关系
* 1、声明关系
* @OneToMany(targetEntity = Orders.class)
* targetEntity:对方对象的字节码
* 2、配置外键(中间表)
* @JoinColumn(name = "person_id",referencedColumnName = "person_id")
* name:外键字段名称
* referencedColumnName:参照的主表的主键字段名称
*
* 在用户实体类上(主表一的一方)添加了外键配置,所以对于客户而言,也具备了维护外键的作用
* */
@OneToMany(targetEntity = Orders.class,fetch = FetchType.EAGER)
@JoinColumn(name = "person_id",referencedColumnName = "person_id")
private Set<Orders> orders=new HashSet<>();
}
fetch = FetchType.EAGER一定要加上急加载
- 测试代码
@Test
public void testgetOrder(){
Person person = personDao.findOne(1);
System.out.println(person);
Set<Orders> ordersSet = person.getOrders();
ordersSet.forEach(orders -> System.out.println(orders));
}
- Orders.java
@Data
@Entity
@Table(name = "orders")
public class Orders {
@Id
@Column(name = "order_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer orderId;
// @Basic
// @Column(name = "person_id")
// private Integer personId;
@Basic
@Column(name = "total_price")
private Double totalPrice;
@Basic
@Column(name = "address")
private String address;
//配置orders和person之间多对一的关系
/*
*使用注解的形式配置多对一
* 1、声明表关系
* @ManyToOne(targetEntity =Person.class)
* targetEntity:对方对象的字节码
* 2、配置外键(多对多使用中间表来配置)
* 配置到了多的一方,就在多的一方维护外键
* */
@ManyToOne(targetEntity =Person.class,fetch = FetchType.EAGER)
@JoinColumn(name = "person_id",referencedColumnName = "person_id")
private Person person;
}
- 示例代码
@Test
public void testgetOrder(){
Orders orders = ordersDao.findOne(7);
System.out.println(orders.getPerson());
}
一对多删除的说明和级联的引入
级联是对主表也就是一的一端来说的,所以相关的配置也在一的一端
- 删除说明
级联:操作一个对象的同时操作其他的关联对象;级联有级联添加和级联删除
级联添加:比如添加一个用户的同时添加一个订单
级联删除:比如删除用户的同时,在从表中删除该用户的所有订单
级联操作:
需要区分操作主体
需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
casecade(配置级联)
- 级联添加
- Person.java
@Data
@Entity
@Table(name = "person")
public class Person {
@Id
@Column(name = "person_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer personId;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "gender")
private Integer gender;
@Basic
@Column(name = "person_addr")
private String personAddr;
@Basic
@Column(name = "birthday")
private Date birthday;
//配置用户和订单之间一对多的关系
/*
* 使用注解的形式配置多表关系
* 1、声明关系
* @OneToMany(targetEntity = Orders.class)
* targetEntity:对方对象的字节码
* 2、配置外键(中间表)
* @JoinColumn(name = "person_id",referencedColumnName = "person_id")
* name:外键字段名称
* referencedColumnName:参照的主表的主键字段名称
*
* 在用户实体类上(主表一的一方)添加了外键配置,所以对于客户而言,也具备了维护外键的作用
* */
@OneToMany(targetEntity = Orders.class,fetch = FetchType.EAGER,cascade =CascadeType.ALL)
@JoinColumn(name = "person_id",referencedColumnName = "person_id")
private Set<Orders> orders=new HashSet<>();
}
- Orders.java
@Data
@Entity
@Table(name = "orders")
public class Orders {
@Id
@Column(name = "order_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer orderId;
// @Basic
// @Column(name = "person_id")
// private Integer personId;
@Basic
@Column(name = "total_price")
private Double totalPrice;
@Basic
@Column(name = "address")
private String address;
//配置orders和person之间多对一的关系
/*
*使用注解的形式配置多对一
* 1、声明表关系
* @ManyToOne(targetEntity =Person.class)
* targetEntity:对方对象的字节码
* 2、配置外键(多对多使用中间表来配置)
* 配置到了多的一方,就在多的一方维护外键
* */
@ManyToOne(targetEntity =Person.class,fetch = FetchType.EAGER)
@JoinColumn(name = "person_id",referencedColumnName = "person_id")
private Person person;
}
- 级联添加测试类
/*
* 级联添加:保存一个用户的同时,保存用户的订单
* */
@Test
@Transactional
@Rollback(false)
public void testCasecade(){
Person person = new Person();
person.setName("石破天");
person.setGender(1);
person.setBirthday(new Date());
person.setPersonAddr("侠客岛");
Orders orders = new Orders();
orders.setAddress("中原");
orders.setTotalPrice(56.25);
person.getOrders().add(orders);//必须使用这种主键维护的方式级联添加才起作用
personDao.save(person);
}
- 级联删除测试类
/*
* 级联删除:删除一个用户的同时,删除用户的订单
* */
@Test
@Transactional
@Rollback(false)
public void testCasecadeDelete(){
Person person = personDao.findOne(3);
personDao.delete(person);
}
CasecadeType all :所有
MERGE :更新
PERSIST :保存
REMOVE :删除
多对多关系
- 分析
Person.java
@ManyToMany(targetEntity = Role.class,fetch = FetchType.EAGER)
@JoinTable(name = “person_role”,
joinColumns = {@JoinColumn(name = “person_id”,referencedColumnName = “person_id”)},
inverseJoinColumns = {@JoinColumn(name = “role_id”,referencedColumnName = “role_id”)})
@Data
@Entity
@Table(name = "person")
public class Person {
@Id
@Column(name = "person_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer personId;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "gender")
private Integer gender;
@Basic
@Column(name = "person_addr")
private String personAddr;
@Basic
@Column(name = "birthday")
private Date birthday;
//配置和角色多对多关系
/*
*1、声明表关系的配置
* targetEntity:对方对象的字节码
* 2、配置中间表
* joinColumns:当前对象在中间表中的外键
* inverseJoinColumns:对方对象在中间表中的外键
* */
@ManyToMany(targetEntity = Role.class,fetch = FetchType.EAGER)
@JoinTable(name = "person_role",
joinColumns = {@JoinColumn(name = "person_id",referencedColumnName = "person_id")},
inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")})
private Set<Role> roles=new HashSet<>();
}
Role.java
@Data
@Entity
@Table(name = "role")
public class Role {
@Id
@Column(name = "role_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int roleId;
@Basic
@Column(name = "role_name")
private String roleName;
@Basic
@Column(name = "role_desp")
private String roleDesp;
@ManyToMany(targetEntity = Person.class,fetch = FetchType.EAGER)
@JoinTable(name = "person_role",
joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")},
inverseJoinColumns = {@JoinColumn(name = "person_id",referencedColumnName = "person_id")}
)
private Set<Person> persons=new HashSet<>();
}
测试类
//保存一个用户 一个角色
@Test
@Transactional
@Rollback(false)
public void test(){
Person person = new Person();
person.setName("王尔德");
person.setGender(1);
person.setPersonAddr("北京");
person.setBirthday(new Date());
Role role = new Role();
role.setRoleName("java程序员");
role.setRoleDesp("写java程序的");
//配置用户到角色的关系,可以对中间表中的数据进行维护(只能配置一个,配置用户到角色或者角色到用户)
person.getRoles().add(role);
personDao.save(person);
roleDao.save(role);
}
通过用户查询角色
- 测试类
@Test
public void testSelect(){
Person person = personDao.findOne(1);
System.out.println(person);
}