【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】 ​

1 Hibernate 的关联映射

1-1 单项 N-1 关联

public class Person{    //标识属性    private Integer id;    //Person的name属性    private String name;    //保留Person的age属性    private int age;    //引用关联实体的属性    private Address address;    ....    //address属性的setter和getter方法    public void setAddress(Address address)    {        this.address = address;    }    public Address getAddress()    {        return this.address;    }}

Address.java

public class Address{    //标识属性    private Integer addressId;    //地址详细信息    private String addressDetail;    //无参数的构造器    public Address()    {    }    //初始化全部属性的构造器    public Address(String addressDetail)    {        this.addressDetail = addressDetail;    }    ...}

1-1-1 无连接表的 N-1 关联

Hibernate 使用 < many-to-one …/ > 元素映射 N-1 的关联实体。

< many-to-one …/ > 元素的可选属性:

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">                                                                                                            class="Address" column="address_id"/>

Address 无需访问 Person 端,所以 Address 类的映射文件无需改变。

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

1-1-2 有连接表的 N-1 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】
...                                                                                class="Address" column="address_id"/>

1-2 单项 1-1 关联

单向 1-1 只需要在 N-1 原有的 < many-to-one /> 元素增加 unique=”true” 属性即可。

1-2-1 基于外键的单向 1-1

...

1-2-2 有连接表的单向 1-1

...                                                                                unique="true" class="Address"                column="address_id"/>

1-2-3 基于主键的单向 1-1

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】
address

1-3 单项 1-N 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】
public class Person{    ...    //1-N关联关系,使用Set来保存关联实体    private Set addresses = new HashSet();    ...    //addresses属性的setter和getter方法    public void setAddresses(Set addresses)    {        this.addresses = addresses;    }    public Set getAddresses()    {        return

1-3-1 无连接表的单向 1-N

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】
...

1-3-2 有连接表的单向 1-N

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】
...

1-4 单项 N-N 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】
...

1-5 双项 1-N 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.java

public class Person{    ...    //1-N关联关系,使用Set来保存关联实体    private Set addresses = new HashSet();    ...    //addresses属性的setter和getter方法    public void setAddresses(Set addresses)    {        this.addresses = addresses;    }    public Set getAddresses()    {        return

Address.java

public class Address{    //标识属性    private int addressId;    //地址详细信息    private String addressDetail;    //记录关联实体的person属性    private Person person;    //无参数的构造器    public Address()    {    }    //初始化全部属性的构造器    public Address(String addressDetail)    {        this.addressDetail = addressDetail;    }    ...    //person属性的setter和getter方法    public void setPerson(Person person)    {        this.person = person;    }    public Person getPerson()    {        return this.person;    }}

1-5-1 无连接表的双向 1-N 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.hbm.xml

...

Address.hbm.xml

...                            column="person_id" not-null="true"/>

使用

private void testPerson(){    Session session = HibernateUtil.currentSession();    Transaction tx = session.beginTransaction();    //创建一个Person对象    Person p = new Person();    //设置Person的Name为Yeeku字符串    p.setName("Yeeku");    p.setAge(29);    //持久化Person对象(对应于插入主表记录)    session.save(p);    //创建一个瞬态的Address对象    Address a = new Address("广州天河");    //先设置Person和Address之间的关联关系    a.setPerson(p);    //再持久化Address对象(对应于插入从表记录)    session.persist(a);    //创建一个瞬态的Address对象    Address a2 = new Address("上海虹口");    //先设置Person和Address之间的关联关系    a2.setPerson(p);    //再持久化Address对象(对应于插入从表记录)

1-5-2 有连接表的双向 1-N 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.hbm.xml

...

Address.hbm.xml

...

1-6 双项 N-N 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.java

public class Person{    ...    //1-N关联关系,使用Set来保存关联实体    private Set addresses = new HashSet();    ...    //addresses属性的setter和getter方法    public void setAddresses(Set addresses)    {        this.addresses = addresses;    }    public Set getAddresses()    {        return

Address.java

public class Address{    //标识属性    private int addressId;    //地址详细信息    private String addressDetail;    //记录关联实体的person属性    private Set persons = new HashSet();    //无参数的构造器    public Address()    {    }    //初始化全部属性的构造器    public Address(String addressDetail)    {        this.addressDetail = addressDetail;    }    ...    //persons属性的setter和getter方法    public void setPersons(Set persons)    {        this.persons = persons;    }    public Set getPersons()    {        return this.persons;    }}

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.hbm.xml

...

Address.hbm.xml

...                                                                                column="person_id"/>

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

1-7 双项 1-1 关联

1-7-1 基于外键的双向 1-1 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.hbm.xml

...

Address.hbm.xml

...                            column="person_id" not-null="true"/>

两边的映射策略可以互换,但是不能两边都使用相同的元素映射关联属性。

1-7-2 基于主键的双向 1-1 关联

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.hbm.xml

...

Address.hbm.xml

person

1-7-3 有连接表的双向 1-1 关联(不推荐)

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.hbm.xml

...                                                                                column="address_id" unique="true"/>

Address.hbm.xml

...                                                                                column="person_id" unique="true"/>

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

1-8 组合属性包含的关联实体

Address 当作组件属性来处理。
Person.java

public class Person{    //标识属性    private Integer id;    //Person的name属性    private String name;    //保留Person的age属性    private int age;    //定义一个组件属性    private Address address;    ...    //address属性的setter和getter方法    public void setAddress(Address address)    {        this.address = address;    }    public Address getAddress()    {        return this.address;    }}

Address.java

public class Address{    //标识属性    private int addressId;    //地址详细信息    private String addressDetail;    //定义引用包含实体的属性    private Person person;    //定义保留关联实体的Set    private Set schools = new HashSet();    //无参数的构造器    public Address()    {    }    //初始化addressDetail属性的构造器    public Address(String addressDetail)    {        this.addressDetail = addressDetail;    }    ...    //schools属性的setter和getter方法    public void setSchools(Set schools)    {        this.schools = schools;    }    public Set getSchools()    {        return this.schools;    }}

School.java

public class School{    //定义该学校实体的id属性    private Integer id;    //定义该学校的name属性    private String name;    //无参数的构造器    public School()    {    }    //初始化全部属性的构造器    public School(String name)    {        this.name = name;    }    ...}

Person.hbm.xml

没有 Address.hbm.xml 文件

PersonManager.java

public class PersonManager{    public static void main(String[] args)    {        PersonManager mgr = new PersonManager();        mgr.testPerson();        HibernateUtil.sessionFactory.close();    }    //保存Person和Address对象    private void testPerson()    {        Session session = HibernateUtil.currentSession();        Transaction tx = session.beginTransaction();        //创建一个Person对象        Person p = new Person();        //设置Person的Name为Yeeku字符串        p.setName("Yeeku");        p.setAge(29);        session.save(p);        //创建一个Address对象        Address a = new Address("广州天河");        //设置Person对象的Address属性        p.setAddress(a);        //创建2个School对象        School s1 = new School("疯狂Java项目冲刺班");        School s2 = new School("疯狂Java训练营");        //保存2个School实体        session.save(s1);        session.save(s2);        //设置Address对象和两个School的关联关系

1-9 基于复合主键的关联关系(不推荐)

Person.java

public class Person implements java.io.Serializable    //定义first属性,作为标识属性的成员    private String first;    //定义last属性,作为标识属性的成员    private String last;    //普通属性age    private int age;    //记录关联实体    private Set addresses = new HashSet();    ...    //重写equals方法,根据first、last进行判断    public boolean equals(Object obj)    {        if (this == obj)        {            return true;        }        if (obj != null && obj.getClass() == Person.class)        {            Person target = (Person)obj;            if (target.getFirst().equals(getFirst())                && target.getLast().equals(getLast()))            {                return true;            }        }        return false;    }    //重写hashCode方法,根据first、last计算hashCode值    public int hashCode()    {        return getFirst().hashCode() * 13

Address.java

public class Address{    //标识属性    private int addressId;    //地址详细信息    private String addressDetail;    //记录关联实体的person属性    private Person person;    //无参数的构造器    public Address()    {    }    //初始化全部属性的构造器    public Address(String addressDetail)    {        this.addressDetail = addressDetail;    }    ...    //person属性的setter和getter方法    public void setPerson(Person person)    {        this.person = person;    }    public Person getPerson()    {        return this.person;    }}

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.hbm.xml

cascade="all">

Address.hbm.xml

not-null="true">

1-10 复合主键的成员属性为关联实体(不推荐)

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Order.java

public class Order{       //标识属性    private Integer orderId;    //订单日期    private Date orderDate;    //关联的的订单项    private Set items = new HashSet();    //无参数的构造器    public Order()    {    }    //初始化全部属性的构造器    public Order(Date orderDate)    {        this.orderDate = orderDate;    }    ...    //items属性的setter和getter方法    public void setItems(Set items)    {        this.items = items;    }    public Set getItems()    {        return this.items;    }}

Product.java

public class Product{    //标识属性    private Integer productId;    //产品名    private String name;    //无参数的构造器    public Product()    {    }    //初始化全部属性的构造器    public Product(String name)    {        this.name = name;    }    ...}

OrderItem.java

public class OrderItem implements java.io.Serializable    //下面3个属性将作为联合主键    //定义关联的Order实体    private Order order;    //定义关联的Product实体    private Product product;    //该订单项订购的产品数量    private int count;    //无参数的构造器    public OrderItem()    {    }    //初始化全部属性的构造器    public OrderItem(Order order , Product product , int count)    {        this.order = order;        this.product = product;        this.count = count;    }    ...}

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

OrderItem.hbm.xml

class="Product" column="product_id"                            class="Order" column="order_id"

1-11 持久化的传播性

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

2 继承映射

Person.java

public class Person{    //标识属性    private Integer id;    //定义该Person实体的名字属性    private String name;    //定义该Person实体的性别属性    private char gender;    //定义该Person实体的组件属性:address    private

Address.java

public class Address{    //定义该Address的详细信息    private String detail;    //定义该Address的邮编信息    private String zip;    //定义该Address的国家信息    private String country; //无参数的构造器    public Address()    {    }    //初始化全部属性的构造器    public Address(String detail , String zip , String country)    {        this.detail = detail;        this.zip = zip;        this.country = country;    }    ...}

Customer.java

//顾客类继承了Person类public class Customer extends Person    //顾客的评论信息    private String comments;    //和员工保持关联关系的属性    private Employee employee;    //无参数的构造器    public Customer()    {    }    //初始化comments属性的构造器    public Customer(String comments)    {        this.comments = comments;    }    ...}

Employee.java

//员工类继承了Person类public class Employee extends Person    //定义该员工的职位属性    private String title;    //定义该员工的工资属性    private double salary;    //和顾客保持关联关系的属性    private Set customers = new HashSet();    //和经理保持关联关系的属性    private Manager manager;    //无参数的构造器    public Employee()    {    }    //初始化全部属性的构造器    public Employee(String title , double salary)    {        this.title = title;        this.salary = salary;    }    ...}

Manager.java

//经理类继承员工类public class  Manager extends Employee    //定义经理管辖部门的属性    private String department;    //和员工保持关联关系的属性    private Set employees = new HashSet();    //无参数的构造器    public Manager()    {    }    //初始化全部属性的构造器    public Manager(String department)    {        this.department = department;    }    ...}

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

2-1 采用 subclass 元素的继承映射

  • 整个继承树的所有实例都保存在同一个表中。
    [En]

    all instances of the entire inheritance tree are saved in the same table.*

  • 表中需要增加一个辨别性列(discriminator)来区分每行记录归属于哪个类的实例。
  • 使用< subclass…/ >来映射子持久化类。
  • 需要在每个类别映射中指定鉴别器列的值。
    [En]

    the value of the discriminator column needs to be specified in each class map.*

  • 子类添加的属性不能有非空约束。
    [En]

    attributes added by subclasses cannot have non-empty constraints.*

Person.hbm.xml

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

2-2 采用 joined-subclass 元素的继承映射(纵向分割映射)

  • 父类实例保存在父类表中,子类实例通过父类表和子类表存储在一起。
    [En]

    the parent class instance is saved in the parent class table, and the subclass instance is stored together by the parent class table and the subclass table.*

  • 子类和父类共享的属性保存在父类表中,子类新增的元素保存在子类表中。
    [En]

    the attributes shared by the subclass and the parent class are saved in the parent class table, and the newly added elements of the subclass are saved in the subclass table.*

  • 无需使用鉴别器列。但是您需要使用元素来映射每个子类的公共主键–此主键列还引用父表的主键列。
    [En]

    No need to use the discriminator column. But you need to use elements to map the common primary key for each subclass-this primary key column also references the primary key column of the parent table.*

  • 子类添加的属性可以有非空约束。
    [En]

    attributes added by subclasses can have non-empty constraints.*

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

Person.hbm.xml

not-null="true"/>

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

2-3 采用 union-subclass 元素的继承映射(横向分割映射)

  • 子类实例的数据只保存在子类表中。
    [En]

    the data of the subclass instance is saved only in the subclass table.*

  • 既不需要辨别者列,也不需要< key…/ >元素来映射共有主键。
  • 子类添加的属性可以有非空约束。
    [En]

    attributes added by subclasses can have non-empty constraints.*

  • 比较简洁,推荐使用。

Person.hbm.xml

not-null="true"/>

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

3 Hibernate 的批量处理

User.java

public class User{    //定义标识属性    private int id;    //定义User实例的名称    private String name;    //定义User实例的年龄    private int age;    //定义User实例的国别    private String nationality;    //无参数的构造器    public User()    {    }    //初始化全部属性的构造器    public User(int id , String name , int age , String nationality)    {        this.id = id;        this.name = name;        this.age = age;        this.nationality = nationality;    }    ...}

HibernateUtil.java

public class HibernateUtil{    public static final SessionFactory sessionFactory;    static    {        try        {            //采用默认的hibernate.cfg.xml来启动一个Configuration的实例            Configuration configuration=new Configuration().configure();            //由Configuration的实例来创建一个SessionFactory实例            sessionFactory = configuration.buildSessionFactory();        }        catch (Throwable ex)        {            // Make sure you log the exception, as it might be swallowed            System.err.println("Initial SessionFactory creation failed." + ex);            throw new ExceptionInInitializerError(ex);        }    }    //ThreadLocal并不是线程本地化的实现,而是线程局部变量。也就是说每个使用该变量的线程都必须为    //该变量提供一个副本,每个线程改变该变量的值仅仅是改变该副本的值,而不会影响其他线程的该变量    //的值.    //ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要对线程同步        public static final ThreadLocal session = new ThreadLocal();    public static Session currentSession() throws HibernateException    {        Session s = (Session) session.get();        //如果该线程还没有Session,则创建一个新的Session        if (s == null)        {            s = sessionFactory.openSession();            //将获得的Session变量存储在ThreadLocal变量session里            session.set(s);        }        return s;    }    public static void closeSession() throws HibernateException     {        Session s = (Session) session.get();        if (s != null)        s.close();        session.set(null);    }}

3-1 批量插入

如果要将100000条记录插入数据库,Hibernate 通常会很低效。

一个简单的策略可以设置一个定时器,定时将 Session 缓存的数据刷入数据库,而不是一直在 Session 层缓存。
UserManager.java

public class UserManager    public static void main(String[] args)throws Exception    {        UserManager mgr = new UserManager();        mgr.testUser();        HibernateUtil.sessionFactory.close();    }    private void testUser()throws Exception    {        //打开Session        Session session = HibernateUtil.currentSession();        //开始事务        Transaction tx = session.beginTransaction();        //循环100000次,插入100000条记录        for (int i = 0 ; i < 100000 ; i++ )        {            //创建User实例            User u1 = new User();            u1.setName("xxxxx" + i);            u1.setAge(i);            u1.setNationality("china");            //在Session级别缓存User实例            session.save(u1);            //每当累加器是20的倍数时,将Session中数据刷入数据库,            //并清空Session缓存。            if (i % 20 == 0)            {                session.flush();                session.clear();            }        }        //提交事务        tx.commit();        //关闭事务

除了 Session 级别的缓存,还应关闭 SessionFactory 的二级缓存:

hibernate.cache.use_second_level_cache false

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

3-2 批量更新

UserManager.java

public class UserManager{    public static void main(String[] args)        throws Exception    {        UserManager mgr = new UserManager();        mgr.testUser();        HibernateUtil.sessionFactory.close();    }    private void testUser()throws Exception    {        //打开Session        Session session = HibernateUtil.currentSession();        //开始事务        Transaction tx = session.beginTransaction();        //查询出User表中的所有记录        ScrollableResults users = session.createQuery("from User")            .setCacheMode(CacheMode.IGNORE)            .scroll(ScrollMode.FORWARD_ONLY);        int count=0;        //遍历User表中的全部记录        while ( users.next() )        {            User u = (User) users.get(0);            u.setName("新用户名" + count);            //当count为20的倍数时,            //将更新的结果从Session中flush到数据库。            if ( ++count % 20 == 0

通常这种更新效果不是很好,效率不是很高。除了执行查询然后更新数据外,还会逐步更新每一行记录。

[En]

Usually this kind of update effect is not very good, the efficiency is not very high. In addition to executing the query and then updating the data, each row of records is also updated step by step.

3-3 DML 风格的批量更新/删除

批量 UPDATE 和 DELETE 语句的语法格式如下:

UPDATE | DELETE FROM?  [WHERE

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

3-3-1 HQL UPDATE

UserManager.java

public class UserManager    public static void main(String[] args)throws Exception    {        UserManager mgr = new UserManager();        mgr.testUser();        HibernateUtil.sessionFactory.close();    }private void testUser()throws Exception{    //打开Session    Session session = HibernateUtil.currentSession();    //开始事务    Transaction tx = session.beginTransaction();    //定义批量更新的HQL语句    String hqlUpdate = "update User set name = :newName";    //执行更新    int updatedEntities = session.createQuery( hqlUpdate )        .setString( "newName", "新名字" )        .executeUpdate();    //提交事务

3-3-2 HQL DELETE

UserManager.java

public class UserManager    public static void main(String[] args)throws Exception    {        UserManager mgr = new UserManager();        mgr.testUser();        HibernateUtil.sessionFactory.close();    }    private void testUser()throws Exception    {        //打开Session实例        Session session = HibernateUtil.currentSession();        //开始事务        Transaction tx = session.beginTransaction();        //定义批量删除的HQL语句        String hqlUpdate = "delete User";        //执行批量删除        int updatedEntities = session.createQuery(hqlUpdate)            .executeUpdate();        //提交事务        tx.commit();        //关闭Session

Original: https://blog.51cto.com/shanglianlm/5549209
Author: mingo_敏
Title: 【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/508283/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球