转载Liferay PortletPreference store()方法研究
我们对于PortletPreference 的store()用的非常广泛,很多情况下,我们一般对其进行一些设定,然后最后调用store()存储之,类似以下代码:
PortletPreferences preferences = renderRequest.getPreferences();
preferences.setValue(“preference_portlet_id”,portletInstanceId);
preferences.store();
下面我们来研究这个store()方法的本质,(花了我足足2个下午,说实话,到存储层之后那段代码的调试真不是太简单的,好多是用的动态代理)
详细分析:
首先,这个store()方法的源代码如下:
@Override
public void store() throws IOException, ValidatorException {
if (_portletId == null) {
throw new UnsupportedOperationException();
}
try {
Portlet portlet = PortletLocalServiceUtil.getPortletById(
getCompanyId(), _portletId);
PreferencesValidator preferencesValidator =
PortalUtil.getPreferencesValidator(portlet);
if (preferencesValidator != null) {
preferencesValidator.validate(this);
}
PortletPreferencesLocalServiceUtil.updatePreferences(
getOwnerId(), getOwnerType(), _plid, _portletId, this);
}
catch (SystemException se) {
throw new IOException(se.getMessage());
}
}
从宏观上来说,它主要做了3件事情:
Line 7-9:它从Portlet池中根据companyId和portletId来获取我们目标的要操作的portlet
Line 10-14:对于Portlet的Preference进行validate.
Line 15-17:它吧检验过的Portlet,吧它的PortletPreference更新到对应的数据库表中。
因为我们的重点应该是如何更新数据库表和怎么更新,所以我们的重点放在第三部分
在我们的代码PortletPreferencesLocalServiceUtil.updatePreferences()方法,它最终会转为PortletPreferencesLocalServiceImpl.updatePreferences()方法的调用。
从上面可以看出来,它首先通过PortletPreferencesFactoryUtil的toXML方法吧我们的PortletPreferences对象转成一个xml字符串,这是第一个亮点,原因很简单,因为String是序列化的,方便存储。
比如我们的例子中,我们的PortletPreferences被转为下面的字符串:
紧接着,它会调用重载的updatePreferences()方法进行更新,并且刚才转为的preferences字符串也作为参数被传递进来,这个方法如下:
public PortletPreferences updatePreferences(
long ownerId, int ownerType, long plid, String portletId,
String xml)
throws SystemException {
PortletPreferences portletPreferences =
portletPreferencesPersistence.fetchByO_O_P_P(
ownerId, ownerType, plid, portletId);
if (portletPreferences == null) {
long portletPreferencesId = counterLocalService.increment();
portletPreferences = portletPreferencesPersistence.create(
portletPreferencesId);
portletPreferences.setOwnerId(ownerId);
portletPreferences.setOwnerType(ownerType);
portletPreferences.setPlid(plid);
portletPreferences.setPortletId(portletId);
}
portletPreferences.setPreferences(xml);
portletPreferencesPersistence.update(portletPreferences, false);
PortletPreferencesLocalUtil.clearPreferencesPool(ownerId, ownerType);
return portletPreferences;
}
其实如果熟悉框架的人一眼就可以看出这里大多数操作都是对数据库的操作。
首先,它在第7-8行通过调用PortletPreferencesPersistence的fetchByO_O_P_P方法,然后传递4个参数,来从数据库中取出已经存放的PortletPreference,我们细化看下:
不难看出,这个fetchByO_O_P_P()方法是从2个级别查询的,首先它会从查询缓存中获取,如果没有的话(因为有时候我们会吧查询缓存禁用)则执行数据库查询,看下这些 语句,可以发现最终拼凑的查询语句是: SELECTportletPreferences FROM PortletPreferences portletPreferences WHERE 上述 4 个参数条件 。
( 这是第二个亮点:因为数据库中一个PortletPreference是由4个参数共同决定的,其中有ownerId,portlet layout id ,所以这可以解释为什么不同的用户可以使用自己的PortletPreference对象而彼此之间不会打架,因为每个用户的userId不同)
一旦查询到PortletPreference之后,拿出来,它先赋值给PortletPreferences,然后会去查看这个对象是否为null,如果为null,则新建一个,否则则使用原有的。
因为我们的例子中,已经曾经点击过save()方法,所以是旧的,这会去触发PortletPreferencePersistenceImpl的updateImpl()方法
而它会去创建一个ClassLoaderSession(),然后调用BatchSessionUtil.update()方法来持久我们的portletPreferences,并且因为是update,所以动作是merge.
我们来细看下BatchSessionUtil.update()方法的源代码:
public void update(Session session, BaseModel<?> model, boolean merge)
throws ORMException {
if (merge || model.isCachedModel()) {
session.merge(model);
}
else {
if (model.isNew()) {
session.save(model);
}
else {
boolean contains = false;
if (isEnabled()) {
Object obj = session.get(
model.getClass(), model.getPrimaryKeyObj());
if ((obj != null) && obj.equals(model)) {
contains = true;
}
}
if (!contains && !session.contains(model)) {
session.saveOrUpdate(model);
}
}
}
if (!isEnabled()) {
session.flush();
return;
}
if ((PropsValues.HIBERNATE_JDBC_BATCH_SIZE == 0) ||
((_counter.get() % PropsValues.HIBERNATE_JDBC_BATCH_SIZE) == 0)) {
session.flush();
}
_counter.set(_counter.get() + 1);
}
可以发现,因为我们的session是 ClassLoaderSession,而且我们的动作是merge ,所以它最终会调用ClassLoaderSession的merge()方法
而ClassLoaderSession的merge()方法通过动态代理会委托调用com.liferay.portal.dao.orm.hibernate.SessionImpl的merge()方法:
而它会接着委托到org.hibernate.Session的merge()方法,所以说,最终这个调用实际上是一个数据库调用。
纵观上文,读者肯定有疑问,既然最终是个数据库的调用,那么操作的表是什么呢?我们回到BatchSessionImpl.update()方法:
从上面调试可以看出,实际session.merge(model)本质上是因为Liferay的数据模型和数据库做了一个O/R mappinig,然后通过操作Entity来操作数据库,而这个merge就是update方法。我们可以看出,这model类是PortletpreferencesImpl类,我们现在找出它对应的数据库表。
不难发现,这个com.liferay.portal.model.impl.PortletPreferencesImpl类的基类是PortletPreferencesModelImpl类:
而从这里可以看出这个类其实对应的数据库表是PortletPreferences,而且从其中的表字段也发现,刚好这些信息是我们存储一个PortletPreferences所必须的。尤其看到,preferences的类型是CLOB,这也是数据库存储字符串的常见做法。
到现在,一切谜底都解开了,真是很有快感。
总结:
我们总结下:
(1)PortletPreferences这个对象,在被持久化之前会被转为xml字符串,然后对应到数据库字段的类型是CLOB(字符大对象)
(2)PortletPreferences的信息是由4个属性共同决定的,portlet preference id ,ownerId,ownerType,portlet layout id ,这就保证了不同的用户可以用自己的Preference喜好来存储自己的变量,而不会影响到其他人。因为不同用户userId是不同的。
(3)PortletPreferences如果要取出来,肯定是先从查询缓存中取,如果没有的话才会去读数据库。
(4)无论是存储还是查询PortletPreferences,其对应的要操作的表名称都是PortletPreferences.
转载出处:http://supercharles888.blog.51cto.com/609344/1281101?utm_source=tuicool&utm_medium=referral
转载Liferay PortletPreference store()方法研究的更多相关文章
- [转]UDP/TCP穿越NAT的P2P通信方法研究(UDP/TCP打洞 Hole Punching)
[转]UDP/TCP穿越NAT的P2P通信方法研究(UDP/TCP打洞 Hole Punching) http://www.360doc.com/content/12/0428/17/6187784 ...
- (手写识别) Zinnia库及其实现方法研究
Zinnia库及其实现方法研究 (转) zinnia是一个开源的手写识别库.采用C++实现.具有手写识别,学习以及文字模型数据制作转换等功能. 项目地址 [http://zinnia.sourcefo ...
- Properties集合_list方法与store方法
Properties集合和流对象结合的功能 list()方法: import java.util.Properties; public class PropertiesDemo { public st ...
- Android Service 通知Activity更新界面的方法研究
Android Service 通知Activity更新界面的方法研究 Android的最重要的组件式service和activity,那么在使用的过程中,我们最常遇到的问题是他们之间的通信问题. ...
- 基于MATLAB的多项式数据拟合方法研究-毕业论文
摘要:本论文先介绍了多项式数据拟合的相关背景,以及对整个课题做了一个完整的认识.接下来对拟合模型,多项式数学原理进行了详细的讲解,通过对文献的阅读以及自己的知识积累对原理有了一个系统的认识.介绍多项式 ...
- RapidIOIP核的验证方法研究_王玉欢
RapidIOIP核的验证方法研究_王玉欢 https://wenku.baidu.com/view/0fd3c925d4d8d15abf234e73.html
- 基于FPGA实现的高速串行交换模块实现方法研究
基于FPGA实现的高速串行交换模块实现方法研究 https://wenku.baidu.com/view/9a3d501a227916888486d7ed.html
- 《基于 UML 的教务系统设计方法研究》论文笔记(十五)
标题:基于 UML 的教务系统设计方法研究 时间:2009 来源:太原师范学院 关键词:UML:面向对象:建模:教务管理系统. 二.研究内容 UML 建模 UML 涵盖了面向对象的分析.设计和实现,融 ...
- 基于MIndSpore框架的道路场景语义分割方法研究
基于MIndSpore框架的道路场景语义分割方法研究 概述 本文以华为最新国产深度学习框架Mindspore为基础,将城市道路下的实况图片解析作为任务背景,以复杂城市道路进行高精度的语义分割为任务目标 ...
随机推荐
- Android 读写位于SD卡上的sqlite数据库文件错误问题
09-12 15:24:33.903: W/System.err(19499): java.lang.NullPointerException: Attempt to invoke virtual m ...
- apache+php生产环境错误记录
报错1: [18-Jul-2016 14:36:31 Asia/Shanghai] PHP Warning: DOMDocument::load(): I/O warning : failed to ...
- locals()
locals() 有两种情况: (1) 如果在函数内部使用 locals(),那么会以字典的形式返回函数内部定义的变量,如下例1(2) 如果在函数外部使用 locals(),那么会以字典的形式返回全局 ...
- 今天是学习C#面向过程的最后的一天
今天学习完啦面向过程,可能写法也就是那些,固定不变的,但是程序的写法就是由自己决定······ 今天学习了调用已经存在的方法,就是在.net Framework 中存在的方法,具体今天提到的有这些, ...
- C++中的字节对齐分析
struct A { int a; char b; short c; }; struct B { char a; int b; short c; }; #pragma pack(2) struct C ...
- C++成员初始化列表的语法
如果Classy是一个累,而mem1.mem2.mem3都是这个类的数据称源,则类构造函数可以使用如下的语法来初始化数据成员:Classy::Classy(int n, int m) : mem1(n ...
- 【Java nio】java nio笔记
缓冲区操作:缓冲区,以及缓冲区如何工作,是所有I/O的基础.所谓“输入/输出”讲的无非就是把数据移出货移进缓冲区.进程执行I/O操作,归纳起来也就是向操作系统发出请求,让它要么把缓冲区里的数据排干,要 ...
- 生命游戏/Game of Life的Java实现
首先简单介绍一下<生命游戏> 生命游戏其实是一个零玩家游戏.它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞.一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死 ...
- 如何查找元素对应事件的js代码
以chrome的firebug为例 1.找到其dom元素,然后右键"break on"-->"subtree modification"等,设置后元素旁边 ...
- maven指定项目的构建、打包和tomcat插件的pom.xml配置
1.pom.xml添加如下配置: <build> <finalName>${parent.artifactId}</finalName> <plugins&g ...