1. 单向一对多配置
单向一对多使用@OneToMany标签进行配置,在一方有一个集合属性与多方进行关联,集合可以是List或者Set,区别是List是有序、Set是无序不重复。
对应在一方配置@OneToMany:
/** * 单向一对多:使用JPA配置 */ @Entity @Table(name = "t_productdir") public class ProductDir { @Id @GeneratedValue private Integer id; @Column(name = "dName") private String dirName; //产品分类名称 /** * 单向一对多:在一方使用集合Set或者List进行多方关系的维护 * 对于集合,必须要先new出来 * * 对于数据库:不管是多对一,还是一对多,不管是单向还是双向, * 数据库的设置都是不变的,哪边是多方,外键就在哪边 * 单向一对多,默认就是使用的懒加载(以后都把懒加载配置上) */ @OneToMany(fetch = FetchType.LAZY) @JoinColumn(name = "dir_id") @OrderBy("id desc") //需要排序的时候使用@OrderBy来拿值,多个排序属性之间使用","分割,并且一定要用List集合 private SetproductSet = new HashSet<>(); //集合默认懒加载 public ProductDir() { }
性能:单向一对多无论配置懒加载还是迫切加载都发送相同数量的SQL语句,性能极差。
2. 双向一对多、多对一配置
2.1.基础配置
双向一对多、多对一需要同时配置两边的属性,一方与多方都要有关联属性存在(同时配置@ManyToOne、@OneToMany),同时在一方放弃关系维护配置mappedBy。具体配置如下:
多方Product:
/** * 双向一对多、多对一:使用JPA配置 */ @Entity @Table(name = "t_product") public class Product { @Id @GeneratedValue private Integer id; @Column(name = "t_name") private String name; //产品名称 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dir_id") private ProductDir productDir; public ProductDir getProductDir() { return productDir; } public void setProductDir(ProductDir productDir) { this.productDir = productDir; } public Product() { } }
一方ProductDir:
/** * 双向一对多、多对一:使用JPA配置 */ @Entity @Table(name = "t_productdir") public class ProductDir { @Id @GeneratedValue private Integer id; @Column(name = "dName") private String dirName; //产品分类名称 //mappedBy:一方放弃关系维护,把关系维护交给多方,注意mappedBy中的值必须和Product中ProductDir的属性一样 //注意:使用mappedBy就不要使用JoinColumn了,这边已经不需要维护关系了 @OneToMany(fetch = FetchType.LAZY, mappedBy = "productDir") //@JoinColumn(name = "dir_id") // @OrderBy("id desc") //需要排序的时候使用@OrderBy来拿值,多个排序属性之间使用","分割,并且一定要用List集合 private SetproductSet = new HashSet<>(); //集合默认懒加载 public ProductDir() { } }
2.2.级联配置
在一方的多方属性@OneToMany上使用cascade表示级联,主要有以下几种配置方式:
① Cascade=CascadeType.PERSIST:级联保存;
② Cascade=CascadeType.REMOVE:级联删除(很危险);
③ Cascade=CascadeType.ALL:级联增删改
3. 单向多对多
多对多关系我们以保存2个用户(user)、3个角色(role)来进行多对多的测试;
用户与角色是多对多的关系,一个用户对应多个角色,一个角色可以由多个用户充当。
多堆多关系涉及到一张中间表user_role,
单向关系:通过用户可以找到多个角色,而角色不能找到对应的用户。
3.1.单向多对多配置
主表:User用户类
/** * 基于单向多对多,在用户方可以找到对应的角色(角色不能找到对应用户),只需要在用户方配置@ManyToMany注解映射 */ @Entity @Table(name="t_user") public class User { @GeneratedValue @Id private Long id; private String name; // @ManyToMany注释表示User是多对多关系的一端。 // @JoinTable描述了多对多关系的中间表关系。name属性指定中间表名称, // joinColumns定义中间表与当前类User的外键关系。inverseJoinColumns定义中间表与关联类Role的外键关系。 @ManyToMany @JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") }) private Setroles = new HashSet (); public User() { } public User(String name) { this.name = name; } }
从表:Role角色类
@Entity @Table(name="t_role") public class Role { @GeneratedValue @Id private Long id; private String name; public Role() { } public Role(String name) { this.name = name; } }
对应创建测试类:
/** * 保存2个用户,保存3个角色(5条) */ @Test public void persistUserTest() { User user1 = new User("user1"); User user2 = new User("user2"); Role role1 = new Role("role1"); Role role2 = new Role("role2"); Role role3 = new Role("role3"); // 保存中间表:建立用户到角色关系user1(role1,role2),user2(role1,role2,role3)(5条) user1.getRoles().add(role1); user1.getRoles().add(role2); user2.getRoles().add(role1); user2.getRoles().add(role2); user2.getRoles().add(role3); EntityManager entityManager = JPAUtil.getEntityManager(); entityManager.getTransaction().begin(); //保存用户 entityManager.persist(user1); entityManager.persist(user2); // 保存角色 entityManager.persist(role1); entityManager.persist(role2); entityManager.persist(role3); entityManager.getTransaction().commit(); JPAUtil.close(entityManager); }
4. 双向多对多
4.1.双向多堆多配置
双向多对多即是在两方同时配置@ManyToMany注解,具体配置如下:
一方Role角色配置:
@Entity @Table(name="t_role") public class Role { @GeneratedValue(strategy = GenerationType.AUTO) @Id private Long id; private String name; // @ManyToMany注释表示Role是多对多关系的一端。 @ManyToMany //joinColumns表示中间表与当前Role表的连接外键,inverseJoinColumns表示多方User与中间表的外键连接关系 @JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = { @JoinColumn(name = "user_id") }) private SetuserSet = new HashSet<>(); public Role() { } public Set getUserSet() { return userSet; } public void setUserSet(Set userSet) { this.userSet = userSet; } public Role(String name) { this.name = name; } }
多方User用户配置:
/** * 基于单向多对多,在用户方可以找到对应的角色(角色不能找到对应用户),只需要在用户方配置@ManyToMany注解映射 */ @Entity @Table(name="t_user") public class User { @GeneratedValue(strategy = GenerationType.AUTO) @Id private Long id; private String name; // @ManyToMany注释表示User是多对多关系的一端。 // @JoinTable描述了多对多关系的中间表关系。name属性指定中间表名称, // joinColumns定义中间表与当前类User的外键关系。inverseJoinColumns定义中间表与关联类Role的外键关系。 @ManyToMany @JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") }) private Setroles = new HashSet (); public User() { } public User(String name) { this.name = name; } }
5. 一对一配置
一对一场景在实际项目中用得比较少,这里主要以QQ与QQ空间为例,有QQ才会有QQ空间。
5.1.唯一外键一对一
QQ类(主一)
@Entity public class QQ { @Id @GeneratedValue private Long id; private String number; // 一对一,一个qq号码对应一个qq空间 @OneToOne(mappedBy="qq") private QQZone zone; }
QQ空间类(从一)
@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; }
5.1.1保存数据测试
public void persist() throws Exception { QQ qq = new QQ(); qq.setNumber("123456"); QQZone zone = new QQZone(); zone.setName("枫夜"); // 建立关系 qq.setZone(zone); zone.setQq(qq); EntityManager entityManager = JPAUtils.getEntityManager(); entityManager.getTransaction().begin(); // 先保存主一 entityManager.persist(qq); entityManager.persist(zone); entityManager.getTransaction().commit(); entityManager.close(); }
5.2.共享主键一对一
QQ空间类(从一)
@Entity public class QQZone { @Id @GeneratedValue(generator = "fkGenerator") @GenericGenerator(name = "fkGenerator", strategy = "foreign", parameters = @Parameter(name = "property", value = "qq")) private Long id; private String name; // 一对一,一个qq空间输入一个qq号码 @OneToOne(optional = false) // 如果不加这个注解,添加QQZone信息时,就会自动在QQZone表中增加了一个外键qq_id @PrimaryKeyJoinColumn private QQ qq; }