一、一对多双向关联与级联操作
1、创建项目,配置文件代码如下:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <!-- name属性用于定义持久化单元的名字 (name必选,空值也合法); transaction-type 指定事务类型(可选) --> <persistence-unit name="learn_jpa" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <!-- hibernate.dialect 指定数据库的方言 --> <!-- <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/> <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver"/> <property name="hibernate.connection.username" value="learn_orcl"/> <property name="hibernate.connection.password" value="learn_orcl"/> <property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521:learn_data?useUnicode=true&characterEncoding=UTF-8"/> <property name="hibernate.hbm2ddl.auto" value="update"/> --> <!-- hibernate.hbm2ddl.auto参数的作用主要用于:自动创建|更新|验证数据库表结构 --> <!-- create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表, 哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。 create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。 update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库), 以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。 要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等 应用第一次运行起来后才会。 validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表, 但是会插入新值。 --> <properties> <!-- 数据库方言 --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" /> <!-- 数据库驱动 --> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> <!-- 数据库用户名 --> <property name="hibernate.connection.username" value="root" /> <!-- 数据库密码 --> <property name="hibernate.connection.password" value="123456" /> <!-- 数据库连接URL --> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/learn_jpa?useUnicode=true&characterEncoding=UTF8"/> <!-- 最大抓取深度 --> <property name="hibernate.max_fetch_depth" value="3" /> <!-- 更新方式创建库表 --> <property name="hibernate.hbm2ddl.auto" value="update" /> <!-- 显示SQL --> <property name="hibernate.show_sql" value="false" /> <!-- 格式SQL --> <property name="hibernate.format_sql" value="true" /> </properties> </persistence-unit> </persistence>
2、创建订单实体类,代码如下:
package learn.jpa.entity; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; /** * 订单 */ @Entity // 定义类为实体类 public class Order { private String orderid; private float amount = 0f; private Set<OrderItem> item = new HashSet<OrderItem>(); @Id // 实体标识符,因为是字符串类型,所有不能用 @GeneratedValue,只能人为的赋值 @Column(length=20) 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}) public Set<OrderItem> getItem() { return item; } public void setItem(Set<OrderItem> item) { this.item = item; } } /** * 1 - N * 多的一端为关系维护端,关系维护端负责外键记录的更新 * */
3、创建订单项实体类,代码如下:
package learn.jpa.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; /** * 订单项 */ @Entity // 定义类为实体类 public class OrderItem { private int id; private String productName; private float sellPrice = 0f; private Order order; @Id // 实体标识符 @GeneratedValue // 主键自动增长 public int getId() { return id; } public void setId(int 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; } public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
注解:
1、@OneToMany(fetch=FetchType,cascade=CascadeType)
@OneToMany描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段.
fetch:表示该属性的读取策略,有EAGER和LAZY两种,分别表示主支抓取和延迟加载,默认为EAGER.
cascade:表示级联操作策略,对于OneToMany类型的关联非常重要,通常该实体更新或删除时,其关联的实体也应当被更新或删除
(1)、CascadeType.MERGE级联更新:若items属性修改了那么order对象保存时同时修改items里的对象。对应EntityManager的merge方法
(2)、CascadeType.PERSIST级联刷新:获取order对象里也同时也重新获取最新的items时的对象。对应EntityManager的refresh(object)方法有效。即会重新查询数据库里的最新数据
(3)、CascadeType.REFRESH级联保存:对order对象保存时也对items里的对象也会保存。对应EntityManager的presist方法
(4)、CascadeType.REMOVE级联删除:对order对象删除也对items里的对象也会删除。对应EntityManager的remove方法
CascadeType.PERSIST只有A类新增时,会级联B对象新增。若B对象在数据库存(跟新)在则抛异常(让B变为持久态)
CascadeType.MERGE指A类新增或者变化,会级联B对象(新增或者变化)
CascadeType.REMOVE只有A类删除时,会级联删除B类;
CascadeType.ALL包含所有;
综上:大多数情况用CascadeType.MERGE就能达到级联跟新又不报错,用CascadeType.ALL时要斟酌下CascadeType.REMOVE
optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true
二、JPA中的一对多延迟加载与关系维护
1、订单实体类,代码:
package learn.jpa.entity; 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.Id; import javax.persistence.OneToMany; import javax.persistence.Table; /** * 订单 */ @Entity // 定义类为实体类 @Table(name="orders") public class Order { private String orderid; private float amount = 0f; private Set<OrderItem> item = new HashSet<OrderItem>(); @Id // 实体标识符,因为是字符串类型,所有不能用 @GeneratedValue,只能人为的赋值 @Column(length=20) 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; } /** * 如果是一对多或多对多 fetch 默认是延迟加载,反之是立即加载 * mappedBy="order" 表示由实体 OrderItem 中的 order 属性维护 * @return */ @OneToMany(cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REMOVE}, fetch=FetchType.LAZY,mappedBy="order") public Set<OrderItem> getItem() { return item; } public void setItem(Set<OrderItem> item) { this.item = item; } public void addOrderItem(OrderItem orderItem){ orderItem.setOrder(this); this.item.add(orderItem); } } /** * 1 - N * 多的一端为关系维护端,关系维护端负责外键记录的更新 * */
mappedBy只有在双向关联时,才会使用这个属性
mappedBy=”另一方的关系引用属性”
2、订单项实体类,代码:
package learn.jpa.entity; 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.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; /** * 订单项 */ @Entity // 定义类为实体类 public class OrderItem { private int id; private String productName; private float sellPrice = 0f; private Order order; @Id // 实体标识符 @GeneratedValue // 主键自动增长 public int getId() { return id; } public void setId(int 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},fetch=FetchType.EAGER,optional=false) @JoinColumn(name="order_id") public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
joinColumns属性表示,在保存关系中的表中,所保存关联关系的外键的字段。并配合@JoinColumn标记使用。
3、测试保存,代码如下:
package learn.jpa.test; import static org.junit.Assert.*; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import learn.jpa.entity.Order; import learn.jpa.entity.OrderItem; import org.junit.Test; public class OneToManyTest { /** * 测试数据库是否可以生成表 */ @Test public void test() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("learn_jpa"); factory.close(); } @Test public void save(){ EntityManagerFactory factory = Persistence.createEntityManagerFactory("learn_jpa"); EntityManager em = factory.createEntityManager(); em.getTransaction().begin(); // 开启事务 Order order = new Order(); order.setAmount(56f); order.setOrderid("SE001"); OrderItem item1 = new OrderItem(); item1.setProductName("足球"); item1.setSellPrice(32f); OrderItem item2 = new OrderItem(); item2.setProductName("羽毛球"); item2.setSellPrice(24f); order.addOrderItem(item1); order.addOrderItem(item2); em.persist(order); em.getTransaction().commit(); em.close(); factory.close(); } }