Hibernate一对多、多对多、一对一关联映射方法
对象之间的关系:关系映射之间的关系只的是对象之间的关系,并不指数据库表的关系(外键关系),当然,数据库表该如何映射,编程上如何实现这两件事密不可分。
1、对象之间的关联关系:
一对多,一对一,多对多
在orm中,区分单向和双向;单向和双向直接影响数据加载不同。
如1:单向管理就是对应的Java对象中,ObjectA,ObjectB存在关联关系,但是只有ObjectA对象包含一个ObjectB对象的成员属性,而ObjectB仅仅是自己的属性,而没有引用ObjectA。
如2:一对多关联映射是指在加载一的一端数据的同时加载多的一端的数据;多对一关联映射是指在加载多的一端数据的同时加载一的一端的数据
2、一对一实现
a、【单向】一对一通过主键关联
@Entity
@Table(name = "a")
public class A implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@OneToOne
@PrimaryKeyJoinColumn
private B b;
}
@Entity @Table(name = "b") public class B implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; }
b、【单向】一对一通过外键关联
A表有B表的外键字段
@Entity
@Table(name = "a")
public class A implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@OneToOne
@JoinColumn(name="bid")
private B b;
}
@Entity @Table(name = "b") public class B implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer bid; }
c.[双向]一对一主键关联
@Entity
@Table(name = "a")
public class A implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@OneToOne
@PrimaryKeyJoinColumn(name="id",referencedColumnName="id")
private B b;
}
@Entity @Table(name = "b") public class B implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne(mappedBy="b") @PrimaryKeyJoinColumn(name="id",columnDefinition="id") private A a; }
d.[双向]一对一外键关联
实现形式之一:双向外键,即双方都持有对对方的引用;
@Entity
@Table(name = "a")
public class A implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@OneToOne
@JoinColumn(name="bid")
private B b;
}
@Entity @Table(name = "b") public class B implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne(mappedBy="b") @JoinColumn(name="id") private A a; }
实现形式之二:复合主键;
3、一对多关系实现:
a.[单向]一对多:
维护端在多的那一端,在一的一方加集合(Set)。如分组Group表与User表,此处定义为一对多,即一个分组里有多个用户。用户表中带有一个groupid外键。
@OneToMany
@JoinColumn(name="groupid")
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
b.【单向】多对一
@Entity
@Table(name="user")
public class User implements Serializable{
@ManyToOne
@JoinColumn(name="groupid")
private Group group;
}
c.[双向]一对多:
双向一对多关系,一是关系维护端(owner side),多是关系被维护端(inverse side)。在关系被维护端需要通过@JoinColumn建立外键列指向关系维护端的主键列。其中默认inverse=false,而mapedBy="xxxx"相当于inverse=true。
( 在一对多中,如果要一方维护关系,就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。
而如果让" 多"方面维护关系时就不会有update操作,因为关系就是在多方的对象中的,直指插入或是删除多方对象就行了。
当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管怎样说,还是让"多"方维护关系更直观一些。)
Order订单与OrberItem订单项关系为一对多映射
public class Order implements Serializable {
privateSet<OrderItem> orderItems = new HashSet<OrderItem>();
。。。。
@OneToMany(mappedBy="order"(有了mappedby不能也不该在此再定义@joincolumn),cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@OrderBy(value= "id ASC")
public Set<OrderItem> getOrderItems() {
returnorderItems;
}
}
//多的那方:
publicclass OrderItem implements Serializable {
private Orderorder;
。。。。
@ManyToOne(cascade=CascadeType.REFRESH,optional=false)
@JoinColumn(name = "order_id")
public Order getOrder() {
returnorder;
}
}
树结构实体实现
public class Category implements java.io.Serializable {
@Id
@GeneratedValue(strategy=IDENTITY)
@Column(name="CAT_ID", unique=true, nullable=false)
private Integer id;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="CAT_PARENT_ID")
private Category parent;
@Column(name="CAT_NAME", nullable=false, length=50)
private String name;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="parent")
private Set<Category> children = new HashSet<Category>(0);
//省略getter/setter
}
@OrderBy(value = "id ASC") 指明加载OrderItem 时按id的升序排序
@OneToMany的属性:
1>targetEntity
定义关系类的类型,默认是该成员属性对应的类类型,所以通常不需要提供定义。
2>mappedBy
定义类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要使用这个属性进行定义,否则可能引起数据一致性的问题。
该属性的值是“多”方class里的“一”方的变量名
3>cascade
该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。举个例子:Order 和OrderItem有级联关系,那么删除Order时将同时删除它所对应的OrderItem对象。而如果OrderItem还和其他的对象之间有级联关系,那么这样的操作会一直递归执行下去。
cascade的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表示选择全部四项。
4>fatch
可选择项包括:FetchType.EAGER和FetchType.LAZY。前者表示关系类(本例是OrderItem 类)在主类(本例是Order类)加载的时候同时加载,后者表示关系类在被访问时才加载。默认值是FetchType.LAZY。
@JoinColumn(name = "order_id")注释指定OrderItem映射表的order_id列作为外键与Order 映射表的主键列关联。
@ManyToOne:指明OrderItem和Order之间为多对一关系。
@ManyToOne注释有四个属性:targetEntity、cascade、fetch 和optional,前三个属性的具体含义和@OneToMany的同名属性相同,但@ManyToOne的fetch 属性默认值是FetchType.EAGER。
optional属性是定义该关联类是否必须存在,值为false 时,关联类双方都必须存在,如果关系被维护端不存在,查询的结果为null。值为true 时, 关系被维护端可以不存在,查询的结果仍然会返回关系维护端,在关系维护端中指向关系被维护端的属性为null。optional属性的默认值是true。optional属性实际上指定关联类与被关联类的join 查询关系,如optional=false 时join 查询关系为inner join,optional=true 时join 查询关系为leftjoin。下面代码片断解释如下:
3、延迟加载时,获取集合数据
有一点需要强调:当业务方法需要把一个实体Bean作为参数返回给客户端时,除了实体Bean本身需要实现Serializable 接口之外,如果关联类(OrderItem)是延迟加载,还需在返回实体Bean之前通过访问关联类的方式加载关联类(见下例)。否则在客户端访问关联类时将会抛出加载例外。
public OrdergetOrderByID(Integer orderid) {
Orderorder = em.find(Order.class, orderid);
//!!!!!因为是延迟加载,通过执行size()这种方式获取订单下的所有订单项
order.getOrderItems().size();
return order;
}
4、Queryquery 使用
另外不管是否延迟加载,通过join fetch 关联语句都可显式加载关联类,如下例:
public ListgetAllOrder() {
Queryquery = em.createQuery("select DISTINCT o from Order o inner
joinfetch o.orderItems order by o.orderid");
List result = query.getResultList();
return result;
}
注:Inverse和Cascade
Inverse:负责控制关系,默认为false,也就是关系的两端都能控制,但这样会造成一些问题,更新的时候会因为两端都控制关系,于是重复更新。一般来说有一端要设为true。
Cascade:负责控制关联对象的级联操作,包括更新、删除等,也就是说对一个对象进行更新、删除时,其它对象也受影响,比如我删除一个对象,那么跟它是多对一关系的对象也全部被删除。
举例说明区别:删除“一”那一端一个对象O的时候,如果“多”的那一端的Inverse设为true,则把“多”的那一端所有与O相关联的对象外键清空;
如果“多”的那一端的Cascade设为Delete,则把“多”的那一端所有与O相关联的对象全部删除。
转载于:https://my.oschina.net/guoshuyun/blog/725928
下一篇: Hibernate一对一关联映射