Hibernate级联设计
数据库表之间的关系(主要关系有一对多、一对一、多对多)主要是从如下三个方面体现出来:
1.表体设计
2.实体类的设计
3.配置文件
以下是一些重要级联设计参数属性介绍:
cascade级联操作,默认值为 none
1.none:只做级联查询
2.save-update:级联查询,级联插入和级联更新
3.delete:级联查询,级联删除
4.all:级联查询,级联插入,级联更新和级联删除
5.all-delete-orphan:基础主从表记录关系时,会把从表对应的记录一并删除
inverse:维护两张表之间的关系,默认值是 false,即维护从表的外键值
1.ture:不维护从表外键值
2.false:维护从表外键值
3.在 hibernate 中通过对 inverse 属性的值决定是由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系
4. 在没有设置 inverse=true 的情况下,父子两边都维护父子关系
5.在 1-n 关系中,将 n 方设为主控方将有助于性能改善
6.在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句。
注意:cascade 和 inverse 优先考虑cascade操作,然后再考虑 inverse 操作!
lazy:是否延迟加载从表取出来的数据
1.true 延迟加载
2.flase 不延迟加载
一对多关联关系映射(表的设计原则是:在多的一方添加外键链来描述彼此之间的关系)
POJO类的设计:
1.设计 OrderItem 类,它代表为“1”的一方:
public class OrderItem { private int id; private int amount; private String productName; private UserOrder userOrder; public int getId() {return id;} public void setId(int id) {this.id = id;} public int getAmount() {return amount;} public void setAmount(int amount) {this.amount = amount;} public String getProductName() {return productName;} public void setProductName(String productName) {this.productName = productName;} public UserOrder getUserOrder() {return userOrder;} public void setUserOrder(UserOrder userOrder) {this.userOrder = userOrder;} }
2.设计 UserOrder 类,它代表为"n"的一方:
import java.util.HashSet; import java.util.Set; public class UserOrder { private int id; private int userId; private int status; private double cost; /** * 存放相关联的OrderItem对象 */ private Set<OrderItem> orderItems = new HashSet<OrderItem>(); //从面向对象的角度来看,在“多”的一方使用一个集合用来保存另一个相关联的对象的信息。 public int getId() {return id;} public void setId(int id) {this.id = id;} public int getUserId() {return userId;} public void setUserId(int userId) {this.userId = userId;} public int getStatus() {return status;} public void setStatus(int status) {this.status = status;} public double getCost() {return cost;} public void setCost(double cost) {this.cost = cost;} public Set<OrderItem> getOrderItems() { return orderItems; } public void setOrderItems(Set<OrderItem> orderItems) { this.orderItems = orderItems; } @Override public String toString() { return "UserOrder [id=" + id + ", userId=" + userId + ", status=" + status + ", cost=" + cost + "]"; } }
3.建立 OrderItem 的 Hibernate 相关映射配置文件:orderitem.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="net.togogo.onetomany.pojo.OrderItem" table="order_item" > <id name="id" type="int"> <column name="id" /> <generator class="native"></generator> </id> <property name="amount" type="int"> <column name="amount" length="11" /> </property> <property name="productName" type="string"> <column name="productName" length="30" /> </property> <many-to-one name="userOrder" class="net.togogo.onetomany.pojo.UserOrder" cascade="all" column="order_id"> </many-to-one> </class> </hibernate-mapping>
4.建立 UserOrder 的 Hibernate 相关映射配置文件:userorder.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- POJO 数据库中的表对应的实体类 --> <class name="net.togogo.onetomany.pojo.UserOrder" table="user_order" > <id name="id" type="int"> <column name="id" /> <generator class="native"></generator> </id> <property name="userId" type="int"> <column name="user_id" length="11" /> </property> <property name="status" type="int"> <column name="status" length="11" /> </property> <property name="cost" type="double"> <column name="cost" /> </property> <!-- many-to-one属性说明: * name:设定待映射的持久化类的名字。 name="orderItems",对应UserOrder中的setOrderItems(Set<OrderItem> orderItems)方法 column:设定和持久化类的属性对应的表的外键。 class:设定持久化类的属性的类型。 not-null:是否允许为空。 cascade:级联操作,默认值是none,常用的值: 1)none:只做级联查询 2)save-update:级联查询,级联插入和级联更新 3)delete:级联查询,级联删除 4)all:级联查询,级联插入,级联删除,级联更新 5)all-delete-orphan:解除主从表记录关系时,会把从表对应的记录一并删除 inverse:维护两张表之间的关系,默认是false,即维护从表的外键值 1)true:不维护从表外键值 2) false:维护从表外键值 cascade和inverse:优先考虑cascade操作,然后再考虑inverse lazy:是否延迟加载从表的数据,true,延迟加载,false,立即加载 --> <set name="orderItems" cascade="all-delete-orphan" inverse="false" lazy="true"> <!-- 从表的外键字段 --> <key column="order_id"></key> <one-to-many class="net.togogo.onetomany.pojo.OrderItem"/> </set> </class> </hibernate-mapping>
5.在 hibernate.cfg.xml 配置文件加入 userorder.hbm.xml 和 orderitem.hbm.xml 相关信息:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库连接信息 --> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- 所使用的数据库名 --> <property name="hibernate.connection.url"> jdbc:mysql://localhost:3306/hibernate_db </property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 设置连接池大小 --> <property name="hibernate.connection.pool_size">10</property> <!-- 实质是调用 Statement.setFetchSize() 方法设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。 例如一次查询1万条记录,对于Oracle的JDBC驱动来说,是不会 1 次性把1万条取出来的,而只会取出Fetch Size条数, 当纪录集遍历完了这些记录以后,再去数据库取Fetch Size条数据。因此大大节省了无谓的内存消耗。 Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。 Oracle数据库的JDBC驱动默认的Fetch Size=10,是一个保守的设定,根据测试,当Fetch Size=50时,性能会提升1倍之多, 当Fetch Size=100,性能还能继续提升20%,Fetch Size继续增大,性能提升的就不显著了。 并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持。MySQL就像上面那种最坏的情况,总是一下就把1万条记录完全取出来, 内存消耗会非常惊人! --> <property name="hibernate.jdbc.fetch_size">50</property> <!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,类似于设置缓冲区大小的意思。Batch Size越大, 批量操作的向数据库发送sql的次数越少,速度就越快。测试结果是当Batch Size=0的时候,使用Hibernate对Oracle 数据库删除1万条记录需要25秒,Batch Size = 50的时候,删除仅仅需要5秒! Oracle数据库 Batch Size = 30 的时候比较合适 --> <property name="hibernate.jdbc.batch_size">50</property> <!-- 显示底层的sql语句,开发阶段设为true,项目发布阶段设为false --> <property name="show_sql">true</property> <property name="format_sql">false</property> <property name="use_sql_comments">false</property> <!-- 在启动和停止时自动地创建,更新或删除数据库模式。取值 create | update | create-drop | validate, 推荐使用update,update值表示如果之前有表,就用之前的表,否则创建新的表 --> <property name="hbm2ddl.auto">update</property> <mapping resource="net/togogo/onetomany/pojo/userorder.hbm.xml"/> <mapping resource="net/togogo/onetomany/pojo/orderitem.hbm.xml"/> </session-factory> </hibernate-configuration>
一对一级联关系映射(表的设计原则是:存在主从关系,主可以没有从,但从不能没有主。在从表中将主键链(默认拥有唯一约束和非空约束)声明成外键约束即可。---------也就是说,有一列既是主键列又是外键链。)
POJO类的设计
1.设计 Boy 类,它代表为"1"的一方:
public class Boy { private int id; private String name; private Girl girl; public int getId() { return id;} public void setId(int id) { this.id = id;} public String getName() {return name; } public void setName(String name) { this.name = name;} public Girl getGirl() {return girl;} public void setGirl(Girl girl) {this.girl = girl;} }
2.设计 Girl 类,它代表为"1"的另一方:
public class Girl { private int id; private String name; private Boy boy; public int getId() {return id;} public void setId(int id) {this.id = id;} public String getName() {return name;} public void setName(String name) {this.name = name;} public Boy getBoy() {return boy;} public void setBoy(Boy boy) {this.boy = boy;} }
3.建立 Boy 的 Hibernate 相关映射配置文件:boy.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Mapping file autogenerated by MyEclipse Persistence Tools --> <hibernate-mapping> <class name="net.togogo.onetoone.pojo.Boy" table="boy"> <id name="id" type="int"> <column name="id" /> <generator class="native"></generator> </id> <property name="name" type="string"> <column name="name" length="30" /> </property> <many-to-one name="girl" cascade="all" lazy="false" class="net.togogo.onetoone.pojo.Girl" unique="true" column="g_id"></many-to-one> </class> </hibernate-mapping>
4.建立 Girl 的 Hibernate 相关映射配置文件:girl.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Mapping file autogenerated by MyEclipse Persistence Tools --> <hibernate-mapping> <class name="net.togogo.onetoone.pojo.Girl" table="girl"> <id name="id" type="int"> <column name="id" /> <generator class="native"></generator> </id> <property name="name" type="string"> <column name="name" length="30" /> </property> <one-to-one name="boy" cascade="all" lazy="false" class="net.togogo.onetoone.pojo.Boy"> </one-to-one> </class> </hibernate-mapping>
5.在 hibernate.cfg.xml 配置文件加入 boy.hbm.xml 和 girl.hbm.xml 相关信息:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库连接信息 --> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="hibernate.connection.url"> jdbc:mysql://localhost:3306/hibernate_db </property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 设置连接池大小 --> <property name="hibernate.connection.pool_size">10</property> <property name="hibernate.jdbc.fetch_size">50</property> <property name="hibernate.jdbc.batch_size">50</property> <property name="show_sql">true</property> <property name="format_sql">false</property> <property name="use_sql_comments">false</property> <property name="hbm2ddl.auto">update</property> <mapping resource="net/togogo/onetoone/pojo/girl.hbm.xml"/> <mapping resource="net/togogo/onetoone/pojo/boy.hbm.xml"/> </session-factory> </hibernate-configuration>
多对多级联关系映射(数据库表的设计原则:增加一个表用来保存两个表之间的关系)
POJO类的设计:
1.设计 Couse 类,它代表为"多"的一方:
public class Course { private int id; private String name; public int getId() {return id;} public void setId(int id) {this.id = id; } public String getName() {return name;} public void setName(String name) {this.name = name;} }
2.设计 Stu 类,它代表为"多"的另一方:
public class Stu { private int id; private String name; private Set<Course> courses = new HashSet<Course>(); public int getId() {return id;} public void setId(int id) {this.id = id;} public String getName() {return name;} public void setName(String name) {this.name = name;} public Set<Course> getCourses() {return courses;} public void setCourses(Set<Course> courses) {this.courses = courses;} }
3.建立 Course 的 Hibernate 相关映射配置文件:course.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="net.togogo.manytomany.pojo.Course" table="course"> <id name="id" type="int"> <column name="id" /> <generator class="native"></generator> </id> <property name="name" type="string"> <column name="name" length="30" /> </property> </class> </hibernate-mapping>
4.建立 Stu 的 Hibernate 相关映射配置文件:stu.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Mapping file autogenerated by MyEclipse Persistence Tools --> <hibernate-mapping> <class name="net.togogo.manytomany.pojo.Stu" table="stu"> <id name="id" type="int"> <column name="id" /> <generator class="native"></generator> </id> <property name="name" type="string"> <column name="name" length="30" /> </property> <!-- inverse="false",在这里是要维护stu_course中的记录值 --> <set name="courses" cascade="all" inverse="false" lazy="false" table="stu_course"> <key column="s_id"/> <many-to-many column="c_id" class="net.togogo.manytomany.pojo.Course"></many-to-many> </set> </class> </hibernate-mapping>
5.在 hibernate.cfg.xml 配置文件加入 stu.hbm.xml 和 course.hbm.xml 相关信息:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库连接信息 --> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="hibernate.connection.url"> jdbc:mysql://localhost:3306/hibernate_db </property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 设置连接池大小 --> <property name="hibernate.jdbc.fetch_size">50</property> <property name="hibernate.jdbc.batch_size">50</property> <property name="show_sql">true</property> <property name="format_sql">false</property> <property name="use_sql_comments">false</property> <property name="hbm2ddl.auto">update</property> <mapping resource="net/togogo/manytomany/pojo/stu.hbm.xml"/> <mapping resource="net/togogo/manytomany/pojo/course.hbm.xml"/> </session-factory> </hibernate-configuration>
对象的状态转化
Hibernate 把对象分为 4 种状态:
1.临时状态
2.持久状态
3.游离状态
4.删除状态
Session 的特定方法能使对象从一个状态转换到另一个状态
临时状态(transient)
1.在使用代理主键的情况下, OID 通常为 null
2.不处于 Session 的缓存中
3.在数据库中没有对应的记录
持久化状态(托管状态)(Persist)
1.OID 不为 null
2.位于 Session 缓存中
3.持久化对象和数据库中的相关记录对应
4.Session 在清理缓存时, 会根据持久化对象的属性变化, 来同步更新数据库
5.在同一个 Session 实例的缓存中, 数据库表中的每条记录只对应唯一的持久化对象
游离状态(Detached)
1.OID 不为 null
2.不再处于 Session 的缓存中
3.一般情况需下, 游离对象是由持久化对象转变过来的, 因此在数据库中可能还存在与它对应的记录
删除状态(Removed)
1.OID 不为 null
2.从一个 Session实例的缓存中删除
3.Session 已经计划将其从数据库删除, Session 在清理缓存时, 会执行 SQL delete 语句, 删除数据库中的对应记录
4.一般情况下, 应用程序不该再使用被删除的对象
对象关系状态转化图如下:
//end