在我参加的一次采访中,我被问到以下问题-
比方说,我们有多集群的应用程序服务器,每个服务器都创建了自己的SessionFactory(指向相同的单个DB)。一台服务器检索一条记录[ID为1的人],该记录在仍处于打开状态的会话(s1)中缓存该记录。
同时,另一台服务器打开另一个会话(s2)并更新ID = 1的Person记录。现在,在s1中缓存的Person记录会怎样?这会是陈旧的记录吗?
我创建了一个小程序来模拟单个JVM上的场景。令我惊讶的是,尽管从同一class Frange(object):
def __init__(self, value, end=None, step=1.0):
if end is None:
self.start, self.end = 0.0, value
self.step = step
else:
self.start = value
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
if self.start < self.end:
return self.start
for n in Frange(0.5, 2.5, 0.5):
print(n)
创建了2个会话,会话缓存仍保留了过时的数据。甚至SessionFactory
也无济于事。
s1.flush()
我的问题是,如果同一记录被另一个会话更新, Session s1 = HibernateManager.getInstance().openNewSession();
System.out.println("Opened session s1");
Person p1 = s1.get(Person.class, 1);
System.out.println("Retreived person with ID = 1 using s1, Name is");
System.out.println(p1.getName()); //prints Test1
Session s2 = HibernateManager.getInstance().openNewSession();
System.out.println("Opened session s2");
Person p2 = s2.get(Person.class, 1);
System.out.println("Retreived person with ID = 1 using s2, Name is");
System.out.println(p2.getName()); //prints Test1
p2.setName("Test2");
Transaction tx = s2.beginTransaction();
s2.saveOrUpdate(p2);
tx.commit();
HibernateManager.getInstance().close(s2);
System.out.println("Changed name and updated - closed s2");
p1 = s1.get(Person.class, 1);
System.out.println("Retreived person with ID = 1 using s1 AGAIN, Name is");
System.out.println(p1.getName()); //prints Test1
System.out.println("Flushed s1");
s1.flush();
p1 = s1.get(Person.class, 1);
System.out.println("Retreived person with ID = 1 using s1 AGAIN, Name is");
System.out.println(p1.getName()); //still prints Test1. I was expecting Test2 because I flushed s1 (synced the session with DB)
HibernateManager.getInstance().close(s1);
是否会使缓存无效?
答案 0 :(得分:1)
是的,p1将是陈旧的记录。如果更新p1,则在会话刷新或会话s1的事务提交时,p1将覆盖p2的更新。
为防止这种情况,您可以使用乐观锁定 g。指定版本号(在Java源代码中为@Version
)(如果有这样的列,则指定为last-updated-date),并指示hibernate使用此列进行版本更新。在这种情况下,如果我们更新p1,则在会话刷新休眠状态下,hibernate将在更新之前执行一次选择,看到版本信息不匹配(有人进来,在中间并更新了记录),并抛出了StaleObject异常