理解Hibernate的inverse和cascade属性(实例)
1.理解cascade属性
创建CUSTOMERS和ORDERS数据库表
CREATE TABLE `hiber`.`customers` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`NAME` varchar(45) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM;
CREATE TABLE `hiber`.`orders` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`ORDER_NO` varchar(45) DEFAULT NULL,
`CUSTOMER_ID` bigint(20) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM;
创建持久化对象
Customer类:
package com;
import java.io.Serializable;
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
private long id;
private String name;
public long getId() {
return id;
}
private void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Customer类的配置文件(Customer.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="com.Customer" table="CUSTOMERS">
<id name="id" column="ID" type="long">
<generator column="NAME" type="string"/>
</class>
</hibernate-mapping>
Order类:
package com;
import java.io.Serializable;
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
private long id;
private String orderNo;
private Customer customer;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
Order类的配置文件(Order.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 package="com">
<class name="Order" table="ORDERS">
<id name="id" column="ID" type="long">
<generator column="ORDER_NO" type="string" not-null="true"/>
<many-to-one name="customer" column="CUSTOMER_ID" />
</class>
</hibernate-mapping>
创建一个工具类
package tool;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.Customer;
import com.Order;
public class HibernateUtil {
private static SessionFactory sessionFactory;
static {
Configuration config = new Configuration();
//适合Java属性配置连接数据库
config.addClass(Customer.class)
.addClass(Order.class);
//适合XML配置连接数据库
//config.configure();
sessionFactory = config.buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession() {
return sessionFactory.openSession();
}
}
配置Hibernate的连接数据库(有2种方式)
1.Java属性(hibernate.properties文件)
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://localhost:3306/hiber
hibernate.connection.username=root
hibernate.connection.password=123456
hibernate.show_sql=true
2.XML(hibernate.cfg.cml)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings(MySQL) -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hiber</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<!-- 映射配置文件 -->
<mapping resource="com/Customer.hbm.xml"/>
<mapping resource="com/Order.hbm.xml"/>
</session-factory>
</hibernate-configuration>
创建测试类
package service;
import org.hibernate.Session;
import org.hibernate.Transaction;
import tool.HibernateUtil;
import com.Customer;
import com.Order;
public class BusinessService {
/**
* @param args
*/
public static void main(String[] args) {
Session session = HibernateUtil.getSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Customer customer = new Customer();
customer.setName("David");
Order o = new Order();
o.setOrderNo("no_003");
//建立单向关联
o.setCustomer(customer);
session.save(o); //因为在Order.hbm.xml中配置了cascade="save-update",所以会级联保存
System.out.println(customer.getId()); //在save()操作后,事物提交前,已经能获得ID
customer.setName("Phil"); //会执行一条Update语句
tx.commit();
customer.setName("Bruce"); //事物提交后,持久化类的修改不会被保存
} catch(Exception e) {
if (tx != null) {
tx.rollback();
}
e.printStackTrace();
} finally {
session.close();
}
}
}
运行会抛以下异常
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient
instance before flushing: com.Customer
因为Order类对象参照的Customer不是持久化对象,而是临时对象。在保存Order对象之前,应该先保存Customer对象。可以使用级
联,修改配置文件(Order.hbm.xml)如下(加上cascade="save-update"):
<many-to-one name="customer" column="CUSTOMER_ID" cascade="save-update" />
再次运行
/**Output:
Hibernate: insert into CUSTOMERS (NAME) values (?)
Hibernate: insert into ORDERS (ORDER_NO, CUSTOMER_ID) values (?, ?)
7
Hibernate: update CUSTOMERS set NAME=? where ID=?
*/
以上输出,没有生成查询ID的语句(Hibernate: select max(ID) from CUSTOMERS)。因为我设置的标识符生成器是native和
identity的,这会由底层数据库负责生成ID,可以看到创建数据库表的时候设置了主键的auto_increment。如果数据库表没有设置
主键的auto_increment或者数据库不支持主键自动增涨,可以将标识符生成器设置为increment,这时持久化对象时会生成查询ID
的语句。
/*
查看数据库
select * from customers where id = 7;
IDNAME
7Phil
select * from orders where customer_id = 7;
IDORDER_IDCUSTOMER_ID
13no_001 7
*/
总结:
cascade表示级联,可选值有:
<1>."none":(默认值)不级联
<2>."save-update":级联保存和更新
<3>."delete":级联删除
<4>."all":包括save-update和delete
<5>."delete-orphan":删除所有和本对象已经解除关系了的对象
<6>."all-delete-orphan":包括all和delete-orphan
2.理解inverse属性
将上面的单向关联改成双向关联
在Customer.java持久化类里填加Order
private Set orders = new HashSet();
public Set getOrders() {
return orders;
}
public void setOrders(Set orders) {
this.orders = orders;
}
在Customer.hbm.xml中填加orders属性的配置
<set name="orders" cascade="all" inverse="false">
<key column="CUSTOMER_ID"/>
<one-to-many cascade="all" inverse="true">
<key column="CUSTOMER_ID"/>
<one-to-many class="com.Order"/>
</set>
再次运行
/**Output:
Hibernate: insert into CUSTOMERS (NAME) values (?)
Hibernate: insert into ORDERS (ORDER_NO, CUSTOMER_ID) values (?, ?)
Hibernate: update ORDERS set ORDER_NO=?, CUSTOMER_ID=? where ID=?
*/
总结:在双向关联关系中,一般设置一的一端为inverse="true",多的一端为inverse="false"。