一对多
客户 与 联系人 一对多的关系
@Entity
@Table(name="cst_customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="cust_id")
private Long custId;
@Column(name="cust_address")
private String custAddress;
@Column(name="cust_industry")
private String custIndustry;
@Column(name="cust_level")
private String custLevel;
@Column(name="cust_name")
private String custName;
@Column(name="cust_phone")
private String custPhone;
@Column(name="cust_source")
private String custSource;
//@OneToMany(targetEntity = LinkMan.class) 对方对象的字节码对象
//@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
配置客户和联系人之间的关系(一对多关系)
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
}
@Entity
@Table(name = "cst_linkman")
public class LinkMan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "lkm_id")
private Long lkmId; //联系人编号(主键)
@Column(name = "lkm_name")
private String lkmName;//联系人姓名
@Column(name = "lkm_gender")
private String lkmGender;//联系人性别
@Column(name = "lkm_phone")
private String lkmPhone;//联系人办公电话
@Column(name = "lkm_mobile")
private String lkmMobile;//联系人手机
@Column(name = "lkm_email")
private String lkmEmail;//联系人邮箱
@Column(name = "lkm_position")
private String lkmPosition;//联系人职位
@Column(name = "lkm_memo")
private String lkmMemo;//联系人备注
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
}
1.配置外键 获得外键维护权
@OneToMany(targetEntity = LinkMan.class) 对方对象的字节码对象
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
name:外键字段名称 referencedColumnName: 参照的主表(一的一方)的主键字段名称
如果在一的一方配置了外键 ,在保存操作时, 除了 两个 insert 语句, 还会多一条update语句 来维护外键关系
2.放弃外键维护权
为了防止出现这种重复的 外键维护 一般 一的一方放弃外键维护 多的一方配置外键,且就是两条Insert语句
mappedBy:对方配置关系的属性名称,就是多的一方的实体类中的 关联对象的 属性名
3.配置级联 cascade : (可以配置到设置多表的映射关系的注解上)
CascadeType.all: 所有 MERGE :更新 PERSIST :保存 REMOVE:删除
配置了这个注解后 ,当有 更新 ,保存,删除操作时,会级联操作 关系到的表
无级联
1.主表到从表保存
当双方都配置了外键对应关系(都有维护权) 并且没有设置级联时,需要使用两个save方法,
添加 主表中从表的信息 customer.getLinkMans().add(linkMan); 这时多出一条update语句 更新外键的关系
@Test
@Transactional //配置事务
@Rollback(false) //不自动回滚
public void testAdd() {
//创建一个客户,创建一个联系人
Customer customer = new Customer();
customer.setCustName("百度");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李");
customer.getLinkMans().add(linkMan);
customerDao.save(customer);
linkManDao.save(linkMan);
}
2.从表到主表保存
正常的两条Insert 语句 从表中使用setCustomer 设置外键关系
@Test
@Transactional //配置事务
@Rollback(false) //不自动回滚
public void testAdd1() {
//创建一个客户,创建一个联系人
Customer customer = new Customer();
customer.setCustName("百度");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李");
linkMan.setCustomer(customer);
customerDao.save(customer);
linkManDao.save(linkMan);
}
3.放弃外键维护权
由于配置了多的一方到一的一方的关联关系(当保存的时候,就已经对外键赋值)
由于配置了一的一方到多的一方的关联关系(发送一条update语句,更新外键) 所以操作就重复了 ,所以在主表中mappedBy
@Test
@Transactional //配置事务
@Rollback(false) //不自动回滚
public void testAdd2() {
//创建一个客户,创建一个联系人
Customer customer = new Customer();
customer.setCustName("百度");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李");
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);
customerDao.save(customer);
linkManDao.save(linkMan);
}
添加级联
只能在主表中添加级联 @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
就只需要保存 客户 就会自动根据根据客户中 联系人的列表 去级联保存 联系人
@Test
@Transactional //配置事务
@Rollback(false) //不自动回滚
public void testCascadeAdd() {
Customer customer = new Customer();
customer.setCustName("百度1");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李1");
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);
customerDao.save(customer);
}
级联删除 删除客户会自动删除更客户关联的 联系人
@Test
@Transactional //配置事务
@Rollback(false) //不自动回滚
public void testCascadeRemove() {
//1.查询1号客户
Customer customer = customerDao.findOne(1l);
//2.删除1号客户
customerDao.delete(customer);
}
多对多
@Entity
@Table(name = "sys_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="user_id")
private Long userId;
@Column(name="user_name")
private String userName;
@Column(name="age")
private Integer age;
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
@JoinTable(name = "sys_user_role",
//joinColumns,当前对象在中间表中的外键
joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
//inverseJoinColumns,对方对象在中间表的外键
inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}
)
private Set<Role> roles = new HashSet<Role>();
}
@Entity
@Table(name = "sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
//配置多对多
@ManyToMany(mappedBy = "roles") //配置多表关系
private Set<User> users = new HashSet<User>();
}
1.声明表关系的配置
@ManyToMany(targetEntity = Role.class) targetEntity:代表对方的实体类字节码
一般被选择的的表放弃维护权 mappedBy 配置自己在关联实体类中的属性名
2.配置中间表(包含两个外键)
@JoinTable name : 中间表的名称 Spring Data Jpa 会根据这个值 自动创建 一个中间表
1.joinColumns:配置当前对象在中间表的外键
@JoinColumn的数组 name:外键名 referencedColumnName:参照的主表的主键名
2.inverseJoinColumns:配置对方对象在中间表的外键
无级联时 保存用户 角色信息 创建三个表,三个Insert语句
@Test
@Transactional
@Rollback(false)
public void testAdd() {
User user = new User();
user.setUserName("小李");
Role role = new Role();
role.setRoleName("java程序员");
//配置用户到角色关系,可以对中间表中的数据进行维护 1-1
user.getRoles().add(role);
//配置角色到用户的关系,可以对中间表的数据进行维护 1-1
role.getUsers().add(user);
userDao.save(user);
roleDao.save(role);
}
添加级联时 @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
根据保存的用户的关联的角色 自动在中间表 和 角色表 添加信息, 创建三个表,三个Insert语句
//测试级联添加(保存一个用户的同时保存用户的关联角色)
@Test
@Transactional
@Rollback(false)
public void testCasCadeAdd() {
User user = new User();
user.setUserName("小李");
Role role = new Role();
role.setRoleName("java程序员");
//配置用户到角色关系,可以对中间表中的数据进行维护 1-1
user.getRoles().add(role);
//配置角色到用户的关系,可以对中间表的数据进行维护 1-1
role.getUsers().add(user);
userDao.save(user);
}
级联删除 自动删除中间表 和 角色表中的信息 三条delete语句
删除id为1的用户,同时删除他的关联对象
@Test
@Transactional
@Rollback(false)
public void testCasCadeRemove() {
//查询1号用户
User user = userDao.findOne(1l);
//删除1号用户
userDao.delete(user);
}
对象导航查询
以上面的 一对多为列
对象导航查询就是 通过查询出来的 一个客户(一的一方), 通过此对象 查询出 关联的 联系人
一往多查询 默认是延迟加载 多往一查询默认是立即加载可以通过 加载方式fetch 注解配置加载方式 ,一般使用默认就行
EAGER :立即加载 一般在多的一方配置
LAZY :延迟加载 一般在一的一方加,因为查询 一的一方时 不会立即查询出 一对多的所有数据 ,提升性能
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch =FetchType.EAGER)
@Test
@Transactional // 解决在java代码中的no session问题
public void testQuery1() {
//查询id为1的客户
Customer customer = customerDao.getOne(1l);
//对象导航查询,此客户下的所有联系人
Set<LinkMan> linkMans = customer.getLinkMans(); 不会查询
for (LinkMan linkMan : linkMans) {
System.out.println(linkMan); 在这里才会查询
}
}
多往一查询
@Test
@Transactional // 解决在java代码中的no session问题
public void testQuery3() {
LinkMan linkMan = linkManDao.findOne(2l); 在这里就查询了
//对象导航查询所属的客户
Customer customer = linkMan.getCustomer();
System.out.println(customer);
}