JPA(六):映射关联关系------映射单向一对多的关联关系

映射单向一对多的关联关系

新建项目项目请参考《JPA(二):HellWord工程》,基于上一章讲解的《JPA(五):映射关联关系------映射单向多对一的关联关系》中的例子进行修改(需要清空数据中的表,因为本例子还是使用customer,order表来测试,但是关联关系发生了变化):

Customer.java

package com.dx.jpa.singlemanytoone;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

@Entity
@Table(name = "jpa_customer")
public class Customer {
    private Integer id;
    private String fullName;
    private Integer age;
    private Date birth;
    private Date createDate;
    private Set<Order> orders = new HashSet<>();

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "FULL_NAME", length = 64, nullable = false)
    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Temporal(TemporalType.DATE)
    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Temporal(TemporalType.TIMESTAMP)
    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    // 映射一对多的关联关系
    // @JoinColumn 用来映射一对多的关联关系
    // @OneToMany 用来映射外键列
    @JoinColumn(name = "CUSTOMER_ID")
    @OneToMany()
    public Set<Order> getOrders() {
        return orders;
    }

    public void setOrders(Set<Order> orders) {
        this.orders = orders;
    }

    // 帮助方法,不希望保存到数据库,但是需要动态获取Customer对象的属性。
    @Transient
    public String getCustomerInfo() {
        return "username:" + fullName + ",age:" + age;
    }
}
View Code

注意:这里在Customer.java中添加了一对多的注解@OneToMany:

    // 映射一对多的关联关系
    // @JoinColumn 用来映射一对多的关联关系
    // @OneToMany 用来映射外键列
    @JoinColumn(name = "CUSTOMER_ID")
    @OneToMany()
    public Set<Order> getOrders() {
        return orders;
    }

Order.java

package com.dx.jpa.singlemanytoone;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "jpa_order")
public class Order {
    private Integer id;
    private String name;

    @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;
    }
}
View Code

初始化JPA项目时,创建表语句如下:

Hibernate: 
    
    create table hibernate_sequence (
       next_val bigint
    ) engine=InnoDB
Hibernate: 
    
    insert into hibernate_sequence values ( 1 )
Hibernate: 
    
    insert into hibernate_sequence values ( 1 )
Hibernate: 
    
    create table jpa_customer (
       id integer not null,
        age integer,
        birth date,
        createDate datetime,
        FULL_NAME varchar(64) not null,
        primary key (id)
    ) engine=InnoDB
Hibernate: 
    
    create table jpa_order (
       id integer not null,
        name varchar(255),
        CUSTOMER_ID integer,
        primary key (id)
    ) engine=InnoDB
Hibernate: 
    
    alter table jpa_order 
       add constraint FK7glkngwj74nr8h2amofkp1fjd 
       foreign key (CUSTOMER_ID) 
       references jpa_customer (id)
View Code

persistence.xml

<?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">
    <persistence-unit name="Jpa-helloword"
        transaction-type="RESOURCE_LOCAL">
        <!-- 配置使用什么 ORM 产品来作为 JPA 的实现 -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <!-- 添加持久化类 -->
        <class>com.dx.jpa.singlemanytoone.Customer</class>
        <class>com.dx.jpa.singlemanytoone.Order</class>
        <properties>
            <!-- 数据库的相关配置 -->
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/jpa" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="root" />
            <!-- 指定方言 
            MySQL                org.hibernate.dialect.MySQLDialect
            MySQL with InnoDB    org.hibernate.dialect.MySQLInnoDBDialect
            MySQL with MyISAM    org.hibernate.dialect.MySQLMyISAMDialect
            MySQL5                org.hibernate.dialect.MySQL5Dialect
            MySQL5 with InnoDB    org.hibernate.dialect.MySQL5InnoDBDialect
            -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true" />
            <!-- 
            create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。<br>
            create-drop :每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。<br>
            update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等 应用第一次运行起来后才会。<br>
            validate :每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 <br> 
            -->
            <property name="hibernate.hbm2ddl.auto" value="update" />            
        </properties>
    </persistence-unit>
</persistence>
View Code

此时在order表中创建了外键关联关系:

添加测试

添加测试函数:

    @Test
    public void testPersist() {
        Customer customer = new Customer();
        customer.setFullName("AA");
        customer.setAge(26);
        customer.setBirth(new Date());
        customer.setCreateDate(new Date());

        Order order1 = new Order();
        order1.setName("O-AA-01");

        Order order2 = new Order();
        order2.setName("O-AA-02");
        
        customer.getOrders().add(order1);
        customer.getOrders().add(order2);
        
        entityManager.persist(customer);
        entityManager.persist(order1);
        entityManager.persist(order2);
    }

此时执行sql如下:

Hibernate: 
    select
        next_val as id_val 
    from
        hibernate_sequence for update
            
Hibernate: 
    update
        hibernate_sequence 
    set
        next_val= ? 
    where
        next_val=?
Hibernate: 
    select
        next_val as id_val 
    from
        hibernate_sequence for update
            
Hibernate: 
    update
        hibernate_sequence 
    set
        next_val= ? 
    where
        next_val=?
Hibernate: 
    select
        next_val as id_val 
    from
        hibernate_sequence for update
            
Hibernate: 
    update
        hibernate_sequence 
    set
        next_val= ? 
    where
        next_val=?
Hibernate: 
    insert 
    into
        jpa_customer
        (age, birth, createDate, FULL_NAME, id) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        jpa_order
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        jpa_order
        (name, id) 
    values
        (?, ?)
Hibernate: 
    update
        jpa_order 
    set
        CUSTOMER_ID=? 
    where
        id=?
Hibernate: 
    update
        jpa_order 
    set
        CUSTOMER_ID=? 
    where
        id=?
View Code

此时并不是因为customer,order保存顺序导致的(即使调换添加先后顺序也会多处update语句),因为多的一端在插入时不会插入外键列,因此一定会多处update语句。

查询测试

添加测试函数:

    @Test
    public void testFind() {
        Customer customer = entityManager.find(Customer.class, 1);
        System.out.println(customer.getFullName());
        System.out.println(customer.getOrders().size());
    }
View Code

执行打印结果:

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.age as age2_0_0_,
        customer0_.birth as birth3_0_0_,
        customer0_.createDate as createDa4_0_0_,
        customer0_.FULL_NAME as FULL_NAM5_0_0_ 
    from
        jpa_customer customer0_ 
    where
        customer0_.id=?
AA
Hibernate: 
    select
        orders0_.CUSTOMER_ID as CUSTOMER3_1_0_,
        orders0_.id as id1_1_0_,
        orders0_.id as id1_1_1_,
        orders0_.name as name2_1_1_ 
    from
        jpa_order orders0_ 
    where
        orders0_.CUSTOMER_ID=?
2

从打印结果上可以看出默认采用懒加载的方式,修改Customer.java中的@OneToMany()中的fetch属性为:@OneToMany(fetch=FetchType.EAGER),此时才会出现非懒加载:

此时,再次执行插叙测试函数,执行打印结果如下:

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.age as age2_0_0_,
        customer0_.birth as birth3_0_0_,
        customer0_.createDate as createDa4_0_0_,
        customer0_.FULL_NAME as FULL_NAM5_0_0_,
        orders1_.CUSTOMER_ID as CUSTOMER3_1_1_,
        orders1_.id as id1_1_1_,
        orders1_.id as id1_1_2_,
        orders1_.name as name2_1_2_ 
    from
        jpa_customer customer0_ 
    left outer join
        jpa_order orders1_ 
            on customer0_.id=orders1_.CUSTOMER_ID 
    where
        customer0_.id=?
AA
2

修改测试

修改测试函数:

    @Test
    public void testUpdate() {
        Customer customer = entityManager.find(Customer.class, 1);
        customer.getOrders().iterator().next().setName("O-XX-01");
    }

此时执行打印结果为:

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.age as age2_0_0_,
        customer0_.birth as birth3_0_0_,
        customer0_.createDate as createDa4_0_0_,
        customer0_.FULL_NAME as FULL_NAM5_0_0_,
        orders1_.CUSTOMER_ID as CUSTOMER3_1_1_,
        orders1_.id as id1_1_1_,
        orders1_.id as id1_1_2_,
        orders1_.name as name2_1_2_ 
    from
        jpa_customer customer0_ 
    left outer join
        jpa_order orders1_ 
            on customer0_.id=orders1_.CUSTOMER_ID 
    where
        customer0_.id=?
Hibernate: 
    update
        jpa_order 
    set
        name=? 
    where

注意:这时@OneToMany(fetch=FetchType.EAGER)

删除测试

删除测试函数:

    @Test
    public void testRemove() {        
         Customer customer = entityManager.find(Customer.class, 4);
         entityManager.remove(customer);
    }

此时customer表内容记录如下:

order表记录如下:

此时执行删除,可以删除成功,删除后customer.id=4的记录被删除了,而order表中id=5,6记录的customer_id值被置为null。

删除后结果为:

customer表记录:

order表记录:

从执行打印语句可以看出:

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.age as age2_0_0_,
        customer0_.birth as birth3_0_0_,
        customer0_.createDate as createDa4_0_0_,
        customer0_.FULL_NAME as FULL_NAM5_0_0_,
        orders1_.CUSTOMER_ID as CUSTOMER3_1_1_,
        orders1_.id as id1_1_1_,
        orders1_.id as id1_1_2_,
        orders1_.name as name2_1_2_ 
    from
        jpa_customer customer0_ 
    left outer join
        jpa_order orders1_ 
            on customer0_.id=orders1_.CUSTOMER_ID 
    where
        customer0_.id=?
Hibernate: 
    update
        jpa_order 
    set
        CUSTOMER_ID=null 
    where
        CUSTOMER_ID=?
Hibernate: 
    delete 
    from
        jpa_customer 
    where
        id=?

 实际上,我们可以通过配置@OneToMany的级联删除属性,可以通过删除customer来实现级联删除的。

修改Customer.java中的@OneToMany注解信息:

这里修改配置后Customer的getOrders()方法的注解为:

    // 映射一对多的关联关系
    // @JoinColumn 用来映射一对多的关联关系
    // @OneToMany 用来映射外键列
    @JoinColumn(name = "CUSTOMER_ID")
    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.REMOVE)
    public Set<Order> getOrders() {
        return orders;
    }

此时,测试通过删除customer.id=1的记录,测试结果可以成功级联删除,执行打印结果为:

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.age as age2_0_0_,
        customer0_.birth as birth3_0_0_,
        customer0_.createDate as createDa4_0_0_,
        customer0_.FULL_NAME as FULL_NAM5_0_0_,
        orders1_.CUSTOMER_ID as CUSTOMER3_1_1_,
        orders1_.id as id1_1_1_,
        orders1_.id as id1_1_2_,
        orders1_.name as name2_1_2_ 
    from
        jpa_customer customer0_ 
    left outer join
        jpa_order orders1_ 
            on customer0_.id=orders1_.CUSTOMER_ID 
    where
        customer0_.id=?
Hibernate: 
    update
        jpa_order 
    set
        CUSTOMER_ID=null 
    where
        CUSTOMER_ID=?
Hibernate: 
    delete 
    from
        jpa_order 
    where
        id=?
Hibernate: 
    delete 
    from
        jpa_order 
    where
        id=?
Hibernate: 
    delete 
    from
        jpa_customer 
    where
        id=?

 

posted @ 2018-06-25 20:59  cctext  阅读(594)  评论(0编辑  收藏  举报