1-m:多的一方为关系维护端,关系维护端负责外键纪录的更新,关系被维护端没有权力更新外键纪录.
拥有mappedBy注解的实体类为关系被维护端,另外的实体类为关系维护端的。顾名思意,关系的维护端对关系(在多对多为中间关联表)的CRUD做操作。关系的被维护端没有该操作,不能维护关系。
@ManyToOne表示一个多对一的映射,该注解标注的属性通常是数据库表的外键
optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true
fetch:表示抓取策略,默认为FetchType.EAGER(一的一端默认为立即加载,多的一端默认为懒加载)
cascade:表示默认的级联操作策略,可以指定为ALL,PERSIST,MERGE,REFRESH和REMOVE中的若干组合,默认为无级联操作
targetEntity:表示该属性关联的实体类型.该属性通常不必指定,ORM框架根据属性类型自动判断targetEntity.
@JoinColumn
@JoinColumn 和 @Column类似,指明此属性描述的不是一个简单字段,而是一个关联字段
name:该字段的名称(指定关联关系中的维护端对应的表中和被维护端的主键进行关联的字段的名称)
referencedColumnName:属性指定关联关系中的被维护端与关联关系中的维护端对应的表之间形成关联关系的字段名称,通常用于关联关系中的被维护端的关联字段不是自己的主键的情况
@OneToMany(fetch=FetchType,cascade=CascadeType)
@OneToMany描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段.
维护端注解
@ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH }, optional = false)
@JoinColumn(name = "order_id")
被维护端注解
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH,
CascadeType.MERGE, CascadeType.REMOVE },
fetch = FetchType.EAGER,
mappedBy = "order")
@Entity
@Table(name = "order_info")
public class OrderInfo {
private Integer id;
private String name;
private Set<OrderItem> items = new HashSet<OrderItem>();
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH,
CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.EAGER,
mappedBy = "order")
public Set<OrderItem> getItems() {
return items;
}
public void setItems(Set<OrderItem> items) {
this.items = items;
}
public void addOrderItem(OrderItem orderItem) {
orderItem.setOrder(this);
this.items.add(orderItem);
}
}
@Entity
@Table(name = "order_item")
public class OrderItem {
private Integer Id;
private String name;
private OrderInfo order;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return Id;
}
public void setId(Integer id) {
Id = id;
}
@Column(length = 20, nullable = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH }, optional = false)
@JoinColumn(name = "order_id")
public OrderInfo getOrder() {
return order;
}
public void setOrder(OrderInfo order) {
this.order = order;
}
}
JPA多对多双向
维护端注解
@ManyToMany (cascade = CascadeType.REFRESH)
@JoinTable (//关联表
name = "student_teacher" , //关联表名
inverseJoinColumns = @JoinColumn (name = "teacher_id" ),//被维护端外键
joinColumns = @JoinColumn (name = "student_id" ))//维护端外键
被维护端注解
@ManyToMany(
cascade = CascadeType.REFRESH,
mappedBy = "teachers",//通过维护端的属性关联
fetch = FetchType.LAZY)
@Entity
public class Student {
private Integer id;
private String name;
private Set<Teacher> teachers = new HashSet<Teacher>();
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(nullable = false, length = 16)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(cascade = CascadeType.REFRESH)
@JoinTable(name = "student_teacher", inverseJoinColumns = @JoinColumn(name = "teacher_id"), joinColumns = @JoinColumn(name = "student_id"))
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
public void addTeacher(Teacher teacher) {
this.teachers.add(teacher);
}
public void removeTeachers(Teacher teacher) {
this.teachers.remove(teacher);
}
}
@Entity
public class Teacher {
private Integer id;
private String name;
private Set<Student> students = new HashSet<Student>();
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(nullable = false, length = 16)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(cascade = CascadeType.REFRESH, mappedBy = "teachers", fetch = FetchType.LAZY)
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}
JPA双向一对一
@OneToOne 注释
public @interface OneToOne
{
Class targetEntity( ) default void.class;
CascadeType[] cascade( ) default {};
FetchType fetch( ) default EAGER;
boolean optional( ) default true;
String mappedBy( ) default "";
}
@Entity
public class Person {
@Id @GeneratedValue
private int id;
@Column(nullable=false)
private String name;
@OneToOne(cascade=CascadeType.ALL,optional=false)
@JoinColumn(name="idCard_id")//关系维护端设置外键
private IDCard idCard;
//省略getter setter...
}
@Entity
public class IDCard {
@Id @GeneratedValue
private int id;
@Column(length=18,nullable=false)
private String idNumber;
@OneToOne(mappedBy="idCard",cascade={CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH},optional=false,fetch=FetchType.EAGER)
private Person person;//被维护端,idCard为维护端里的属性,optional=false不允许为null
//省略getter setter...
}
JPA复合主键映射
一个实体的主键可能同时构成,当且仅当多个字段的值完全相同时,才认为是相同的实体对象。
复合主键映射时,通常构成将复合主键的多个字段单独抽取出来建一个类作为符合主键类。
符合主键类必须满足以下几点要求
必须实现Serializable接口。
必须有默认的public无参数的构造方法。
必须覆盖equals和hashCode方法。equals方法用于判断两个对象是否相同,EntityManger通过find方法来查找Entity时,是根据equals的返回值来判断的。
@Embeddable
public class AirLinePK implements Serializable {
@Column(nullable=false,length=3,name="LEAVECITY")
private String leavecity;
@Column(nullable=false,length=3,name="ARRIVECITY")
private String arrivecity;
public AirLinePK(){}
public AirLinePK(String leavecity, String arrivecity) {
this.leavecity = leavecity;
this.arrivecity = arrivecity;
}
@Override
public int hashCode() {
int hash = 0;
hash += (this.leavecity!=null && this.arrivecity!=null ? (this.leavecity+ "-"+ this.arrivecity).hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof AirLinePK)) {
return false;
}
AirLinePK other = (AirLinePK)object;
if (this.leavecity != other.leavecity && (this.leavecity == null || !this.leavecity.equalsIgnoreCase(other.leavecity))) return false;
if (this.arrivecity != other.arrivecity && (this.arrivecity == null || !this.arrivecity.equalsIgnoreCase(other.arrivecity))) return false;
return true;
}
//省略getter setter...
}
@Entity
public class AirLine implements Serializable {
@EmbeddedId
private AirLinePK id;
@Column(length=15)
private String airLineNum;
public AirLine(){}
@Override
public int hashCode() {
int hash = 0;
hash += (this.id != null ? this.id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof AirLine)) {
return false;
}
AirLine other = (AirLine)object;
if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) return false;
return true;
}
//省略getter setter...
}
复合主键映射还有另一种映射方式
public class AirLinePK implements Serializable {
@Column(length=3)
private String leavecity;
@Column(length=3)
private String arrivecity;
public AirLinePK(){}
public AirLinePK(String leavecity, String arrivecity) {
this.leavecity = leavecity;
this.arrivecity = arrivecity;
}
@Override
public int hashCode() {
int hash = 0;
hash += (this.leavecity!=null && this.arrivecity!=null ? (this.leavecity+ "-"+ this.arrivecity).hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof AirLinePK)) {
return false;
}
AirLinePK other = (AirLinePK)object;
if (this.leavecity != other.leavecity && (this.leavecity == null || !this.leavecity.equalsIgnoreCase(other.leavecity))) return false;
if (this.arrivecity != other.arrivecity && (this.arrivecity == null || !this.arrivecity.equalsIgnoreCase(other.arrivecity))) return false;
return true;
}
//省略getter setter
}
@Entity
@IdClass(AirLinePK.class)
public class AirLine implements Serializable {
@Id private String leavecity;
@Id private String arrivecity;
@Column(length=15) private String airLineNum;
public AirLine(){}
//省略getter setter
}
常见异常
1、异常信息:org.hibernate.hql.ast.QuerySyntaxException: person is not mapped
异常环境:查询
异常原因:查询语句中Person类没有大写
2、java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to java.lang.String
异常环境:查询、遍历显示
异常原因:转型出错
3、javax.persistence.NonUniqueResultException: result returns more than one elements
异常环境:查询、getSingleResult
异常原因:getSingleResult只能获取一条数据,而查询语句返回的是多条数据
4、 org.hibernate.PropertyValueException: not-null property references a null or transient value: com.sunyard.entities.Person.name
异常环境:数据插入
异常原因:JPA的Entity中一个属性定义为nullable=false,插入数据该字段为null
5、 执行添加没反应、没异常
异常原因:没有开启事务、没有提交事务
6、javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.sunyard.entities.Person
异常环境:OneToOne 共享主键关联
异常原因:一对一中,一个提供主键、另一个共享其主键,共享主键的对象可以set 提供主键的对象 然后添加到数据库中方向弄反了 后果就是没人提供主键
7、org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing:
异常环境:多对一添加
异常原因:在多的一端维护 ,没有添加级联
8、javax.persistence.PersistenceException: [PersistenceUnit: JPA] Unable to configure EntityManagerFactory
异常原因:很多、实体管理器Factory没有成功创建,是注解的问题
9、org.hibernate.MappingException: Unable to find column with logical name: sid in org.hibernate.mapping.
异常环境:添加表做多对一关联映射
异常原因:表字段写反了,name添加表字段名referencedColumnName指向本表字段名