JPA 一对多延迟加载与关系维护
原文章地址:http://hai0378.iteye.com/blog/1558397
- package cn.itcast.bean;
- import java.util.HashSet;
- import java.util.Set;
- import javax.persistence.CascadeType;
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.FetchType;
- import javax.persistence.GeneratedValue;
- import javax.persistence.Id;
- import javax.persistence.OneToMany;
- import javax.persistence.Table;
- @Entity
- @Table(name="orders") //把表名改成orders(默认表名是order),防止默认表名order与数据库的关键字"order by"中的order冲突。不改的话测试不成功,出现异常,orderitem表建立成功,order表建不了。
- public class Order {
- private String orderId;
- private Float amount = 0f;
- private Set<OrderItem> items = new HashSet<OrderItem>();
- @Id //要注意:目前JPA规范并没有提供UUID这种生成策略,目前主键值只提供了整型的生成方式,所以@GeneratedValue这个注解就不能在这里用上,不能对字符串进行id自增长。
- @Column(length = 12)
- public String getOrderId() {
- return orderId;
- }
- public void setOrderId(String orderId) {
- this.orderId = orderId;
- }
- @Column(nullable = false)
- public Float getAmount() {
- return amount;
- }
- public void setAmount(Float amount) {
- this.amount = amount;
- }
- @OneToMany(cascade = { CascadeType.REFRESH, CascadeType.PERSIST,
- CascadeType.MERGE, CascadeType.REMOVE },fetch=FetchType.LAZY,mappedBy="order")
- //mappedBy="order",中的order是关系维护端的order属性,这个order属性的类型是这个bean。
- public Set<OrderItem> getItems() {
- return items;
- }
- /*
- @OneToMany(fetch=FetchType.)的选项有,如下图:
- FetchType.EAGER:代表立即加载;
- FetchType.LAZY:代表延迟加载。
- 当我们把fetch设置为FetchType.LAZY的时候,什么时候初始化items里面的数据呢?当我们第一次访问这个属性,并对这个属性进行操作的时候,这个集合的数据才会从数据库里面load出来。但要注意:当我们访问这个延迟属性的时候,我们的前提要EntityManager这个对象没有被关闭,如果被关闭了我们再去访问延迟属性的话,就访问不到,并抛出延迟加载意外。
- 如果没有设置fetch这属性的话,会怎么样呢?是立即加载?还是延迟加载呢?
- 记住@OneToMany这个标签最后的英文单词,如果是要得到Many的一方,我不管你前面是什么,只要后面的单词是Many,也就是说要得到多的一方,你们就给我记住,默认的加载策略就是延迟加载(Many记录可能上几万条,立即加载的话可能对效率影响大,所以延迟加载)。
- 反过来,如果后面是One呢?因为它是加载一的一方,这对性能影响不是很大,所以它的默认加载策略是立即加载。
- mappedBy:我们怎么知道关系的维护端和被维护端呢?当然JPA规范规定多的一端应该是为维护端(关系维护段增加一个字段为外键,里面保存的是一的一端的主键),一的一端为关系被维护端,那么我们总要在程序里给他们打上标志吧?虽然规范是这么规定,但总要申明一下吧?就是通过mappedBy属性,只要哪个类出现了mappedBy,那么这个类就是关系的被维护端。里面的值指定的是关系维护端。
- orderItem这边由哪一个属性去维护关系呢?是OrderItem类的order属性。
- mappedBy属性对应Hibernate里面的inverse属性:<SET name="items" inverse="true"></SET>
- */
- public void setItems(Set<OrderItem> items) {
- this.items = items;
- }
- //用这个方法会方便很多
- public void addOrderItem(OrderItem orderItem){
- orderItem.setOrder(this); //关系维护方orderItem加入关系被维护方(this)后,才能维护更新关系(orderItem表中的外键字段order_id),维护关系其实就是更新外键。只有为关系维护端设置了关系被维护端,关系才能建立起来。
- this.items.add(orderItem);
- }
- }
OrderItem.java
- package cn.itcast.bean;
- import javax.persistence.CascadeType;
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.GeneratedValue;
- import javax.persistence.Id;
- import javax.persistence.JoinColumn;
- import javax.persistence.ManyToOne;
- @Entity
- public class OrderItem {
- private Integer id;
- private String productName;
- private Float sellPrice = 0f; //默认值为0。
- private Order order;
- @Id
- @GeneratedValue //id自增长方式生成主键。
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- @Column(length = 40, nullable = false)
- public String getProductName() {
- return productName;
- }
- public void setProductName(String productName) {
- this.productName = productName;
- }
- @Column(nullable = false)
- public Float getSellPrice() {
- return sellPrice;
- }
- public void setSellPrice(Float sellPrice) {
- this.sellPrice = sellPrice;
- }
- @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
- @JoinColumn(name="order_id") //设置外键的名称。
- public Order getOrder() { //OrderItem是关系维护端,负责关系更新,它是根据它的order属性值维护关系的。当它保存的时候(主动保存或是被级联保存),他会根据order属性的值更新关系,当order为null时,就不会更新关系了。级联操作也是根据双方对象中的映射属性值进行的,当映射属性没值的时候就不会对对方进行级联操作了。
- return order;
- }
- /*
- @ManyToOne的级联保存(CascadeType.PERSIST)是不需要的,不可能说你保存某个订单项OrderItem的时候,也保存订单Order的。通常都是保存订单Order的时候,保存订单项OrderItem的。
- CascadeType.MERGE:如果我们更新了订单项orderItem产品的价钱,那么整个订单Order的总金额是会发生改变的,所以可以定义这个级联更新。
- CascadeType.REFRESH:如果我们想得到目前数据库里orderItem最新的数据的话,我们也希望得到订单order的最新数据,我们可以定义这个级联刷新,就是说把数据库里最新的数据重新得到。
- CascadeType.REMOVE:这个属性这里肯定不设。就好比现在有一个订单,一个订单里面有3个购物项orderItem,买了A,B,C三个产品,我现在不要A这个产品了,我们只是把A这条记录删掉,那么如果这里定义了级联删除的话,那么你删除A记录的同时,也会把整个订单也删除掉,所以这里不需要设级联删除。
- */
- //optional:说明order这个是否是可选的?是否可以没有的?false表示必须的,true表示是可选的。
- public void setOrder(Order order) {
- this.order = order;
- }
- }
OneToManyTest.java
- package junit.test;
- import javax.persistence.EntityManager;
- import javax.persistence.EntityManagerFactory;
- import javax.persistence.Persistence;
- import org.junit.BeforeClass;
- import org.junit.Test;
- import cn.itcast.bean.Order;
- import cn.itcast.bean.OrderItem;
- public class OneToManyTest {
- @BeforeClass
- public static void setUpBeforeClass() throws Exception {
- }
- @Test
- public void save() {
- EntityManagerFactory factory = Persistence.createEntityManagerFactory("itcast");
- EntityManager em = factory.createEntityManager();
- em.getTransaction().begin();
- Order order = new Order();
- order.setAmount(34f);
- order.setOrderId("992"); //orderId是数据库里面的主键,同时也是实体的标识。这里并没有使用Id自增长的方式来生成主键值,而是自己设值,所以可以随便写。如果想用UUID,可以这样写UUID.randomUUID().toString();这个类JDK5提供了。
- OrderItem orderItem1 = new OrderItem();
- orderItem1.setProductName("排球");
- orderItem1.setSellPrice(90f);
- OrderItem orderItem2=new OrderItem();
- orderItem2.setProductName("篮球");
- orderItem2.setSellPrice(30f);
- order.addOrderItem(orderItem1);
- order.addOrderItem(orderItem2);
- em.persist(order);
- em.getTransaction().commit();
- em.close();
- factory.close();
- }
- }
运行junit测试,发现保存订单Order的时候,也保存了订单项OrderItem.为什么呢?是因为订单Order和订单项OrderItem定义 了级联保存(CascadeType.PERSIST)关系,这个级联关系在我们调用em.persist(order);的persist方法时就会起 作用。
E炎灵的含义--
奇异:非同凡响
火焰:生生不息
灵魂:人之精髓