zoukankan      html  css  js  c++  java
  • 转载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

  • 相关阅读:
    Python:dict用法
    Ubuntu无法识别显示器情况下,高分辨率的设置
    select节点clone全解析
    js控制frameset的rows
    jQuery中事情的动态绑定 (转)
    jQuery动态添加表格1
    使用ajax,后台传回的数据处理
    Spring Boot 之构建Hello Word项目
    linux防火墙基本操作
    Vmware虚拟机中安装cnetOS7详细图解步骤
  • 原文地址:https://www.cnblogs.com/airycode/p/5290919.html
Copyright © 2011-2022 走看看