一、关系映射
实体类之间的关联映射以及表之间的关系是 ORM 的灵魂之处。对象间的关系的子集可以用下列四种方式解释。关联映射可以是单向的也可以是双向的。
映射类型 | 描述 |
---|---|
Many-to-One | 使用 Hibernate 映射多对一关系 |
One-to-One | 使用 Hibernate 映射一对一关系 |
One-to-Many | 使用 Hibernate 映射一对多关系 |
Many-to-Many | 使用 Hibernate 映射多对多关系 |
1、多对一
1、映射文件 Product.hbm.xml 配置:
1 <!DOCTYPE hibernate-mapping PUBLIC 2 "-//Hibernate/Hibernate Mapping DTD//EN" 3 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 4 5 <!--映射文件--> 6 <hibernate-mapping package="com.hibernate.pojo"> 7 <!--class表示一个由hibernate管理的持久对象,对应数据库中一个表--> 8 <!--table数据库的表名--> 9 <!--一方--> 10 <class name="ProductDir" table="product_dir"> 11 <id name="id" type="int" column="id"> 12 <!--generator表示主键的生成方式,native自动选择数据库本地的策略--> 13 <generator class="native"/> 14 </id> 15 <!--非主键属性--> 16 <property name="dirNum" column="num" type="string"/> 17 <property name="dirName" column="name" type="string"/> 18 </class> 19 20 <!--多方--> 21 <class name="Product" table="product"> 22 <id name="id" type="int" column="id"> 23 <!--generator表示主键的生成方式,native自动选择数据库本地的策略--> 24 <generator class="native"/> 25 </id> 26 <!--非主键属性--> 27 <property name="name" column="name" type="string"/> 28 <property name="price" column="price" type="float"/> 29 30 <!--多Product对一ProductDir--> 31 <many-to-one name="dir" class="ProductDir" column="dir_id"/> 32 </class> 33 </hibernate-mapping>
2、pojo设计:
1、多方
1 package com.hibernate.pojo; 2 3 4 /** 5 * @author zt1994 2018/3/6 14:16 6 */ 7 public class Product { 8 private Integer id; 9 private String name; 10 private float price; 11 private ProductDir dir; 12 13 public Integer getId() { 14 return id; 15 } 16 17 public void setId(Integer id) { 18 this.id = id; 19 } 20 21 public String getName() { 22 return name; 23 } 24 25 public void setName(String name) { 26 this.name = name; 27 } 28 29 public float getPrice() { 30 return price; 31 } 32 33 public void setPrice(float price) { 34 this.price = price; 35 } 36 37 public ProductDir getDir() { 38 return dir; 39 } 40 41 public void setDir(ProductDir dir) { 42 this.dir = dir; 43 } 44 }
2、一方
1 package com.hibernate.pojo; 2 3 /** 4 * @author zt1994 2018/3/7 15:23 5 */ 6 public class ProductDir { 7 private Integer id; 8 private String dirNum; 9 private String dirName; 10 11 public Integer getId() { 12 return id; 13 } 14 15 public void setId(Integer id) { 16 this.id = id; 17 } 18 19 public String getDirNum() { 20 return dirNum; 21 } 22 23 public void setDirNum(String dirNum) { 24 this.dirNum = dirNum; 25 } 26 27 public String getDirName() { 28 return dirName; 29 } 30 31 public void setDirName(String dirName) { 32 this.dirName = dirName; 33 } 34 }
3、数据库设计:
1、product 表
2、product_dir 表
4、查询测试
1 /** 2 * 测试多对一 查询 3 */ 4 @Test 5 public void testManyToOneQuery(){ 6 //1.获取session 7 Session session = HibernateUtil.getSession(); 8 Transaction transaction = null; 9 try { 10 //2.开启事务 11 transaction = session.getTransaction(); 12 transaction.begin(); 13 //3.操作CRUD 14 String hql = "select o from com.hibernate.pojo.Product o"; 15 Query query = session.createQuery(hql); 16 List<Product> list = query.list(); 17 for (Product product : list) { 18 System.out.println(product.getDir().getDirName()); 19 } 20 //4.提交事务 21 transaction.commit(); 22 } catch (Exception e) { 23 if (transaction != null) transaction.rollback(); //回滚 24 e.printStackTrace(); 25 } finally { 26 //5.关闭资源 27 session.close(); 28 } 29 }
5、保存测试
1 /** 2 * 保存测试 3 */ 4 @Test 5 public void testManyToOneSave(){ 6 //1.获取session 7 Session session = HibernateUtil.getSession(); 8 Transaction transaction = null; 9 try { 10 //2.开启事务 11 transaction = session.getTransaction(); 12 transaction.begin(); 13 //3.操作CRUD 14 //一方(产品类型) 15 ProductDir productDir = new ProductDir(); 16 productDir.setDirName("类型4"); 17 18 //多方(产品1) 19 Product product1 = new Product(); 20 //建立联系 21 product1.setDir(productDir); 22 product1.setName("产品1"); 23 //多方(产品2) 24 Product product2 = new Product(); 25 //建立联系 26 product2.setDir(productDir); 27 product2.setName("产品2"); 28 29 //保存一方产品类型 30 session.save(productDir); 31 //保存多方产品1,2 32 session.save(product1); 33 session.save(product2); 34 35 //4.提交事务 36 transaction.commit(); 37 } catch (Exception e) { 38 if (transaction != null) transaction.rollback(); //回滚 39 e.printStackTrace(); 40 } finally { 41 //5.关闭资源 42 session.close(); 43 } 44 }
控制台输出:
注意:如果先保存产品后保存产品类型,控制台输出不一样,
控制台输出:
实际情况,我们先把类型分好,然后进货的时候就直接可以分类。但是如果相反的话,我们就要先进货,再分类型,再把货放到相应类型中去。
2、一对多
1、映射文件 Product.hbm.xml 配置:
1 <!DOCTYPE hibernate-mapping PUBLIC 2 "-//Hibernate/Hibernate Mapping DTD//EN" 3 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 4 5 <!--映射文件--> 6 <hibernate-mapping package="com.hibernate.pojo"> 7 <!--class表示一个由hibernate管理的持久对象,对应数据库中一个表--> 8 <!--table数据库的表名--> 9 <!--一方--> 10 <class name="ProductDir" table="product_dir"> 11 <id name="id" type="int" column="id"> 12 <!--generator表示主键的生成方式,native自动选择数据库本地的策略--> 13 <generator class="native"/> 14 </id> 15 <!--非主键属性--> 16 <property name="dirNum" column="num" type="string"/> 17 <property name="dirName" column="name" type="string"/> 18 19 <set name="products"> 20 <key column="dir_id"/> 21 <one-to-many class="Product"/> 22 </set> 23 </class> 24 25 <!--多方--> 26 <class name="Product" table="product"> 27 <id name="id" type="int" column="id"> 28 <!--generator表示主键的生成方式,native自动选择数据库本地的策略--> 29 <generator class="native"/> 30 </id> 31 <!--非主键属性--> 32 <property name="name" column="name" type="string"/> 33 <property name="price" column="price" type="float"/> 34 </class> 35 </hibernate-mapping>
2、pojo 设计:
1 package com.hibernate.pojo; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 /** 7 * @author zt1994 2018/3/7 15:23 8 */ 9 public class ProductDir { 10 private Integer id; 11 private String dirNum; 12 private String dirName; 13 private Set<Product> products = new HashSet<>(); //产品集合 14 15 public Integer getId() { 16 return id; 17 } 18 19 public void setId(Integer id) { 20 this.id = id; 21 } 22 23 public String getDirNum() { 24 return dirNum; 25 } 26 27 public void setDirNum(String dirNum) { 28 this.dirNum = dirNum; 29 } 30 31 public String getDirName() { 32 return dirName; 33 } 34 35 public void setDirName(String dirName) { 36 this.dirName = dirName; 37 } 38 39 public Set<Product> getProducts() { 40 return products; 41 } 42 43 public void setProducts(Set<Product> products) { 44 this.products = products; 45 } 46 }
3、测试一对多查询
1 /** 2 * 测试一对多保存 3 */ 4 @Test 5 public void testOneToManySave(){ 6 //1.获取session 7 Session session = HibernateUtil.getSession(); 8 Transaction transaction = null; 9 try { 10 //2.开启事务 11 transaction = session.getTransaction(); 12 transaction.begin(); 13 //3.操作CRUD 14 //一方(产品类型) 15 ProductDir productDir = new ProductDir(); 16 productDir.setDirName("类型4"); 17 18 //多方(产品1) 19 Product product1 = new Product(); 20 product1.setName("产品1"); 21 //多方(产品2) 22 Product product2 = new Product(); 23 product2.setName("产品2"); 24 25 //建立关系(只能由一方建立多方关系) 26 productDir.getProducts().add(product1); 27 productDir.getProducts().add(product2); 28 29 //保存一方产品类型 30 session.save(productDir); 31 //保存多方产品1,2 32 session.save(product1); 33 session.save(product2); 34 //4.提交事务 35 transaction.commit(); 36 } catch (Exception e) { 37 if (transaction != null) transaction.rollback(); //回滚 38 e.printStackTrace(); 39 } finally { 40 //5.关闭资源 41 session.close(); 42 } 43 }
二、二级缓存
第二级缓存是一种可选择的缓存并且第一级缓存在任何想要在第二级缓存中找到一个对象前将总是被询问。第二级缓存可以在每一个类和每一个集合的基础上被安装,并且它主要负责跨会话缓存对象。
任何第三方缓存可以和 Hibernate 一起使用。org.hibernate.cache.CacheProvider 接口被提供,它必须实现来给 Hibernate 提供一个缓存实现的解决方法。
1、并发策略
一个并发策略是一个中介,它负责保存缓存中的数据项和从缓存中检索它们。如果你将使用一个二级缓存,你必须决定,对于每一个持久类和集合,使用哪一个并发策略。
- Transactional:为主读数据使用这个策略,在一次更新的罕见状况下并发事务阻止过期数据是关键的。
- Read-write:为主读数据再一次使用这个策略,在一次更新的罕见状况下并发事务阻止过期数据是关键的。
- Nonstrict-read-write:这个策略不保证缓存和数据库之间的一致性。如果数据几乎不改变并且过期数据不是很重要,使用这个策略。
- Read-only:一个适合永不改变数据的并发策略。只为参考数据使用它。
2、配置二级缓存
1、拷贝 jar 包
2、配置Hibernate.cfg.xml文件
1、开启二级缓存
hibernate.cache.use_second_level_cache=true
2、添加一个二级缓存的供应商(实现类)
hibernate4的配置:hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
hibernate3的配置:hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
3、开启查询缓存
hibernate.cache.use_query_cache=true
4、配置那个 domain 需要二级缓存(最好添加在映射文件后)
<class-cache class="com.hibernate.day01.model.Product" usage="read-write" />
1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE hibernate-configuration PUBLIC 3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> 5 6 <!--hibernate核心配置文件--> 7 <hibernate-configuration> 8 <session-factory> 9 <property name="dialect"> 10 org.hibernate.dialect.MySQLDialect 11 </property> 12 <!--链接池配置--> 13 <property name="connection.driver_class">com.mysql.jdbc.Driver</property> 14 <property name="connection.url">jdbc:mysql://localhost:3306/how2java</property> 15 <property name="connection.username">root</property> 16 <property name="connection.password">admin</property> 17 <!--显示sql语句--> 18 <property name="show_sql">true</property> 19 20 <!--开启二级缓存--> 21 <property name="cache.use_second_level_cache">true</property> 22 23 <!--添加一个二级缓存的供应商(实现类)--> 24 <property name="hibernate.cache.region.factory_class"> 25 org.hibernate.cache.ehcache.EhCacheRegionFactory 26 </property> 27 28 <!--开启查询缓存--> 29 <property name="hibernate.cache.use_query_cache">true</property> 30 31 <!-- 映射文件--> 32 <mapping resource="mapper/Product.hbm.xml"/> 33 34 <!--配置那个domain需要二级缓存(最好添加在映射文件后)--> 35 <class-cache class="com.hibernate.pojo.Product" usage="read-write" /> 36 </session-factory> 37 </hibernate-configuration>
3、测试二级缓存
1 /** 2 * 测试二级缓存 3 */ 4 @Test 5 public void testSecondLevelCache(){ 6 //1.获取session 7 Session session = HibernateUtil.getSession(); 8 //2.开启事务 9 Transaction transaction = session.getTransaction(); 10 transaction.begin(); 11 //3.操作CRUD 12 Product product1 = (Product) session.get(Product.class, 1); //一、二级缓存都没有命中 13 System.out.println(product1.hashCode()); 14 Product product2 = (Product) session.get(Product.class, 1); //一级缓存命中 15 System.out.println(product2.hashCode()); 16 session.close(); 17 18 Session session2 = HibernateUtil.getSession(); 19 Product product3 = (Product) session2.get(Product.class, 1); //二级缓存命中 20 System.out.println(product3.hashCode()); 21 Product product4 = (Product) session2.get(Product.class, 1); //一级缓存命中 22 System.out.println(product4.hashCode()); 23 24 //4.提交事务 25 transaction.commit(); 26 //5.关闭资源 27 session2.close(); 28 } 29 }
控制台输出:
分析:
第一次 get 操作获取到 product1 时,一级,二级缓存都没有,所有发送 SQL 语句查询,并保存到一级缓存和二级缓存中;
第二次 get 操作直接从一级缓存获取数据,所有两个对象的 hashCode 相同;
第三次 get 操作时,由于前一个 session 被关闭了,所有没有一级缓存,只有从二级缓存中获取数据,并保存到新session中;
第四次 get 操作时,直接从 session 的一级缓存中获取数据,hashCode 和 product3 的 hashCode 相同。