JPA中的映射关系

jpa中维护one to one ,one to many, many to one ,many to many 四种映射关系。

      在每个关系中,双方中的一方在其表中拥有连接列。那么一方称为所有方(owning side) 或者关系的所有者。

不具有连接列的一方称之为非所有方(non-owning)或者反方

      所有权对于映射很重要,因为用于定义映射到数据库序列的物理注解(例如,@JoinColumn总是在关系的所有方定义)。如果它们不存在,那么值的默认值将从所有方的特性的角度来考虑。

     多对一映射总是在关系的所有方之上,所以如果在拥有多对一方的关系中发现一个@JoinColumn,那么这将是所有方的位置。

注解@JoinColumn,需要指定连接列的名称,可以使用name元素。

举个例子来说: 学生和课程的关系定为一对多,

外键维护在多的一方,此时课程表映射的实体就是owning-side,学生表就是inverse-side

课程表

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_通用实践

学生表

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_Spring_02

关系映射的使用到的注解说明

@JoinColumn

标记一个列指定实体的关联关系的。可以和@OneToMany或者@ManyToOne搭配使用

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_SpringDataJpa_03

@OneToMany

在一的一方定义一对多的关联关系,并且如果关联关系时双向的,mappedBy属性必须用来标注,在拥有关联关系的实体一方中表示关系的字段名,也就是使用mappedBy属性是不维护关联关系的一方,值是拥有关联关系一方中标识关系的字段名

使用了mappedBy属性后,不能在使用@JoinColumn注解,会抛异常

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_SpringDataJpa_04

 

@ManyToOne 没有mappedBy属性.

@OneToOne /@ManyToMany

都是在实体的字段上表示对应的关联关系,在表示双向关联关系时候,都必须使用mappedBy属性

 

单向多对一映射

 

 成员和班级是多对一映射,并且外键列定义在成员表中,那么创建成员实体的时候,需要使用@JoinColumn标注

成员是关系的拥有者。也就是关系为多的一方持有

@JoinColumn的值是数据库中的外键名称,@JoinColumn和@ManyToOne搭配维护了关联关系,此时是单向的关联

Partner实体类

1. 

2.

@Entity


3.


@Table(name="partner_info")


4.


public class Partner {


5.

private Integer partner_id;


6.

private String partner_name;


7.

private Grade grade;


8.

@Id



9.

@Column(name="partner_id")


10.

@GeneratedValue(strategy=GenerationType.IDENTITY)


11.

public Integer getPartner_id() {


12.

return partner_id;


13.

}


14.

public void setPartner_id(Integer partner_id) {


15.

this.partner_id = partner_id;


16.

}


17.

@Column(name="partner_name")


18.

public String getPartner_name() {


19.

return partner_name;


20.

}


21.

public void setPartner_name(String partner_name) {


22.

this.partner_name = partner_name;


23.

}


24.

@JoinColumn(name="team_id")


25.

@ManyToOne



26.

public Grade getGrade() {


27.

return grade;


28.

}


29.

public void setGrade(Grade grade) {


30.

this.grade = grade;


31.

}


32.

}


33.



Grade实体类

1. 

2.

@Entity


3.


@Table(name="grade_info")


4.


public class Grade {


5.

private Integer teamId;


6.

private String teamName;


7.

@Id



8.

@Column(name="team_id")


9.

@GeneratedValue(strategy=GenerationType.IDENTITY)


10.

public Integer getTeamId() {


11.

return teamId;


12.

}


13.

public void setTeamId(Integer teamId) {


14.

this.teamId = teamId;


15.

}


16.

@Column(name="team_name",columnDefinition="varchar(50) not null ")


17.

public String getTeamName() {


18.

return teamName;


19.

}


20.

public void setTeamName(String teamName) {


21.

this.teamName = teamName;


22.

}


23.

}


24.



测试下:

1. 

2.


public static void main(String[] args) {


3.

EntityManagerFactory factory = Persistence.createEntityManagerFactory("jpa");


4.

EntityManager entityManager = factory.createEntityManager();


5.

EntityTransaction transaction = entityManager.getTransaction();


6.

transaction.begin();


7.

//执行insert操作



8.

Grade grade = new Grade();


9.

grade.setTeamName("初中1班");


10.




11.

Partner p1 = new Partner();


12.

p1.setPartner_name("张三风");


13.




14.

Partner p2 = new Partner();


15.

p2.setPartner_name("李四关");


16.




17.

p1.setGrade(grade);


18.

p2.setGrade(grade);


19.




20.

entityManager.persist(grade);


21.

entityManager.persist(p1);


22.

entityManager.persist(p2);


23.

transaction.commit();


24.

entityManager.close();


25.

factory.close();


26.

}


27.



在实体创建中,字段的定义和数据库表的字段类型、长度、是否非空一致,不一致容易出问题。

如果出现问题,可以试着将数据库的表删除,交给JPA自己创建,持久化的过程会自动创建表。JPA会自动建表和外键关系.

每一个建立的实体都是javaBean风格的。

数据库中原来不存在表partner_info和grade_info,通过JPA将会在数据库中将生成对应的表,并且生成外键关系

JPA自动建表的策略需要配置: hibernate.ddl-auto 属性

hibernate.ddl-auto节点的值有几个create、create-drop、update、validate、none

create:每次加载hibernate会自动创建表,以后启动会覆盖之前的表,所以这个值基本不用,严重会导致的数据的丢失。

create-drop : 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除,下一次启动会重新创建。

update: 加载hibernate时根据实体类model创建数据库表,这是表名的依据是@Entity注解的值或者@Table注解的值,sessionFactory关闭表不会删除,且下一次启动会根据实体                   model更新结构或者有新的实体类会创建新的表。

validate:启动时验证表的结构,不会创建表

none:启动时不做任何操作

 

表的字段字段类型可以使用@Column注解的columnDefinition 指定。

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_SpringDataJpa_05

如果表创建失败,是映射表没有定义正确

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_通用实践_06

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_通用实践_07

持久化顺序,是先持久化一的一方,再去持久化多的一方,这样不会多执行update语句

单向一对一映射

员工Employee到停车位ParkingSpace的关系是一对一关系,一对一映射在一个数据库表中存在一个连接列(外键列),需要在拥有外键列的一方实体中使用@JoinColumn注解中指定列名,并使用@OneToOne注解标识映射关系

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_通用实践_08

1. 

2.

@Entity


3.


@Table(name="employee")


4.


public class Employee {


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.IDENTITY)


8.

private Integer id;


9.

@Column(name="name")


10.

private String name;


11.

@Column(name="salary",columnDefinition="DECIMAL(10,2)")


12.

private BigDecimal salary;


13.

@JoinColumn(name="pspace_id")


14.

@OneToOne



15.

private ParkingSpace ps;


16.

public Integer getId() {


17.

return id;


18.

}


19.

public void setId(Integer id) {


20.

this.id = id;


21.

}


22.

public String getName() {


23.

return name;


24.

}


25.

public void setName(String name) {


26.

this.name = name;


27.

}


28.

public BigDecimal getSalary() {


29.

return salary;


30.

}


31.

public void setSalary(BigDecimal salary) {


32.

this.salary = salary;


33.

}


34.

public ParkingSpace getPs() {


35.

return ps;


36.

}


37.

public void setPs(ParkingSpace ps) {


38.

this.ps = ps;


39.

}


40.

}


41.


1. 

2.

@Entity


3.


@Table(name="parking_space")


4.


public class ParkingSpace{


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.IDENTITY)


8.

private Integer id;


9.

@Column(name="lot")


10.

private Integer lot;


11.

@Column(name="location")


12.

private String location;


13.

public Integer getId() {


14.

return id;


15.

}


16.

public void setId(Integer id) {


17.

this.id = id;


18.

}


19.

public String getLocation() {


20.

return location;


21.

}


22.

public void setLocation(String location) {


23.

this.location = location;


24.

}


25.

public Integer getLot() {


26.

return lot;


27.

}


28.

public void setLot(Integer lot) {


29.

this.lot = lot;


30.

}


31.

}


32.



双向一对一关系

现在员工已经指向停车位,只要停车位实体在指向员就构成双向的关系

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_SpringDataJpa_09

首先,必须清楚包含连接列的一方,决定了是关系的所有者,可是在双向一对一关系中,两个映射均是一对一映射,两方

都可以是所有者,最后,关系的拥有者只能有一个,连接列只能在一方,另一方只能指向关系的拥有者

ParkingSpace实体,添加@OneToOne表示映射关系,并且添加mappBy元素表示关系的所有方是Employee。

ParkingSpace修改如下;

1. 

2.

@Entity


3.


@Table(name="parking_space")


4.


public class ParkingSpace{


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.IDENTITY)


8.

private Integer id;


9.

@Column(name="lot")


10.

private Integer lot;


11.

@Column(name="location")


12.

private String location;


13.

@OneToOne(mappedBy="ps")


14.

private Employee el;


15.

public Employee getEl() {


16.

return el;


17.

}


18.

public void setEl(Employee el) {


19.

this.el = el;


20.

}


21.

...........


22.



双向一对一关联的规则;

1. @JoinColumn注解只能放置在映射到包含连接列的表的实体上。

2. mappedBy元素应该在没有定义连接列的实体的@OneToOne注解中指定,就是没有持有关系的一方使用

3. 双向一对一映射关系中只能有一方使用mappedBy属性

集合值关联

当源实体引用一个或者多个目标实体实例时候,将使用一个多值关联(many-value association)或者关联集合。体现在一对多和多对多关联关系中

1. 一对多映射

员工Employee和部门Department的关系,如果使用一对多映射表示,本质上是双向的

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_java经验集锦_10

一个关系是双向的,意味着存在两个映射关系。双向的一对多映射意味着一个回源的多对一映射。

当一个源实体中有任意数量的目标实体存储在它的集合属性中,没有可以扩展的方式可以用于在数据库表中存储这些

它所映射到的引用。如何在单行中存储任意数量的外键?这是行不通的,因此,必须让集合中的实体表能够指向会

源实体表的外键。因而一对多是双向的。

1. 

2.

@Entity


3.


@Table(name="department_info")


4.


public class DepartmentInfo {


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.AUTO)


8.

private Integer id;


9.

@Column(name="name")


10.

private String name;


11.

@OneToMany(mappedBy="department")


12.

private List<Employee> list = new ArrayList<Employee>();


13.

public Integer getId() {


14.

return id;


15.

}


16.

public void setId(Integer id) {


17.

this.id = id;


18.

}


19.

public String getName() {


20.

return name;


21.

}


22.

public void setName(String name) {


23.

this.name = name;


24.

}


25.

public List<Employee> getList() {


26.

return list;


27.

}


28.

public void setList(List<Employee> list) {


29.

this.list = list;


30.

}


31.

}


32.


1. 

2.

@Entity


3.


@Table(name="employee")


4.


public class Employee {


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.IDENTITY)


8.

private Integer id;


9.

@Column(name="name")


10.

private String name;


11.

@Column(name="salary",columnDefinition="DECIMAL(10,2)")


12.

private BigDecimal salary;


13.

@JoinColumn(name="department_id")


14.

@ManyToOne



15.

private DepartmentInfo department;


16.

public Integer getId() {


17.

return id;


18.

}


19.

public void setId(Integer id) {


20.

this.id = id;


21.

}


22.

public String getName() {


23.

return name;


24.

}


25.

public void setName(String name) {


26.

this.name = name;


27.

}


28.

public BigDecimal getSalary() {


29.

return salary;


30.

}


31.

public void setSalary(BigDecimal salary) {


32.

this.salary = salary;


33.

}


34.

public DepartmentInfo getDepartment() {


35.

return department;


36.

}


37.

public void setDepartment(DepartmentInfo department) {


38.

this.department = department;


39.

}


40.

}


41.



持久化先持久化一的一方,并且从持有关系的一方设值。

1. 

2.

DepartmentInfo di = new DepartmentInfo();


3.

di.setName("java开发部2");


4.

Employee e1 = new Employee();


5.

e1.setName("张三1");


6.

e1.setSalary(new BigDecimal(110));


7.

Employee e2 = new Employee();


8.

e2.setName("李四1");


9.

e2.setSalary(new BigDecimal(200));


10.

e1.setDepartment(di);


11.

e2.setDepartment(di);


12.

//di.getList().add(e1); 持久化,只能从拥有关系的一方设值


13.

//di.getList().add(e2);


14.

entityManager.persist(di);


15.

entityManager.persist(e1);


16.

entityManager.persist(e2);


17.



关于部门实体,有几点需要注意

1. 使用明确类型的集合类来存储,如果并不指定存储的类型,就必须使用targetEntity指示指向的类的信息。

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_Spring_11

2. 定义双向一对多关系时候,必须记住多的一方是关系的所有方,必须在那一方定义连接列,一对多的映射是反方,必须使用mappedBy元素

多对多映射

多对多是双向的关系映射,关系双方都是多对多关系,并且双方均没有连接列,需要借助中间表。因为每一个双向关系都必须具有所有方和反方,必须两个实体中挑选一个作为关系的持有者,反方使用mappedBy属性指向持有关系的一方。

使用@ManyToMany在实体的集合属性上表示映射关系。

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_java经验集锦_12

1. 

2.

@Entity


3.


@Table(name="employee")


4.


public class Employee {


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.IDENTITY)


8.

private Integer id;


9.

@Column(name="name")


10.

private String name;


11.

@Column(name="salary",columnDefinition="DECIMAL(10,2)")


12.

private BigDecimal salary;


13.

@JoinTable(name="employee_project_inner",


14.

//中间表product_id字段



15.

joinColumns={@JoinColumn(name="employee_id",referencedColumnName="id")},


16.

inverseJoinColumns={@JoinColumn(name="project_id",referencedColumnName="id")}


17.

)


18.

@ManyToMany



19.

private List<ProjectInfo> projects = new ArrayList<ProjectInfo>();


20.

public Integer getId() {


21.

return id;


22.

}


23.

public void setId(Integer id) {


24.

this.id = id;


25.

}


26.

public String getName() {


27.

return name;


28.

}


29.

public void setName(String name) {


30.

this.name = name;


31.

}


32.

public BigDecimal getSalary() {


33.

return salary;


34.

}


35.

public void setSalary(BigDecimal salary) {


36.

this.salary = salary;


37.

}


38.

public List<ProjectInfo> getProjects() {


39.

return projects;


40.

}


41.

public void setProjects(List<ProjectInfo> projects) {


42.

this.projects = projects;


43.

}


44.

}


45.



@JoinTable说明;

joinColumns元素描述关系所有方在中间表的连接列,inverseJoinColumns元素指定了反方在中间表的连接列。

反方,使用mappedBy属性指向关系拥有的一方。

1. 

2.

@Entity


3.


@Table(name="project_info")


4.


public class ProjectInfo {


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.IDENTITY)


8.

private Integer id;


9.

@Column(name="name")


10.

private String name;


11.

@ManyToMany(mappedBy="projects")


12.

private List<Employee> employees = new ArrayList<Employee>();


13.

public Integer getId() {


14.

return id;


15.

}


16.

public void setId(Integer id) {


17.

this.id = id;


18.

}


19.

public String getName() {


20.

return name;


21.

}


22.

public void setName(String name) {


23.

this.name = name;


24.

}


25.

}


26.



单向集合映射

 

       当一个实体到目标实体存在一对多映射,但是@OneToMany注解不包括mappedBy元素时,就认为它存在目标实体的单向关系,此时,两个实体都不存在连接列,是单向的映射关系

 

       需要借助中间表,存储映射关系

JPA中映射关系详细说明(一对多,多对一,一对一、多对多)、@JoinColumn、mappedBy说明_SpringDataJpa_13

,同样的当多对多关系中,一方没有映射到另一方时候,就是一个单向的关系,也要借助连接表。

唯一的区别是,只有两个实体类型中的一个会使用该表来加载器相关的实体,或者更新它已存储新增的实体关联

创建实体:

目标实体中不会存在集合特性,源实体的@OneToMany注解中不存在mappedBy属性,

就是phone中没有集合属性,employee中@OneToMany注解不用mappedBy属性

1. 

2.

@Entity


3.


@Table(name="employee")


4.


public class Employee {


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.IDENTITY)


8.

private Integer id;


9.

@Column(name="name")


10.

private String name;


11.

@Column(name="salary",columnDefinition="DECIMAL(10,2)")


12.

private BigDecimal salary;


13.

@JoinTable(name="employee_phone_inner",


14.

//中间表product_id字段



15.

joinColumns={@JoinColumn(name="employee_id",referencedColumnName="id")},


16.

inverseJoinColumns={@JoinColumn(name="phone_id",referencedColumnName="id")}


17.

)


18.

@OneToMany



19.

private List<Phone> phones = new ArrayList<Phone>(); //.. 省略get/set方法



20.


1. 

2.

@Entity


3.


@Table(name="phone")


4.


public class Phone {


5.

@Id



6.

@Column(name="id")


7.

@GeneratedValue(strategy=GenerationType.IDENTITY)


8.

private Integer id;


9.

@Column(name="name")


10.

private String num;


11.

public Integer getId() {


12.

return id;


13.

}


14.

public void setId(Integer id) {


15.

this.id = id;


16.

}


17.

public String getNum() {


18.

return num;


19.

}


20.

public void setNum(String num) {


21.

this.num = num;


22.

}


23.

}


24.