Hibernate的FlushMode

文章详细介绍了Hibernate中FlushMode的设置及其影响,包括ALWAYS、AUTO、COMMIT、MANUAL的区别,以及手动调用flush()方法强制同步数据库的情况。此外,还讨论了Session的clear()方法如何清除缓存。内容主要关注数据持久化和事务管理在SpringBoot和Hibernate环境下的实现。
摘要由CSDN通过智能技术生成

一、Session中FlushMode的设置:

在事务开启前设置FlushMode属性,方法:

// session.setFlushMode(FlushMode.Always|AUTO|COMMIT|NEVER|MANUAL)。

@Service
public class TestService {
    Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    EntityManager entityManager;

    public FlushModeType getFlushModeType() { 
        // entityManager.setFlushMode(FlushModeType.AUTO);       
        return entityManager.getFlushMode();
    }
}

SpringBoot 中 application.yml 配置: 

spring:
  jpa:
    properties:
      org:
        hibernate:
          # FlushMode
          #   ALWAYS 和 AUTO的区别:
              #   当hibernate缓存中的对象被改动之后, 会被标记为脏数据(即与数据库不同步了)。
              #   当 session设置为FlushMode.AUTO时, hibernate 在进行查询的时候会判断缓存中的数据是否为脏数据,
              #   是则刷数据库, 不是则不刷, 而always是直接刷新, 不进行任何判断。很显然 auto 比 always 要高效得多。
          #   ALWAYS: 每次进行查询、提交事务、session.flush()的时候都会刷数据库
          #   AUTO: 设置成auto之后, 当程序进行查询、提交事务或者调用session.flush()的时候, 都会使缓存和数据库进行同步, 也就是刷新数据库
          #   COMMIT: 提交事务或者session.flush()时, 刷新数据库;查询不刷新
          #   MANUAL: 如果FlushMode是MANUAL或NEVEL, 在操作过程中hibernate会将事务设置为readonly, 所以在增加、删除或修改操作过程中会出现错误
          #   NEVER: 已经废弃了, 被MANUAL取代了
          flushMode: AUTO

FlushMode有5个值可选:

1  NEVEL   

已经废弃了,被MANUAL取代了

2 MANUAL 

spring3.x中的opensessioninviewfilter已经将默认的FlushMode设置为MANUAL了;

如果FlushMode是MANUAL或NEVEL,在操作过程中hibernate会将事务设置为readonly,所以在 增加、删除或修改 操作过程中会出现如下错误:
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read - only mode (FlushMode.NEVER)  -  turn your Session into FlushMode.AUTO or remove  ' readOnly '  marker from transaction definition;

解决办法网上有很多;
  1 配置事务,spring会读取事务中的各种配置来覆盖hibernate的session中的FlushMode;
  
  2 先编程式修改FlushMode,比如session.setFlushMode(FlushMode.AUTO); 这样hibernate就会自动去除readonly限制;
  
  3 直接修改opensessioninviewfilter过滤器的配置,配置过滤器的时候配置
<filter>
       <filter-name>openSession</filter-name>
       <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
       <init-param>
           <param-name>flushMode</param-name>
           <param-value>AUTO</param-value>
       </init-param>
   </filter>

3 AUTO

设置成auto之后,当程序进行查询、提交事务或者调用session.flush()的时候,都会使缓存和数据库进行同步,也就是刷新数据库

4 COMMIT

提交事务或者session.flush()时,刷新数据库;查询不刷新

5 ALWAYS

每次进行查询、提交事务、session.flush()的时候都会刷数据库
这里需要说一下和AUTO的区别,当hibernate缓存中的对象被改动之后,会被标记为脏数据(即与数据库不同步了)。当session设置为FlushMode.AUTO时,hibernate在进行查询的时候会判断缓存中的数据是否为脏数据,是则刷数据库,不是则不刷,而always是直接刷新,不进行任何判断。很显然auto比always要高效得多。

JPA的FlushModeType只有两种:

1、COMMIT:仅当提交事务时才能进行刷新

2、AUTO:(默认)在执行查询时进行刷新

二、Session中flush()方法说明:

可以强制进行从内存到数据库的同步,方法session.flush()

例: 

/** 
 * flush 强制与数据库同步 
 */  
@Test  
public void testFlush(){  
	Session session =  HibernateUitl.getSessionFactory().getCurrentSession();  
	session.beginTransaction();  
	Teacher t = (Teacher) session.get(Teacher.class, 3);  
	t.setName("yyy");  

	t.setName("yyyyy");  
	session.getTransaction().commit();  
}  

看这段代码,我们setName() 2次, 但程序只会更改数据库一次,在commit时。

/** 
 * flush 强制与数据库同步 
 */  
@Test 
public void testFlush(){  
	Session session =  HibernateUitl.getSessionFactory().getCurrentSession();  
	session.beginTransaction();  
	Teacher t = (Teacher) session.get(Teacher.class, 3);  
	t.setName("yyy");  
	session.flush();//有flush会执行2次UPDAE,没有会只执行一次  
	 t.setName("yyyyy");  
	session.getTransaction().commit();  
}  

我们在第2次setName()时 执行session.flush()

再看hibernate 执行的sql 语句

Hibernate: 
    update
        Teacher 
    set
        birthday=?,
        name=?,
        title=? 
    where
        id=?
Hibernate: 
    update
        Teacher 
    set
        birthday=?,
        name=?,
        title=? 
    where
        id=?

执行了2次Update

所以看出来flush方法会强制与数据库同步。

flush方法是可以设置的,也就是fulsh什么时候执行是可以设置的(第一条有说明)。

二、Session中Clear()方法说明:

无论是 Load 还是 Get 都会首先查找缓存(一级缓存) 如果没有,才会去数据库查找,调用Clear() 方法,可以强制清除Session缓存。

例:

public void testClear(){  
	Session session =  HibernateUitl.getSessionFactory().getCurrentSession();  
	session.beginTransaction();  
	Teacher t = (Teacher) session.get(Teacher.class, 3);  
	System.out.println(t.getName());  
	Teacher t2 = (Teacher) session.get(Teacher.class, 3);  
	System.out.println(t2.getName());  
	session.getTransaction().commit();  
}  

这里虽然用了2个get方法(get方法会立即执行sql语句),但因为第一次执行了会缓存一个ID为3的实体,所以虽然有2个get方法只执行一次SQL语句。

public void testClear(){  
	Session session =  HibernateUitl.getSessionFactory().getCurrentSession();  
	session.beginTransaction();  
	Teacher t = (Teacher) session.get(Teacher.class, 3);  
	System.out.println(t.getName());  
	session.clear();//这里不clear只会执行一次sql语句,有clear会执行2次  
	 Teacher t2 = (Teacher) session.get(Teacher.class, 3);  
	System.out.println(t2.getName());  
	session.getTransaction().commit();  
}  

这里在第2次get前执行session.clear(), 我们把 hibernate show_sql 出来,它就会执行2次sql语句了。

所以session.clear()会清除缓存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风情客家__

原创不易,觉得好的话给个打赏哈

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值