You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@openjpa.apache.org by "Jody Grassel (JIRA)" <ji...@apache.org> on 2018/01/12 18:50:00 UTC

[jira] [Created] (OPENJPA-2726) Under certain conditions an Embeddable can be directly admitted into the Datacache map

Jody Grassel created OPENJPA-2726:
-------------------------------------

             Summary: Under certain conditions an Embeddable can be directly admitted into the Datacache map
                 Key: OPENJPA-2726
                 URL: https://issues.apache.org/jira/browse/OPENJPA-2726
             Project: OpenJPA
          Issue Type: Bug
          Components: kernel
    Affects Versions: 2.2.2
            Reporter: Jody Grassel
            Assignee: Jody Grassel
             Fix For: 2.2.3


A certain sequence of events can result in an Embeddable object instance, with a null identity, being admitted to the DataCache.  This has a number of consequences, ranging from errors by third party datacache implementations which cannot accept a null key value (which the native OpenJPA datacache impl can), to possible data integrity issues since a null key can refer to any kind of object type (since the regular key includes both
identity and identity type in its information.)

Consider the following entities:

@Entity
public class LeftHand {
    @Id private long id;   
    private String strData;
    @OneToMany private Collection<RightHand> rhList; 
    ...

@Entity
public class RightHand {
    @Id private long id; 
    @Basic private String strData;    
    @Embedded private EmbeddableData emb;
    ...
    
@Embeddable
public class EmbeddableData {
    @Basic private String embeddedString;   
    @Basic(fetch=FetchType.LAZY) private String lazyEmbeddedString;

After committing the above entities with filled data, the contents of the L2 datacache is as follows:

this	TestDataCache  (id=42)	
cm	ConcurrentDataCache$1  (id=43)	
	[0]	ConcurrentHashMap$Entry  (id=68)	
		key	LongId  (id=73)	
		value	DataCachePCDataImpl  (id=74)	
			_cache	"default" (id=85)	
			_data	Object[4]  (id=88)	
				[0]	Long  (id=102)	
				[1]	AbstractPCData$ProxyDataList  (id=104)	
				[2]	"left hand" (id=110)	
				[3]	Long  (id=111)	
			_exp	-1	
			_fieldImpl	null	
			_impl	null	
			_loaded	BitSet  (id=91)	
			_oid	LongId  (id=73)	
			_type	Class<T> (org.apache.openjpa.persistence.cache.jpa.model.LeftHand) (id=94)	
			_version	Integer  (id=99)	
	[1]	ConcurrentHashMap$Entry  (id=69)	
		key	LongId  (id=116)	
		value	DataCachePCDataImpl  (id=117)	
			_cache	"default" (id=85)	
			_data	Object[3]  (id=120)	
				[0]	DataCachePCDataImpl  (id=125)	
					_cache	"default" (id=85)	
					_data	Object[2]  (id=128)	
						[0]	"Embedded String" (id=130)	
						[1]	"Lazy String" (id=131)	
					_exp	-1	
					_fieldImpl	null	
					_impl	null	
					_loaded	BitSet  (id=129)	
					_oid	BrokerImpl$StateManagerId  (id=3611)	
					_type	Class<T> (org.apache.openjpa.persistence.cache.jpa.model.EmbeddableData) (id=2336)	
					_version	null	
				[1]	Long  (id=126)	
				[2]	"right hand" (id=127)	
			_exp	-1	
			_fieldImpl	null	
			_impl	null	
			_loaded	BitSet  (id=121)	
			_oid	LongId  (id=116)	
			_type	Class<T> (org.apache.openjpa.persistence.cache.jpa.model.RightHand) (id=122)	
			_version	null	

Here we see that the datacache contains two entries, one for LeftHand, one for RightHand.  Completely expected, and at this point Life is Good.

After purging the persistence context and L2 cache, a query "SELECT lh from LeftHand lh" is executed, and iterating through its result list yields the following L2 datacache state:

this	TestDataCache  (id=42)	
cm	ConcurrentDataCache$1  (id=43)	
	[0]	ConcurrentHashMap$Entry  (id=3657)	
		key	LongId  (id=3660)	
		value	DataCachePCDataImpl  (id=3661)	
			_cache	"default" (id=85)	
			_data	Object[4]  (id=3662)	
				[0]	Long  (id=3665)	
				[1]	null	
				[2]	"left hand" (id=3666)	
				[3]	Long  (id=111)	
			_exp	-1	
			_fieldImpl	null	
			_impl	null	
			_loaded	BitSet  (id=3667)	
			_oid	LongId  (id=3660)	
			_type	Class<T> (org.apache.openjpa.persistence.cache.jpa.model.LeftHand) (id=94)	
			_version	Long  (id=111)	
	[1]	ConcurrentHashMap$Entry  (id=3658)	
		key	null	
		value	DataCachePCDataImpl  (id=3670)	
			_cache	"default" (id=85)	
			_data	Object[2]  (id=3671)	
				[0]	"Embedded String" (id=3673)	
				[1]	"Lazy String" (id=3674)	
			_exp	-1	
			_fieldImpl	null	
			_impl	null	
			_loaded	BitSet  (id=3675)	
			_oid	null	
			_type	Class<T> (org.apache.openjpa.persistence.cache.jpa.model.EmbeddableData) (id=2336)	
			_version	null	
	[2]	ConcurrentHashMap$Entry  (id=3659)	
		key	LongId  (id=3682)	
		value	DataCachePCDataImpl  (id=3683)	
			_cache	"default" (id=85)	
			_data	Object[3]  (id=3686)	
				[0]	DataCachePCDataImpl  (id=3688)	
					_cache	"default" (id=85)	
					_data	Object[2]  (id=3691)	
						[0]	"Embedded String" (id=3673)	
						[1]	"Lazy String" (id=3674)	
					_exp	-1	
					_fieldImpl	null	
					_impl	null	
					_loaded	BitSet  (id=3694)	
					_oid	BrokerImpl$StateManagerId  (id=3695)	
					_type	Class<T> (org.apache.openjpa.persistence.cache.jpa.model.EmbeddableData) (id=2336)	
					_version	null	
				[1]	Long  (id=3689)	
				[2]	"right hand" (id=3690)	
			_exp	-1	
			_fieldImpl	null	
			_impl	null	
			_loaded	BitSet  (id=3687)	
			_oid	LongId  (id=3682)	
			_type	Class<T> (org.apache.openjpa.persistence.cache.jpa.model.RightHand) (id=122)	
			_version	null	

A specific sequence of events, requiring the embeddable to contain a lazy loaded field (which forces a ROPStoreManager.load() in the AbstractPCData.toEmbeddedData() path results in the DataCacheStoreManager.load() operation attempting to admit the embeddable into the Datacache as if it was an entity type (object ConcurrentHashMap$Entry(id=3658) at index [1].)  The embeddable has no identity of its own, so is inserted into the cache with a null key value.  The OpenJPA datacache impl replaces the null value with an object that represents the null value -- while other datacache implementations attempt to add the key to a regular map instance which results in a NullPointerException.

The attached patch looks for the attempt to insert a new (embeddable) item into the datacache at updateDataCache() - admission into the datacache is rejected if this condition is met.  I've further modified cacheStateManager() to reject any attempt to admit an entry into the datacache that has a null entity to guard against other unidentified paths which could lead to a similar issue, as a null key identity in a datacache is meaningless.  A unit test has been added to verify that the fix corrects the issue.




--
This message was sent by Atlassian JIRA
(v6.4.14#64029)