一、单向一对多映射关系
实体类的创建
1.本例子的 多的一方是Order,一的一方是Customer,一个Customer 可以有很多个Order, 而一个Order只能属于一个Customer。
实体类Customer
package hibernates.entities.n21; public class Customer { private Integer customerId; private String customerName; public Integer getCustomerId() { return customerId; } public void setCustomerId(Integer customerId) { this.customerId = customerId; } public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public Customer(String customerName) { super(); this.customerName = customerName; } public Customer() { super(); // TODO Auto-generated constructor stub } @Override public String toString() { return "Customer [customerId=" + customerId + ", customerName=" + customerName + "]"; } }
实体类Order
package hibernates.entities.n21; public class Order { private Integer orderId; private String orderName; private Customer customer; public Integer getOrderId() { return orderId; } public void setOrderId(Integer orderId) { this.orderId = orderId; } public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public Order(String orderName, Customer customer) { super(); this.orderName = orderName; this.customer = customer; } public Order() { super(); // TODO Auto-generated constructor stub } @Override public String toString() { return "Order [orderId=" + orderId + ", orderName=" + orderName + ", customer=" + customer + "]"; } }
编写基础映射文件
Order.hbm.xml的编写:主要是编写<many-to-one>标签
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-11-27 21:13:51 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="hibernates.entities.n21.Order" table="ORDERS"> <id name="orderId" type="java.lang.Integer"> <column name="ORDERID" /> <generator class="native" /> </id> <property name="orderName" type="java.lang.String"> <column name="ORDERNAME" /> </property> <!-- 映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系 name: 多这一端关联的一那一端的属性的名字 class: 一那一端的属性对应的类名 column: 一那一端在多的一端对应的数据表中的外键的名字 --> <many-to-one name="customer" class="hibernates.entities.n21.Customer" > <column name="CUSTOMER_ID" /> </many-to-one> </class> </hibernate-mapping>
Customer.hbm.xml的编写
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-11-27 21:13:51 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="hibernates.entities.n21.Customer" table="CUSTOMERS"> <id name="customerId" type="java.lang.Integer"> <column name="CUSTOMER_ID" /> <generator class="native" /> </id> <property name="customerName" type="java.lang.String"> <column name="CUSTOME_RNAME" /> </property> </class> </hibernate-mapping>
核心配置文件hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!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="connection.username">root</property> <property name="connection.password">123456</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///hibernate</property> <!-- 配置 hibernate 的基本信息 --> <!-- hibernate 所使用的数据库方言 <property name="dialect">org.hibernate.dialect.MySQLMyISAMDialect</property> <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> --> <!-- 执行操作时是否在控制台打印 SQL --> <property name="show_sql">true</property> <!-- 是否对 SQL 进行格式化 --> <property name="format_sql">true</property> <!-- 指定自动生成数据表的策略 --> <property name="hbm2ddl.auto">update</property> <!-- 设置 Hibernate 的事务隔离级别 --> <property name="connection.isolation">2</property> <!-- 删除对象后, 使其 OID 置为 null --> <property name="use_identifier_rollback">true</property> <!-- 配置 C3P0 数据源 --> <property name="hibernate.c3p0.max_size">10</property> <property name="hibernate.c3p0.min_size">5</property> <property name="c3p0.acquire_increment">2</property> <property name="c3p0.idle_test_period">2000</property> <property name="c3p0.timeout">2000</property> <property name="c3p0.max_statements">10</property> <!-- 指定关联的 .hbm.xml 文件 --> <mapping resource="hibernates/entities/n21/Customer.hbm.xml"/> <mapping resource="hibernates/entities/n21/Order.hbm.xml"/> </session-factory> </hibernate-configuration>
对Order和Customer的增删改查的测试
package hibernates.entities.n21; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.service.ServiceRegistry; import org.junit.After; import org.junit.Before; import org.junit.Test; public class HibernateTest { Session session; SessionFactory sessionFactory; Transaction transaction; @Before public void init() { ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build(); sessionFactory = new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); } @After public void destroy() { transaction.commit(); session.close(); sessionFactory.close(); } @Test public void testMany2OneSave() { Customer customer = new Customer(); customer.setCustomerName("小明"); Order order1 = new Order(); order1.setOrderName("AA"); Order order2 = new Order(); order2.setOrderName("BB"); //设定关联关系 order1.setCustomer(customer); order2.setCustomer(customer); //执行 save 操作: 先插入 Customer, 再插入 Order, 3 条 INSERT //先插入 1 的一端, 再插入 n 的一端, 只有 INSERT 语句. // session.save(customer); // session.save(order1); // session.save(order2); //先插入 Order, 再插入 Customer. 3 条 INSERT, 2 条 UPDATE //先插入 n 的一端, 再插入 1 的一端, 会多出 UPDATE 语句! //因为在插入多的一端时, 无法确定 1 的一端的外键值. 所以只能等 1 的一端插入后, 再额外发送 UPDATE 语句. //推荐先插入 1 的一端, 后插入 n 的一端 session.save(order1); session.save(order2); session.save(customer); } @Test public void testMany2OneGet() { //1. 若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的 //1 的那一端的对象! Order order = session.get(Order.class, 1); System.out.println(order); System.out.println(order.getCustomer().getClass().getName()); session.close(); //2. 在需要使用到关联的对象时, 才发送对应的 SQL 语句. Customer customer = order.getCustomer(); System.out.println(customer); //3. 在查询 Customer 对象时, 由多的一端导航到 1 的一端时, //若此时 session 已被关闭, 则默认情况下 //会发生 LazyInitializationException 异常 //4. 获取 Order 对象时, 默认情况下, 其关联的 Customer 对象是一个代理对象! } @Test public void testUpdate() { Order order = session.get(Order.class, 1); order.getCustomer().setCustomerName("555"); } @Test public void testDelete() { //在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象 Customer customer = session.get(Customer.class, 1); session.delete(customer); } }
二、双向一对多映射关系
实体类的创建(与一中的一样)
编写基础映射文件
Order.hbm.xml的编写:主要是编写<many-to-one>标签
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-12-1 19:54:01 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="hibernates.entities.n21.both.Order" table="BOTHORDERS"> <id name="orderId" type="java.lang.Integer"> <column name="ORDERID" /> <generator class="native" /> </id> <property name="orderName" type="java.lang.String"> <column name="ORDERNAME" /> </property> <many-to-one name="customer" class="hibernates.entities.n21.both.Customer" fetch="join" cascade="delete"> <column name="CUSTOMERID" /> </many-to-one> </class> </hibernate-mapping>
Customer.hbm.xml的编写
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-12-1 19:54:01 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="hibernates.entities.n21.both.Customer" table="BOTHCUSTOMERS"> <id name="customerId" type="java.lang.Integer"> <column name="CUSTOMERID" /> <generator class="native" /> </id> <property name="customerName" type="java.lang.String"> <column name="CUSTOMERNAME" /> </property> <set name="orders" table="BOTHORDERS" inverse="true" cascade="delete" order-by="CUSTOMERID DESC"> <key> <column name="CUSTOMERID" /> </key> <one-to-many class="hibernates.entities.n21.both.Order" /> </set> </class> </hibernate-mapping>
核心配置文件hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?><!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="connection.username">root</property> <property name="connection.password">123456</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///hibernate</property> <!-- 配置 hibernate 的基本信息 --> <!-- hibernate 所使用的数据库方言 <property name="dialect">org.hibernate.dialect.MySQLMyISAMDialect</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> --> <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!-- 执行操作时是否在控制台打印 SQL --> <property name="show_sql">true</property> <!-- 是否对 SQL 进行格式化 --> <property name="format_sql">true</property> <!-- 指定自动生成数据表的策略 --> <property name="hbm2ddl.auto">update</property> <!-- 设置 Hibernate 的事务隔离级别 --> <property name="connection.isolation">2</property> <!-- 删除对象后, 使其 OID 置为 null --> <property name="use_identifier_rollback">true</property> <!-- 配置 C3P0 数据源 --> <property name="hibernate.c3p0.max_size">10</property> <property name="hibernate.c3p0.min_size">5</property> <property name="c3p0.acquire_increment">2</property> <property name="c3p0.idle_test_period">2000</property> <property name="c3p0.timeout">2000</property> <property name="c3p0.max_statements">10</property> <!-- 指定关联的 .hbm.xml 文件 --> <!-- <mapping resource="hibernates/entities/n21/Customer.hbm.xml"/> <mapping resource="hibernates/entities/n21/Order.hbm.xml"/>--> <mapping resource="hibernates/entities/n21/both/Order.hbm.xml"/> <mapping resource="hibernates/entities/n21/both/Customer.hbm.xml"/> </session-factory> </hibernate-configuration>
Set标签中的三个属性:inverse、 cascade、 order-by
inverse属性:在hibernate中通过对 inverse 属性的来决定是由双向关联的哪一方来维护表和表之间的关系. inverse = false 的为主动方,inverse = true 的为被动方, 由主动方负责维护关联关系
在没有设置 inverse=true 的情况下,父子两边都维护父子关系,在一对多的关系中一般是把一的一端设置为true,这样可以让hibernate减少很多的update语句。
cascade属性:在对象 – 关系映射文件中, 用于映射持久化类之间关联关系的元素, <set>, <many-to-one> 和 <one-to-one> 都有一个 cascade 属性, 它用于指定如何操纵与当前对象关联的其他对象.
all: 所有情况下均进行关联操作,即save-update和delete。
none: 所有情况下均不进行关联操作。这是默认值。
save-update: 在执行save/update/saveOrUpdate时进行关联操作。
delete: 在执行delete 时进行关联操作。
all-delete-orphan: 当一个节点在对象图中成为孤儿节点时,删除该节点
order-by属性:<set> 元素有一个 order-by 属性, 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序,order-by 属性中还可以加入 SQL 函数。
下面是对于这几个属性打的一些相关代码,以及一些发现:
package hibernates.entities.n21.both; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.service.ServiceRegistry; import org.junit.After; import org.junit.Before; import org.junit.Test; public class HibernateTest { Session session; SessionFactory sessionFactory; Transaction transaction; @Before public void init() { ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build(); sessionFactory = new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); Customer customer1 = new Customer(); customer1.setCustomerName("AA"); Customer customer2 = new Customer(); customer2.setCustomerName("BB"); Order order1 = new Order(); Order order2 = new Order(); customer1.getOrders().add(order1); customer1.getOrders().add(order2); customer2.getOrders().add(order1); customer2.getOrders().add(order2); order1.setCustomer(customer1); order2.setCustomer(customer1); order1.setCustomer(customer2); order2.setCustomer(customer2); session.save(customer1); session.save(customer2); session.save(order1); session.save(order2); } @After public void destroy() { transaction.commit(); session.close(); sessionFactory.close(); } @Test public void testInverse() { //1.可以在 1 的一端的 set 节点指定 inverse=true, 来使 1 的一端放弃维护关联关系! //2.通过多的一端改变来改变表中的数据: Order order3 = session.get(Order.class, 1); order3.setOrderName("HH"); order3.getCustomer().setCustomerName("BB"); //3.通过一的一端来修改表中的数据: Customer customer3 = session.get(Customer.class, 1); customer3.setCustomerName("DD"); customer3.getOrders().iterator().next().setCustomer(customer3); System.out.println(customer3.getOrders()); } @Test public void testCascade() { //当<set> 中的 cascade的属性值为delete时一的一端的数据被删除时,它所关联的多的一端也被删除,<many-to-one>中的 //cascade也具有同样的功能 Customer customer3 = session.get(Customer.class, 1); session.delete(customer3); Order order3 = session.get(Order.class, 1); session.delete(order3); } @Test public void testOrderBy() { //下面两句输出语句都会使Hibernate查询customer关联的order表,然后按照order-by的设定排序查询 Customer customer3 = session.get(Customer.class, 1); System.out.println(customer3.getOrders()); System.out.println(customer3.getCustomerName()); } }