A StaleObjectStateException is Thrown When Using a Liferay Service

Issue

After installing Liferay DXP 7.2 Service Pack 1 or Fix Pack 2 a StaleObjectStateException is generated when a customization uses a Liferay service to update or modify data:

  Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted
  by another transaction (or unsaved-value mapping was incorrect): [com.liferay.portal.model.impl.ContactImpl#8367818]
  at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:485)
  at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:255)
  at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84)
  at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:867) at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:851)
  at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:855) at com.liferay.portal.dao.orm.hibernate.SessionImpl.merge(SessionImpl.java:241)
  ... 208 more

Resolution

A StaleObjectStateException is commonly thrown by Hibernate when stale data is detected, such as when conflicting modifications are encountered.

General Pattern: if you update a model, but don’t re-capture the return value, future updates to that same model, even within the same thread, may result in a StaleObjectStateException. To resolve this, make sure to store the return value of any methods that update the object:

Incorrect:

_fooLocalService.update(foo);

Correct:

foo = _fooLocalService.update(foo);

Example #1: if you fetch a variable and then update it multiple times within different methods, this may also result in a StaleObjectStateException. This pattern often spans multiple classes (something updated in one class’s instance method is never returned, so another class never knows about the update). To resolve this, make sure that any method that updates a model object also returns that model object:

Incorrect:

public void setLastAccessDate(Foo foo) {
    foo.setLastAccessDate(new Date());
    _fooLocalService.update(foo);
}

public void setLastAccessIPAddress(Foo foo) {
    foo.setLastAccessIPAddress("127.0.0.1");
    _fooLocalService.update(foo);
}

public void service(
    HttpServletRequest request, HttpServletResponse response) {

    Foo foo = _fooLocalService.getFoo(fooId);
    setLastAccessDate(foo);
    setLastAccessIPAddress(foo);
}

Correct:

public Foo setLastAccessDate(Foo foo) {
    foo.setLastAccessDate(new Date());
    return _fooLocalService.update(foo);
}

public Foo setLastAccessIPAddress(Foo foo) {
    foo.setLastAccessIPAddress("127.0.0.1");
    return _fooLocalService.update(foo);
}

public void service(
    HttpServletRequest request, HttpServletResponse response) {

    Foo foo = _fooLocalService.getFoo(fooId);
    foo = setLastAccessDate(foo);
    foo = setLastAccessIPAddress(foo);
}

Example #2: if you fetch a variable and then store it in a shared memory location (such as the HttpSession or an internal cache of frequently accessed objects), this shared memory location may not know about any updates, which may also cause a StaleObjectStateException. To resolve this, you would make sure to reset the value in the shared location any time an update occurs:

Incorrect:

public void setLastAccessDate(Foo foo) {
    foo.setLastAccessDate(new Date());
    _fooLocalService.update(foo);
}

public void service(
    HttpServletRequest request, HttpServletResponse response) {

    Foo foo = _fooLocalService.getFoo(fooId);
    session.setAttribute("FOO", foo);
    setLastAccessDate(foo);
}

Correct:

public Foo setLastAccessDate(Foo foo) {
    foo.setLastAccessDate(new Date());
    return _fooLocalService.update(foo);
}

public void service(
    HttpServletRequest request, HttpServletResponse response) {

    Foo foo = _fooLocalService.getFoo(fooId);
    session.setAttribute("FOO", foo);
    foo = setLastAccessDate(foo);
    session.setAttribute("FOO", foo);
}

Additional Examples from Liferay source: LPS-86385 and LPS-86695

Additional Information

As of Liferay DXP 7.2 Service Pack 1 or Fix Pack 2, additional Liferay services now have MVCC enabled.

Liferay's MVCC implementation relies on Hibernate's optimistic locking system. With this mechanism in place, modifications that Hibernate identifies as conflicting will be revealed.

Below is a list of additional Liferay services that now have MVCC enabled.

Additional Liferay Services with MVCC Enabled

Components Services Tables

Account

AccountEntry
AccountEntryUserRel

AccountEntry
AccountEntryUserRel

Asset

AssetEntryUsage

AssetEntryUsage

Asset Auto Tagger

AssetAutoTaggerEntry

AssetAutoTaggerEntry

Asset Category Property

AssetCategoryProperty

AssetCategoryProperty

Asset Display Page

AssetDisplayPageEntry

AssetDisplayPageEntry

Asset Entry Rel

AssetEntryAssetCategoryRel

AssetEntryAssetCategoryRel

Asset List

AssetListEntry
AssetListEntryAssetEntryRel
AssetListEntrySegmentsEntryRel
AssetListEntryUsage

AssetListEntry
AssetListEntryAssetEntryRel
AssetListEntrySegmentsEntryRel
AssetListEntryUsage

Blogs

BlogsEntry
BlogsStatsUser

BlogsEntry
BlogsStatsUser

Bookmarks

BookmarksEntry
BookmarksFolder

BookmarksEntry
BookmarksFolder

Calendar

Calendar
CalendarBooking
CalendarNotificationTemplate
CalendarResource

Calendar
CalendarBooking
CalendarNotificationTemplate
CalendarResource

Document Library Content

DLContent

DLContent

Dynamic Data Mapping

DDMContent
DDMDataProviderInstance
DDMDataProviderInstanceLink
DDMFormInstance
DDMFormInstanceRecord
DDMFormInstanceRecordVersion
DDMFormInstanceVersion
DDMStorageLink
DDMStructure
DDMStructureLayout
DDMStructureLink
DDMStructureVersion
DDMTemplate
DDMTemplateLink
DDMTemplateVersion

DDMContent
DDMDataProviderInstance
DDMDataProviderInstanceLink
DDMFormInstance
DDMFormInstanceRecord
DDMFormInstanceRecordVersion
DDMFormInstanceVersion
DDMStorageLink
DDMStructure
DDMStructureLayout
DDMStructureLink
DDMStructureVersion
DDMTemplate
DDMTemplateLink
DDMTemplateVersion

Fragment

FragmentCollection
FragmentEntry
FragmentEntryLink

FragmentCollection
FragmentEntry
FragmentEntryLink

Journal

JournalArticle
JournalArticleLocalization
JournalArticleResource
JournalContentSearch
JournalFeed
JournalFolder

JournalArticle
JournalArticleLocalization
JournalArticleResource
JournalContentSearch
JournalFeed
JournalFolder

Knowledge Base

KBArticle
KBComment
KBFolder
KBTemplate

KBArticle
KBComment
KBFolder
KBTemplate

Layout Page Template

LayoutPageTemplateCollection
LayoutPageTemplateEntry
LayoutPageTemplateStructure
LayoutPageTemplateStructureRel

LayoutPageTemplateCollection
LayoutPageTemplateEntry
LayoutPageTemplateStructure
LayoutPageTemplateStructureRel

Mobile Device Rules

MDRAction
MDRRule
MDRRuleGroup
MDRRuleGroupInstance

MDRAction
MDRRule
MDRRuleGroup
MDRRuleGroupInstance

Segments

SegmentsEntry
SegmentsEntryRel
SegmentsExperience
SegmentsExperiment
SegmentsExperimentRel

SegmentsEntry
SegmentsEntryRel
SegmentsExperience
SegmentsExperiment
SegmentsExperimentRel

Site

SiteFriendlyURL

SiteFriendlyURL

Site Navigation

SiteNavigationMenu
SiteNavigationMenuItem

SiteNavigationMenu
SiteNavigationMenuItem

Trash

TrashEntry
TrashVersion

TrashEntry
TrashVersion

Wiki

WikiNode
WikiPage
WikiPageResource

WikiNode
WikiPage
WikiPageResource

Was this article helpful?
0 out of 0 found this helpful