关联关系映射
客观世界中的对象很少有孤立存在的,例如老师,往往与被授课的学生存在关联关系,如果已经得到某个老师的实体,
那么可以获取该老师对应的全部学生。反过来,如果已经得到一个学生实体,
也应该可以访问该学生对应的老师——这种实例之间的相互访问就是关联关系.
关联关系是面向对象分析,面向对象设计最重要的是知识,JPA完全可以正常处理这种关联关系。
如果映射得当,JPA的关联映射将可以大大简化持久层数据的访问。关联关系大致有如下两个分类;
1.单向关联:只需要访问关联端。例如,只能通过老师访问学生,或者只能通过学生访问老师,这种关联关系就是单向关联
2.双向关联:关联的两端可以相互访问。例如,老师和学生之间可以相互访问,这种关联关系就是双向关联
关联关系还存在数量上的区别,假如说有一个老师负责某个班的全部课程,那么这个班级的所有学生都只对应一个老师,
从学生到老师的角度来看,这种关联关系被称为N-1关联;反过来看,老师到学生的关联被称为1-N关联。
对于多个商店顾客,售货员的关系,一个售货员可以向多个顾客提供服务,一个顾客也可能通过多个售货员购买商品,
那么这种关系就被称为N-N关联
从关联关系的数量上来看,单向关联可分为:
一对一 一对多 多对一 多对多
从关联关系的数量上来看,双向关联可分为:
一对一 一对多 多对多
双向关系里没有N-1,因为双向关系1-N和N-1是完全相同的
从两个关联实体的源代码来看,如果两个实体之间存在双向关联,那么两个实体都需要提供属性来访问关联实体;
如果两个实体之间只存在单向关联,那么两个实体只需要在一方提供属性来访问关联实体即可
1.单向N-1关联
N-1是非常常见的关联关系,最常见的父子关系也是N-1关联,单向的N-1关联只需从N的一段可以访问1的一端
单向N-1关系比如多个人对应同一个住址,只需从人实体端可以找到对应的地址实体,无需关心某个地址的全部住户
为了让实体支持这种单向的关联关系,程序应该在N的一端的实体类中增加一个属性,该属性引用1的一端的关联实体
eg:多个人对应同一个地址(N-1)
1.新建数据库
新建数据库person_table:字段为personId,name,age,addressId(外键)
新建数据库address_table:字段为addressId,detail
2.新建主控Person人员类变为实体
@Entity
@Table(name = "persion_table")
public class Persion {
@Id
private int persionId;
@Column
private String name;
@Column
private int age;
//多个人对应一个地址
/**cascade 联级策略(当进行主表操作的时候,从表中的数据同样也会进行操作) ALL:将所有持久化操作都级联到关联实体还支持merge,persist,refresh,remove四个属性
* fetch 抓取策略 EAGER一次性发送多条sql语句进行查询 LAZY延迟加载,使用的时候才进行查询
* optional 指定关联关系是否可选
* targetEntity 指定关联实体的类名。在默认情况下,JPA将通过反射来判断关联实体的类名
*/
@ManyToOne(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
optional = false,
targetEntity = Address.class
)
//连接字段 重命名 在数据库中那个字段体现连接关系
@JoinColumn(
name = "addressId",
nullable = false
)
private Address address;
public Persion() {
}
public int getPersionId() {
return persionId;
}
public void setPersionId(int persionId) {
this.persionId = persionId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
3.新建Address地址类变为实体
@Entity
@Table(name = "address_table")
public class Address {
@Id
private int addressId;
@Column
private String detail;
public Address() {
}
public int getAddressId() {
return addressId;
}
public void setAddressId(int addressId) {
this.addressId = addressId;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}
4.测试类
public class RelationTest {
private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("company");
@Test
public void testMantToOne() {
//数据库关联关系 单向N-1
EntityManager em = emf.createEntityManager();
//地址实体
Address add = new Address();
add.setAddressId(1);
add.setDetail("天津");
//人员实体
Persion per1 = new Persion();
per1.setPersionId(1);
per1.setName("张三");
per1.setAge(20);
per1.setAddress(add);
Persion per2 = new Persion();
per2.setPersionId(2);
per2.setName("李四");
per2.setAge(30);
per2.setAddress(add);
try {
em.getTransaction().begin();
em.persist(add);
em.persist(per1);
em.persist(per2);
em.getTransaction().commit();
System.out.println(per1.getName());
System.out.println(per1.getAddress().getDetail());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
em.getTransaction().rollback();
}finally {
em.close();
}
}
}
2.单向1-1关联
对于单向1-1关联关系,需要在实体类里为关联实体的引用属性增加setter和getter方法。从实体类的代码上来看,
单向1-1与单向N-1没有丝毫区别,因为N的一端,或者1的一端都是直接访问关联实体,
只需要增加关联实体属性的setter和getter方法即可
eg:一个人对应一个地址(1-1)
1.新建数据库
新建数据库person_table:字段为personId,name,age,addressId(外键)
新建数据库address_table:字段为addressId,detail
2.新建主控Person人员类变为实体
@Entity
@Table(name = "persion_table")
public class Persion1t1 {
@Id
private int persionId;
@Column
private String name;
@Column
private int age;
@OneToOne(cascade = CascadeType.ALL,optional = false,
fetch = FetchType.LAZY,targetEntity = Address1t1.class)
@JoinColumn(name = "addressId",nullable = false)
private Address1t1 address;
public Persion1t1() {}
public int getPersionId() {
return persionId;
}
public void setPersionId(int persionId) {
this.persionId = persionId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address1t1 getAddress() {
return address;
}
public void setAddress(Address1t1 address) {
this.address = address;
}
}
3.新建Address住址类变为实体
@Entity
@Table(name = "address_table")
public class Address1t1 {
@Id
private int addressId;
@Column
private String detail;
public Address1t1() {}
public int getAddressId() {
return addressId;
}
public void setAddressId(int addressId) {
this.addressId = addressId;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}
4.测试类
public class RelationTest {
private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("company");
@Test
public void testOneToOne() {
EntityManager em =emf.createEntityManager();
try {
Address1t1 addr = new Address1t1();
addr.setAddressId(2);
addr.setDetail("晋中市");
Persion1t1 per1 = new Persion1t1();
per1.setPersionId(3);
per1.setName("王五");
per1.setAge(40);
per1.setAddress(addr);
em.getTransaction().begin();
em.persist(per1);
em.getTransaction().commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
em.getTransaction().rollback();
}finally {
em.close();
}
}
}
3.单向1-N关联
单向1-N关联的实体类需要做点小调整,实体类需要使用集合属性。因为1的一端需要访问N个关联实体,
那么多个关联实体就需要以集合(Set)形式出现。从这个意义上来看,1-N(实际上还包括N-N)都需要增加一个Set类型的属性,
该属性记录了他的多个关联实体.
使用Set类型的属性来记录关联实体时,最好使用泛型来记录关联实体的实体类,以便JPA可以通过反射来确定关联实体的类型。
当然,也可以通过@OneToMany增加targetEntity属性来明确指定关联实体的类型
eg:一个人部门对应多名员工(1-N)
1.新建数据库
新建数据库emp:字段为empno,ename,sal,deptno(外键)
新建数据库dept:字段为deptno,dname,loc,empno
2.新建主控dept部门类变为实体
@Entity
@Table(name = "dept")
public class Dept1tN{
@Id
private int deptno;
private String dname;
private String loc;
//一个部门多名员工(集合)
@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,
targetEntity = Emp1tN.class)
@JoinColumn(name = "deptno")
private Set<Emp1tN> emps = new HashSet<Emp1tN>();
public Dept1tN() {}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public Set<Emp1tN> getEmps() {
return emps;
}
public void setEmps(Set<Emp1tN> emps) {
this.emps = emps;
}
}
3.新建emp员工类变为实体
@Entity
@Table(name = "emp")
public class Emp1tN{
@Id
private int empno;
private String ename;
private int sal;
public Emp1tN() {}
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getSal() {
return sal;
}
public void setSal(int sal) {
this.sal = sal;
}
}
4.测试
public class RelationTest {
private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("company");
@Test
public void TestOneToMany() {
EntityManager em = emf.createEntityManager();
Dept1tN dept = new Dept1tN();
dept.setDeptno(1);
dept.setDname("研发部");
dept.setLoc("天津市");
Emp1tN emp1 = new Emp1tN();
emp1.setEmpno(1);
emp1.setEname("张三");
emp1.setSal(4000);
Emp1tN emp2 = new Emp1tN();
emp2.setEmpno(2);
emp2.setEname("李四");
emp2.setSal(5000);
Emp1tN emp3 = new Emp1tN();
emp3.setEmpno(3);
emp3.setEname("王五");
emp3.setSal(7000);
//创建一个封装员工实体的集合对象
Set<Emp1tN> emps = new HashSet<Emp1tN>();
emps.add(emp1);
emps.add(emp2);
emps.add(emp3);
dept.setEmps(emps);
try {
em.getTransaction().begin();
em.persist(dept);
em.getTransaction().commit();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
em.getTransaction().rollback();
}finally {
em.close();
}
}
}
4.单向N-N关联
eg:多个人对应多个住址(N-N)
1.1.新建数据库
新建数据库person_table:字段为personId,name,age,addressId(外键)
新建数据库address_table:字段为addressId,detail,personId(外键)
2.新建主控Person人员类变为实体
@Entity
@Table(name="person_table")
public class PersonNtN {
@Id
private int id;
private String name;
private int age;
@ManyToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
targetEntity = AddressNtN.class
)
@JoinTable(
name = "person_address",
joinColumns = @JoinColumn(name="personId"),
inverseJoinColumns = @JoinColumn(name="addressId")
)
private Set<AddressNtN> addresses = new HashSet<AddressNtN>();
public PersonNtN() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Set<AddressNtN> getAddresses() {
return addresses;
}
public void setAddresses(Set<AddressNtN> addresses) {
this.addresses = addresses;
}
}
3.创建Address住址类变为实体
@Entity
@Table(name="address_table")
public class AddressNtN {
@Id
private int id;
private String detail;
public AddressNtN() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}
4.测试类
public class RelationTest {
private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("company");
@Test
public void testConnection() {
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
Query query = em.createQuery("select p.persionId,p.name,p.age,"
+ "a.detail from Person as p inner join"
+ "p.address as a");
List result = query.getResultList();
em.getTransaction().commit();
System.out.println(result);
System.out.println(result.size());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}