zoukankan      html  css  js  c++  java
  • JPA学习---第九节:JPA中的一对多双向关联与级联操作

    一、一对多双向关联与级联操作

    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&amp;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&amp;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();
        }
    }
  • 相关阅读:
    十二、 Spring Boot 静态资源处理
    九、 Spring Boot 拦截器
    docker之搭建私有仓库
    docker之Dokcerfile 常用指令
    docker之网络管理
    docker之故障问题解决方案
    docker之搭建LNMP
    docker之容器数据持久化
    都说岁月不饶人,我们又何曾饶过岁月
    docker之容器管理
  • 原文地址:https://www.cnblogs.com/hwlsniper/p/4088216.html
Copyright © 2011-2022 走看看