上一篇介绍了hql语句的认识,接下来认识sessionFactory二级缓存。
首先总结一下下面将要介绍知识:
一)SessionFactory二级缓存
(1)在默认情况下,SessionFactory二级缓存只存放连接数据库的相关信息,和映射文件信息,不能存放持久化对象(2)在默认情况下,session.get(),每执行一次,都要查询数据库,即生成select语句
(3)启用SessionFactory二级缓存,使其也能存持久化对象
(4) 在默认情况下,session.get(Customer.class,1),按如下顺序查找:
(1)在session一级缓存中查询对象,如果查到,直接返回。
(2)如果查不到,再查询数据库表,如果查到了,将春封装成PO对象返回,同时存于session一级缓存中,以备后用
(5)当启用SessionFacotry二级缓存后,session.get/load(Customer.class,1),则按如下顺序查找:
(1)在session一级缓存中查询对象,如果查到,直接返回。
(2)如果查不到,再查询sessionfactory二级缓存,如果查询到了则返回,并复制一份到session一级缓存中,以备后用
(3)如果在sessionfactory二级缓存没有找到,查询数据库表,如果查到了,将其封装成PO对象返回,同时存于session
和sessionfactory级缓存中,以备后用
(6)如何启用SessionFactory二级缓存
hibernate.cache.provider_class=org.hibernate.cache.HashtableCacheProvider
(7)将Customer对象存入二级缓存
Customer.hbm.xml文件
<class name="Customer" table="CUSTOMERS">
<cache usage="read-only"/>
</class>
(8)将Customer对象和Order集合存入二级缓存
Customer.hbml.xml文件
<set name="ordetSet" table="ORDERS">
<cache usage="read-only"/>
</set>
Order.hbm.xml文件
<class name="Order" table="ORDERS">
<cache usage="read-only"/>
</class>
(9)get()/load()使用二级缓存中的普通缓存区
(10)HQL使用二级缓存中的查询缓存区,即list()/uniqueResult()使用二级缓存中的查询缓存区,如何启用查询缓存区
hibernate.cache.use_query_cache=true
session.createQuery("FROM Customer c WHERE c.id=1").setCacheable(true).list();
session.createQuery("FROM Customer c WHERE c.id=1").setCacheable(true).uniqueResult();
(11)session.update()会同步更新一级和二级缓存区
综上所述:以空换时
接下来用实例来学习二级缓存:
首先写两个实体类Customer,Order:
Customer:
package example.one2many_single;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 客户(一方)
* @author Administrator
*
*/
public class Customer {
private Integer id;//对应表的主键
private String name;
private Integer age;
private Set<Order> orderSet=new LinkedHashSet<Order>();//关联属性
public Customer() {
}
public Customer(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Set<Order> getOrderSet() {
return orderSet;
}
public void setOrderSet(Set<Order> orderSet) {
this.orderSet = orderSet;
}
}
Order类:
package example.one2many_single;
/**
* 订单(多的一方)
* @author Administrator
*
*/
public class Order {
private Integer id;
private String OrderNo;//订单编号
private Integer price;//价格
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderNo() {
return OrderNo;
}
public void setOrderNo(String orderNo) {
OrderNo = orderNo;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Order(String orderNo, Integer price) {
super();
OrderNo = orderNo;
this.price = price;
}
public Order() {
}
}
第二步写配置文件hibernate.properties:
#hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect
#hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://127.0.0.1:3306/example_db
hibernate.connection.username=root
hibernate.connection.password=****
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
hibernate.cache.provider_class=org.hibernate.cache.HashtableCacheProvider
第三步写映射配置文件CustomerOrder.hbm.xml:只是把customer写入二级缓存,是只能读的
<?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="example.sessionFactory">
<class name="Customer" table="customers">
<cache usage="read-only"/>
<id name="id" column="id" type="int">
<generator class="native"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="int"></property>
<!-- set标签用于映射单向一对多
name表示单方的关联属性
table表示多方对应表的名字
key-cloumn表示多方对应表的外键
one-to-many-class表示单方关联属性中的每个元素的类型
-->
<set name="orderSet" table="orders" cascade="all" inverse="true">
<key column="customers_id"></key>
<one-to-many class="Order"/>
</set>
</class>
<!-- 映射类的多方 -->
<class name="Order" table="Orders">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="orderNo" column="orderNo"></property>
<property name="price" column="price"></property>
<many-to-one name="customer" column="customers_id"></many-to-one>
</class>
</hibernate-mapping>
package example.sessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import example.utils.HibernateUtils;
public class CustomerDao {
@Test
public void test01(){
Session session = HibernateUtils.getSession();
Transaction t = session.getTransaction();
try{
t.begin();
Customer c= (Customer) session.get(Customer.class, 1);
for(Order o:c.getOrderSet()){
System.out.println(o.getOrderNo()+","+o.getPrice());
}
session.clear();//清空一级缓存
System.out.println("-----------------");
c = (Customer) session.get(Customer.class,1);
for(Order o : c.getOrderSet()){
System.out.println(o.getOrderNo()+":"+o.getPrice());
}
t.commit();
}catch (Exception e) {
// TODO: handle exception
t.rollback();
e.printStackTrace();
}
}
}
运行结果:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_, customer0_.age as age0_0_ from customers customer0_ where customer0_.id=?
Hibernate: select orderset0_.customers_id as customers4_0_1_, orderset0_.id as id1_, orderset0_.id as id1_0_, orderset0_.orderNo as orderNo1_0_, orderset0_.price as price1_0_, orderset0_.customers_id as customers4_1_0_ from Orders orderset0_ where orderset0_.customers_id=?
order2011,130
order2012,130
-----------------
Hibernate: select orderset0_.customers_id as customers4_0_1_, orderset0_.id as id1_, orderset0_.id as id1_0_, orderset0_.orderNo as orderNo1_0_, orderset0_.price as price1_0_, orderset0_.customers_id as customers4_1_0_ from Orders orderset0_ where orderset0_.customers_id=?
order2011:130
order2012:130
根据结果我们可以分析到,使用get立即查询数据时,以及对象导航查询有两条sql语句产生,当把一级缓存session清空掉之后,只有查询订单一条sql语句产生,这个说明我们此时查询的顾客是从sessionFactory二级缓存查到customer数据的,然而订单查询没有在映射配置文件中配置存入二级缓存,所以就会从数据库中查询。
那么接下来测试把订单也存入二级缓存:
<?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="example.sessionFactory">
<class name="Customer" table="customers">
<cache usage="read-only"/>
<id name="id" column="id" type="int">
<generator class="native"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="int"></property>
<!-- set标签用于映射单向一对多
name表示单方的关联属性
table表示多方对应表的名字
key-cloumn表示多方对应表的外键
one-to-many-class表示单方关联属性中的每个元素的类型
-->
<set name="orderSet" table="orders" cascade="all" inverse="true">
<cache usage="read-only"/>
<key column="customers_id"></key>
<one-to-many class="Order"/>
</set>
</class>
<!-- 映射类的多方 -->
<class name="Order" table="Orders">
<cache usage="read-only"/>
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="orderNo" column="orderNo"></property>
<property name="price" column="price"></property>
<many-to-one name="customer" column="customers_id"></many-to-one>
</class>
</hibernate-mapping>
测试类跟上面的一样。
查询结果如下:
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_, customer0_.age as age0_0_ from customers customer0_ where customer0_.id=?
Hibernate: select orderset0_.customers_id as customers4_0_1_, orderset0_.id as id1_, orderset0_.id as id1_0_, orderset0_.orderNo as orderNo1_0_, orderset0_.price as price1_0_, orderset0_.customers_id as customers4_1_0_ from Orders orderset0_ where orderset0_.customers_id=?
order2011,130
order2012,130
-----------------
order2012:130
order2011:130
下面学习HQL使用二级缓存中的查询缓存区:
首先在配置文件hibernate.properties加入:
hibernate.cache.use_query_cache=true
接下来写测试类:
@Test
public void test2(){
Session session = HibernateUtils.getSession();
Transaction t = session.getTransaction();
try{
t.begin();
String hql = "from Customer c where c.id = 1";
Query query = session.createQuery(hql);
query.setCacheable(true);//启用查询缓冲区
Customer c = (Customer) query.uniqueResult();
for(Order o : c.getOrderSet()){
System.out.println(o.getOrderNo()+":"+o.getPrice());
}
session.clear();
hql = "from Customer c where c.id = 1";
query = session.createQuery(hql);
query.setCacheable(true);
c = (Customer) query.uniqueResult();
for(Order o : c.getOrderSet()){
System.out.println(o.getOrderNo()+":"+o.getPrice());
}
t.commit();
}catch(Exception e){
e.printStackTrace();
t.rollback();
}finally{
HibernateUtils.closeSession();
}
}
运行结果如下:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: select customer0_.id as id0_, customer0_.name as name0_, customer0_.age as age0_ from customers customer0_ where customer0_.id=1
Hibernate: select orderset0_.customers_id as customers4_0_1_, orderset0_.id as id1_, orderset0_.id as id1_0_, orderset0_.orderNo as orderNo1_0_, orderset0_.price as price1_0_, orderset0_.customers_id as customers4_1_0_ from Orders orderset0_ where orderset0_.customers_id=?
order2012:130
order2011:130
order2011:130
order2012:130
从结果中我们可以看到从session.close()之后得到的数据是从二级缓存中得到的。
下面学习如何修改二级缓存中的数据:假设我们要修改customer中的数据:
在映射文件中把<cache usage="read-only"/>改为<cache usage="read-write"/>。
接下来我们写一个测试类:
@Test
public void test3(){
Session session = HibernateUtils.getSession();
Transaction t = session.getTransaction();
try{
t.begin();
Customer c = (Customer) session.get(Customer.class,1);
c.setName("小尼妞");
session.update(c);
t.commit();
}catch(Exception e){
e.printStackTrace();
t.rollback();
}finally{
HibernateUtils.closeSession();
}
}
运行结果如下:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_, customer0_.age as age0_0_ from customers customer0_ where customer0_.id=?
Hibernate: update customers set name=?, age=? where id=?