HHH-9231 : Uncommitted data can remain in transactional collection cache after rollback if collection is initialized after flush
This commit is contained in:
parent
4851ab01df
commit
f9a49efd17
|
@ -128,6 +128,7 @@ public class BasicTransactionalTestCase extends AbstractFunctionalTestCase {
|
|||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session s = openSession();
|
||||
s.getTransaction().begin();
|
||||
SecondLevelCacheStatistics cStats = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
|
||||
Item loadedWithCachedCollection = (Item) s.load( Item.class, item.getId() );
|
||||
stats.logSummary();
|
||||
|
@ -136,6 +137,12 @@ public class BasicTransactionalTestCase extends AbstractFunctionalTestCase {
|
|||
assertEquals( 1, cStats.getHitCount() );
|
||||
Map cacheEntries = cStats.getEntries();
|
||||
assertEquals( 1, cacheEntries.size() );
|
||||
Item itemElement = loadedWithCachedCollection.getItems().iterator().next();
|
||||
itemElement.setOwner( null );
|
||||
loadedWithCachedCollection.getItems().clear();
|
||||
s.delete( itemElement );
|
||||
s.delete( loadedWithCachedCollection );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
return null;
|
||||
}
|
||||
|
@ -389,6 +396,91 @@ public class BasicTransactionalTestCase extends AbstractFunctionalTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNewManyToManyPropertyRefNoInitFlushInitLeaveCacheConsistent() throws Exception {
|
||||
Statistics stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
SecondLevelCacheStatistics cStats = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
|
||||
|
||||
OtherItem otherItem = null;
|
||||
Transaction txn = null;
|
||||
Session s = null;
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
s = openSession();
|
||||
txn = s.beginTransaction();
|
||||
otherItem = new OtherItem();
|
||||
otherItem.setName( "steve" );
|
||||
s.save( otherItem );
|
||||
txn.commit();
|
||||
s.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
|
||||
// create an element for otherItem.bagOfItems
|
||||
Item item = new Item();
|
||||
item.setName( "element" );
|
||||
item.setDescription( "element Item" );
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
s = openSession();
|
||||
txn = s.beginTransaction();
|
||||
otherItem = (OtherItem) s.get( OtherItem.class, otherItem.getId() );
|
||||
assertFalse( Hibernate.isInitialized( otherItem.getBagOfItems() ) );
|
||||
// Add an element to otherItem.bagOfItems (a bag); it will not initialize the bag.
|
||||
otherItem.addItemToBag( item );
|
||||
assertFalse( Hibernate.isInitialized( otherItem.getBagOfItems() ) );
|
||||
s.persist( item );
|
||||
s.flush();
|
||||
// Now initialize the collection; it will contain the uncommitted itemElement.
|
||||
// The many-to-many uses a property-ref
|
||||
Hibernate.initialize( otherItem.getBagOfItems() );
|
||||
setRollbackOnlyTx();
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTxExpected(e);
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
if ( s != null && s.isOpen() ) {
|
||||
try {
|
||||
s.close();
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
// cleanup
|
||||
s = openSession();
|
||||
txn = s.beginTransaction();
|
||||
otherItem = (OtherItem) s.get( OtherItem.class, otherItem.getId() );
|
||||
// Because of HHH-9231, the following will fail due to ObjectNotFoundException because the
|
||||
// collection will be read from the cache and it still contains the uncommitted element,
|
||||
// which cannot be found.
|
||||
Hibernate.initialize( otherItem.getBagOfItems() );
|
||||
assertTrue( otherItem.getBagOfItems().isEmpty() );
|
||||
s.delete( otherItem );
|
||||
txn.commit();
|
||||
s.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaleWritesLeaveCacheConsistent() throws Exception {
|
||||
Statistics stats = sessionFactory().getStatistics();
|
||||
|
|
|
@ -38,6 +38,7 @@ public class Item {
|
|||
private Set<Item> items = new HashSet<Item>( );
|
||||
private Item bagOwner;
|
||||
private List<Item> bagOfItems = new ArrayList<Item>( );
|
||||
private Set<OtherItem> otherItems = new HashSet<OtherItem>( );
|
||||
|
||||
public Item() {}
|
||||
|
||||
|
@ -111,4 +112,18 @@ public class Item {
|
|||
item.setBagOwner( this );
|
||||
getBagOfItems().add( item );
|
||||
}
|
||||
|
||||
public Set<OtherItem> getOtherItems() {
|
||||
return otherItems;
|
||||
}
|
||||
|
||||
public void setOtherItems(Set<OtherItem> otherItems) {
|
||||
this.otherItems = otherItems;
|
||||
}
|
||||
|
||||
public void addOtherItem(OtherItem otherItem) {
|
||||
getOtherItems().add( otherItem );
|
||||
otherItem.getBagOfItems().add( this );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
77
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/OtherItem.java
vendored
Executable file
77
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/OtherItem.java
vendored
Executable file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2014, Red Hat, Inc. and/or it's affiliates or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat, Inc. and/or it's affiliates.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.cache.infinispan.functional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
*/
|
||||
public class OtherItem {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Item favoriteItem;
|
||||
private List<Item> bagOfItems = new ArrayList<Item>();
|
||||
|
||||
public OtherItem() {
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Item getFavoriteItem() {
|
||||
return favoriteItem;
|
||||
}
|
||||
|
||||
public void setFavoriteItem(Item favoriteItem) {
|
||||
this.favoriteItem = favoriteItem;
|
||||
}
|
||||
|
||||
public List<Item> getBagOfItems() {
|
||||
return bagOfItems;
|
||||
}
|
||||
|
||||
public void setBagOfItems(List<Item> bagOfItems) {
|
||||
this.bagOfItems = bagOfItems;
|
||||
}
|
||||
|
||||
public void addItemToBag(Item item) {
|
||||
bagOfItems.add( item );
|
||||
item.getOtherItems().add( this );
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@
|
|||
<id name="id">
|
||||
<generator class="increment" />
|
||||
</id>
|
||||
<property name="name" not-null="true" />
|
||||
<property name="name" not-null="true" column="item_name" unique="true"/>
|
||||
<property name="description" not-null="true" />
|
||||
<many-to-one name="owner" column="owner_id" class="Item" />
|
||||
<many-to-one name="bagOwner" column="bagowner_id" class="Item" />
|
||||
|
@ -40,9 +40,27 @@
|
|||
<key column="bagowner_id" />
|
||||
<one-to-many class="Item" />
|
||||
</bag>
|
||||
<set name="otherItems" table="items_otheritems">
|
||||
<key property-ref="name" column="item_name"/>
|
||||
<many-to-many class="OtherItem" column="other_item_id"/>
|
||||
</set>
|
||||
</class>
|
||||
|
||||
<class name="VersionedItem" table="VersionedItems">
|
||||
|
||||
<class name="OtherItem" table="OtherItems">
|
||||
<id name="id">
|
||||
<generator class="increment" />
|
||||
</id>
|
||||
<many-to-one name="favoriteItem" property-ref="name"/>
|
||||
<property name="name" not-null="true" column="other_item_name" unique="true" />
|
||||
<bag name="bagOfItems" inverse="true" table="items_otheritems">
|
||||
<key column="other_item_id"/>
|
||||
<many-to-many class="Item" property-ref="name" column="item_name" />
|
||||
</bag>
|
||||
</class>
|
||||
|
||||
|
||||
<class name="VersionedItem" table="VersionedItems">
|
||||
<id name="id">
|
||||
<generator class="increment" />
|
||||
</id>
|
||||
|
|
Loading…
Reference in New Issue