单向一对多
单向一对多是指,由一方来控制外键,多方不管理,这种方法会影响性能,不建议使用
在一方类中定义一个集合Set对象(对象要使用多态的写法,并且加上泛型),然后在其上打上注解@OneToMany表明交给一方来管理外键
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
@Entity
public class ProductDir {
@Id
@GeneratedValue
private Long id;
private String name;
//表明交给一方管理
@OneToMany
@JoinColumn(name = "dir_id")
// 集合映射
private Set<Product> products = new HashSet<Product>();
在单项一对多中,我们保存数据时,无论是先保存对放还是先报错一方,都会产生五条sql语句,这是因为交给一方管理时必须发出额外的Update语句。
单向一对多默认的是懒加载模式,我们可以设置其加载的模式。
一般Many在后面的时候都是延迟加载。
小总结:单向一对多影响性能,不建议使用,默认加载方式是延迟加载
集合映射
声明集合的时候必须使用多态写法也就是接口
例:Set<Product> products = new HashSet<>();
因为Hibernate在创建了集合对象后是使用了它的的集合类PersistentSet来接收数据的(而不是我们写的那个HashSet)。如果我们使用接口声明(如Set),那么它的集合类PersistentSet也是实现了这个接口。所以它可以把这个类赋给Set。 但是我们使用HashSet,它们关系是平级的,不能进行转换,然后会出现代码错误
总结:声明集合的时候必须使用接口
集合映射有两种:
java.util.Set对应PersistentSet Set:无序且不可重复
java.util.List对应PersistentBag List:有序且可重复,当我们需要的存入的数据是有序且可重复的时候用List,一般是组合关系用
Set:无序,不可以重复
@OneToMany
@JoinColumn(name = "dir_id")
private Set<Product> products = new HashSet<Product>();
List:有序,且可重复
@OneToMany
@JoinColumn(name = "dir_id")
@OrderBy("price DESC") //设置排序方式,售价降序
private List<Product> products = new ArrayList<Product>();
总结:一般组合关系采用List,其他的时候一般用Set
使用List集合的时候可以使用@OrdeBy来排列顺序
双向多对一&双向一对多
双向直接理解:你认得我(OneToMany),那我也要认识你(ManyToOne)
添加数据性能优化:
选择一:在java代码中让多方来维护关系(性能高),类似于单向多对一
选择二:在java 代码中让一方来维护关系(性能低),类似于单向一对多。
双向配置:
让一方失去对外键的管理,让多方来管理外键
OneToMany(mappedBy = "dir")表示一方的关系参照多方Prodcut属性dir来管理
@Entity//一方
@Table(name = "t_productDir")
public class ProductDir {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "dir")//一方失去管理外键功能
private Set<Product> products =new HashSet();
}
@Entity//多方
@Table(name = "t_product")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "dir_id")
private ProductDir dir;
}
级联操作
级联操作一般都是用在组合关系的
级联保存映射配置:@OneToMany(cascade = CascadeType.PERSIST, mappedBy = “dir”)
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "dir")
private Set<Product> products = new HashSet<Product>();
当我们保存一方时,它就会对应的将多方也保存起来
级联保存
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "dir")
1.mappedBy = "dir"表示一方的关系参照多方Prodcut属性dir来管理
2.cascade = CascadeType.PERSIST
3.必须2边都建立关系
4.entityManager.persist(dir);只能保存一方
级联删除:
1.删除一方,然后多方都删除(危险操作)
@OneToMany(cascade = CascadeType.REMOVE, mappedBy = “dir”)
2.从一方去删除一个解除关系(外键的值为null)的多方:先获取一方,再删除一个多方
@OneToMany(mappedBy = “dir”,orphanRemoval = true) 解除关联关系
@Test
public void testRemoveOne() throws Exception {
EntityManager entityManager = JpaUtils.getEntityManager();
entityManager.getTransaction().begin();
//获取产品种类4
ProductDir productDir = entityManager.find(ProductDir.class, 4L);
//获取产品3
Product product = entityManager.find(Product.class, 3L);
// 删除所有和当前产品种类对象解除关联关系的产品对象
productDir.getProducts().remove(product);
entityManager.getTransaction().commit();
}
3.从一方去删除一个解除关系(外键的值为null)的多方:先获取一方,在删除一个多方
@Test
public void testRemoveOne() throws Exception {
EntityManager entityManager = JpaUtils.getEntityManager();
entityManager.getTransaction().begin();
//获取产品种类4
ProductDir productDir = entityManager.find(ProductDir.class, 4L);
// 删除所有和当前对象解除关联关系的对象
productDir.getProducts().clear();
entityManager.getTransaction().commit();
}
强级联CascadeType.ALL:
CascadeType.ALL包含级联删除,级联保存,可是在删除的时候比较危险操作
单向多对多
意思:双向多对多就是配置两个多对多即可,就是一个演员可能有多种角色
配置信息:
@Entity//演员多方
@Table(name="t_user")
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany//单向多对多
//name为中间表名,中间表列名user_id作为user的外键,反之role_id作为role的外键
@JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") },inverseJoinColumns= {
@JoinColumn(name = "role_id") })
private Set<Role> roles= new HashSet();
}
@Entity//角色多方
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue
private Long id;
private String name;
}
我们保存三个用户(user),两个角色(role)来进来多对多的测试
一个用户拥有多个角色
反过来一个角色也可以拥有多个用户
相当于2个多对一叠加变成多对多,多了一张中间表user_role
单向:用户可以找到多个角色
单向多对多:运用的是懒加载
双向多对多
双向多对多就是多个单向多对多
配置信息:
@Entity//演员多方
@Table(name="t_user")
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
//使用级联保存,这样保存时只用保存User即可
@ManyToMany(cascade = CascadeType.PERSIST)//单向多对多
//name为中间表名,中间表列名user_id作为user的外键,反之role_id作为role的外键
@JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") },inverseJoinColumns= {
@JoinColumn(name = "role_id") })
private Set<Role> roles= new HashSet();
}
@Entity//角色多方
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany//双向多对多
//name为中间表名,中间表列名role_id作为role的外键,反之user_id作为user的外键
@JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "role_id") },inverseJoinColumns= {
@JoinColumn(name = "user_id") })
private Set<User> users= new HashSet();
}
双向多对多删除用户User:指定删除user1,由Hibernate自动处理,先删除中间表,再删除user1,在测试方法中直接找出user1,然后remove即可
双向多对多删除用户User:删除user1的一个角色,不能删除user1(只删除中间表)
//找到那个要删除的用户
User user = entityManager.find(User.class, 1L);
//然后清除其角色
user.getRoles().clear();
双向多对多修改角色:先删除再添加
双向级联删除:
配置user方,就会删除当前对象的所有role以及user本身
配置双方,就会删除所有数据
(双向级联配置删除是特别危险的)
一对一
一对一:唯一外键一对一,可以修改为一对多
@Entity
public class QQ {
@Id
@GeneratedValue
private Long id;
private String number;
// 一对一,一个qq号码对应一个qq空间
@OneToOne(mappedBy="qq")
private QQZone zone;
}
@Entity
public class QQZone {
@Id
@GeneratedValue
private Long id;
private String name;
// 一对一,一个qq空间输入一个qq号码
// 默认值optional = true表示qq_id可以为空;反之。。。
@OneToOne(optional = false)
// unique=true确保了一对一关系
@JoinColumn(name = "qq_id", unique = true)
private QQ qq;
}