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

网友投稿 255 2022-11-15

【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 …/ > 元素的可选属性:

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

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

...

1-2 单项 1-1 关联

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

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

...

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

...

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

address

1-3 单项 1-N 关联

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

...

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

...

1-4 单项 N-N 关联

...

1-5 双项 1-N 关联

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 关联

Person.hbm.xml

...

Address.hbm.xml

...

使用

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 关联

Person.hbm.xml

...

Address.hbm.xml

...

1-6 双项 N-N 关联

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; }}

Person.hbm.xml

...

Address.hbm.xml

...

1-7 双项 1-1 关联

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

Person.hbm.xml

...

Address.hbm.xml

...

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

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

Person.hbm.xml

...

Address.hbm.xml

person

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

Person.hbm.xml

...

Address.hbm.xml

...

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; }}

Person.hbm.xml

Address.hbm.xml

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

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; } ...}

OrderItem.hbm.xml

1-11 持久化的传播性

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; } ...}

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

整个继承树的所有实例都保存在同一个表中。表中需要增加一个辨别性列(discriminator)来区分每行记录归属于哪个类的实例。使用< subclass…/ >来映射子持久化类。每个类映射中都需要指定辨别者列的值。子类增加的属性不可以有非空约束。

Person.hbm.xml

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

父类实例保存在父类表中,子类实例由父类表和子类表共同存储。子类和父类共有属性保存在父类表中,子类新增加的元素保存在子类表中。无需使用辨别者列。但需要为每个子类使用元素映射共同主键-这个主键列还将参照父类表的主键列。子类增加的属性可以有非空约束。

Person.hbm.xml

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

子类实例的数据仅保存在子类表中。既不需要辨别者列,也不需要< key…/ >元素来映射共有主键。子类增加的属性可以有非空约束。比较简洁,推荐使用。

Person.hbm.xml

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

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

通常这种更新效果不是很好,效率不是很高。除了先执行查询后更新数据,每行记录也是逐步更新的。

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

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

UPDATE | DELETE FROM? [WHERE

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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:MyBatis基础①
下一篇:Callable接口介绍 Runnable和Callable的区别
相关文章

 发表评论

暂时没有评论,来抢沙发吧~