Skip to content

Commit 78d1a9f

Browse files
committed
HHH-7912 - Define edge-case behavior for Session.evict
(cherry picked from commit c8b2066)
1 parent 923002f commit 78d1a9f

File tree

6 files changed

+215
-75
lines changed

6 files changed

+215
-75
lines changed

hibernate-core/src/main/java/org/hibernate/Session.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,10 @@ public interface Session extends SharedSessionContract {
266266
* not be synchronized with the database. This operation cascades to associated
267267
* instances if the association is mapped with <tt>cascade="evict"</tt>.
268268
*
269-
* @param object a persistent instance
269+
* @param object The entity to evict
270+
*
271+
* @throws NullPointerException if the passed object is {@code null}
272+
* @throws IllegalArgumentException if the passed object is not defined as an entity
270273
*/
271274
public void evict(Object object);
272275

hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,22 @@ public class DefaultEvictEventListener implements EvictEventListener {
6262
* @throws HibernateException
6363
*/
6464
public void onEvict(EvictEvent event) throws HibernateException {
65-
EventSource source = event.getSession();
6665
final Object object = event.getObject();
66+
if ( object == null ) {
67+
throw new NullPointerException( "null passed to Session.evict()" );
68+
}
69+
70+
final EventSource source = event.getSession();
6771
final PersistenceContext persistenceContext = source.getPersistenceContext();
6872

6973
if ( object instanceof HibernateProxy ) {
70-
LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
71-
Serializable id = li.getIdentifier();
72-
EntityPersister persister = source.getFactory().getEntityPersister( li.getEntityName() );
74+
final LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
75+
final Serializable id = li.getIdentifier();
7376
if ( id == null ) {
74-
throw new IllegalArgumentException("null identifier");
77+
throw new IllegalArgumentException( "Could not determine identifier of proxy passed to evict()" );
7578
}
7679

80+
final EntityPersister persister = source.getFactory().getEntityPersister( li.getEntityName() );
7781
final EntityKey key = source.generateEntityKey( id, persister );
7882
persistenceContext.removeProxy( key );
7983

@@ -92,6 +96,23 @@ public void onEvict(EvictEvent event) throws HibernateException {
9296
persistenceContext.removeEntity( e.getEntityKey() );
9397
doEvict( object, e.getEntityKey(), e.getPersister(), source );
9498
}
99+
else {
100+
// see if the passed object is even an entity, and if not throw an exception
101+
// this is different than legacy Hibernate behavior, but what JPA 2.1 is calling for
102+
// with EntityManager.detach
103+
EntityPersister persister = null;
104+
final String entityName = persistenceContext.getSession().guessEntityName( object );
105+
if ( entityName != null ) {
106+
try {
107+
persister = persistenceContext.getSession().getFactory().getEntityPersister( entityName );
108+
}
109+
catch (Exception ignore) {
110+
}
111+
}
112+
if ( persister == null ) {
113+
throw new IllegalArgumentException( "Non-entity object instance passed to evict : " + object );
114+
}
115+
}
95116
}
96117
}
97118

hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1156,7 +1156,7 @@ private void fireReplicate(ReplicateEvent event) {
11561156

11571157
/**
11581158
* remove any hard references to the entity that are held by the infrastructure
1159-
* (references held by application or other persistant instances are okay)
1159+
* (references held by application or other persistent instances are okay)
11601160
*/
11611161
public void evict(Object object) throws HibernateException {
11621162
fireEvict( new EvictEvent( object, this ) );
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
5+
* indicated by the @author tags or express copyright attribution
6+
* statements applied by the authors. All third-party contributions are
7+
* distributed under license by Red Hat Inc.
8+
*
9+
* This copyrighted material is made available to anyone wishing to use, modify,
10+
* copy, or redistribute it subject to the terms and conditions of the GNU
11+
* Lesser General Public License, as published by the Free Software Foundation.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16+
* for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public License
19+
* along with this distribution; if not, write to:
20+
* Free Software Foundation, Inc.
21+
* 51 Franklin Street, Fifth Floor
22+
* Boston, MA 02110-1301 USA
23+
*/
24+
package org.hibernate.test.eviction;
25+
26+
import org.hibernate.Session;
27+
28+
import org.junit.Test;
29+
30+
import org.hibernate.testing.TestForIssue;
31+
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
32+
33+
import static org.junit.Assert.assertFalse;
34+
import static org.junit.Assert.assertTrue;
35+
import static org.junit.Assert.fail;
36+
37+
/**
38+
* @author Steve Ebersole
39+
*/
40+
public class EvictionTest extends BaseCoreFunctionalTestCase {
41+
@Override
42+
protected Class<?>[] getAnnotatedClasses() {
43+
return new Class[] { IsolatedEvictableEntity.class };
44+
}
45+
46+
@Test
47+
@TestForIssue( jiraKey = "HHH-7912" )
48+
public void testNormalUsage() {
49+
Session session = openSession();
50+
session.beginTransaction();
51+
session.save( new IsolatedEvictableEntity( 1 ) );
52+
session.getTransaction().commit();
53+
session.close();
54+
55+
session = openSession();
56+
session.beginTransaction();
57+
IsolatedEvictableEntity entity = (IsolatedEvictableEntity) session.get( IsolatedEvictableEntity.class, 1 );
58+
assertTrue( session.contains( entity ) );
59+
session.evict( entity );
60+
assertFalse( session.contains( entity ) );
61+
session.getTransaction().commit();
62+
session.close();
63+
64+
session = openSession();
65+
session.beginTransaction();
66+
session.delete( entity );
67+
session.getTransaction().commit();
68+
session.close();
69+
}
70+
71+
@Test
72+
@TestForIssue( jiraKey = "HHH-7912" )
73+
public void testEvictingNull() {
74+
Session session = openSession();
75+
session.beginTransaction();
76+
try {
77+
session.evict( null );
78+
fail( "Expecting evict(null) to throw NPE" );
79+
}
80+
catch (NullPointerException expected) {
81+
}
82+
session.getTransaction().commit();
83+
session.close();
84+
}
85+
86+
@Test
87+
@TestForIssue( jiraKey = "HHH-7912" )
88+
public void testEvictingTransientEntity() {
89+
Session session = openSession();
90+
session.beginTransaction();
91+
session.evict( new IsolatedEvictableEntity( 1 ) );
92+
session.getTransaction().commit();
93+
session.close();
94+
}
95+
96+
@Test
97+
@TestForIssue( jiraKey = "HHH-7912" )
98+
public void testEvictingDetachedEntity() {
99+
Session session = openSession();
100+
session.beginTransaction();
101+
session.save( new IsolatedEvictableEntity( 1 ) );
102+
session.getTransaction().commit();
103+
session.close();
104+
105+
session = openSession();
106+
session.beginTransaction();
107+
IsolatedEvictableEntity entity = (IsolatedEvictableEntity) session.get( IsolatedEvictableEntity.class, 1 );
108+
assertTrue( session.contains( entity ) );
109+
// detach the entity
110+
session.evict( entity );
111+
assertFalse( session.contains( entity ) );
112+
// evict it again the entity
113+
session.evict( entity );
114+
assertFalse( session.contains( entity ) );
115+
session.getTransaction().commit();
116+
session.close();
117+
118+
session = openSession();
119+
session.beginTransaction();
120+
session.delete( entity );
121+
session.getTransaction().commit();
122+
session.close();
123+
}
124+
125+
@Test
126+
@TestForIssue( jiraKey = "HHH-7912" )
127+
public void testEvictingNonEntity() {
128+
Session session = openSession();
129+
session.beginTransaction();
130+
try {
131+
session.evict( new EvictionTest() );
132+
fail( "Expecting evict(non-entity) to throw IAE" );
133+
}
134+
catch (IllegalArgumentException expected) {
135+
}
136+
session.getTransaction().commit();
137+
session.close();
138+
}
139+
140+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
5+
* indicated by the @author tags or express copyright attribution
6+
* statements applied by the authors. All third-party contributions are
7+
* distributed under license by Red Hat Inc.
8+
*
9+
* This copyrighted material is made available to anyone wishing to use, modify,
10+
* copy, or redistribute it subject to the terms and conditions of the GNU
11+
* Lesser General Public License, as published by the Free Software Foundation.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16+
* for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public License
19+
* along with this distribution; if not, write to:
20+
* Free Software Foundation, Inc.
21+
* 51 Franklin Street, Fifth Floor
22+
* Boston, MA 02110-1301 USA
23+
*/
24+
package org.hibernate.test.eviction;
25+
26+
import javax.persistence.Entity;
27+
import javax.persistence.Id;
28+
29+
/**
30+
* @author Steve Ebersole
31+
*/
32+
@Entity
33+
public class IsolatedEvictableEntity {
34+
@Id
35+
private Integer id;
36+
private String name;
37+
38+
public IsolatedEvictableEntity() {
39+
}
40+
41+
public IsolatedEvictableEntity(Integer id) {
42+
this.id = id;
43+
}
44+
}

hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/cascade/FetchTest.java

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -163,74 +163,6 @@ public void testTwoLevelDeepPersistOnManyToOne() throws Exception {
163163
em.close();
164164
}
165165

166-
@Test
167-
public void testPerfCascadeAndFetchEntity() throws Exception {
168-
EntityManager em = getOrCreateEntityManager();
169-
//init data
170-
em.getTransaction().begin();
171-
int loop = 50;
172-
for ( int i = 0; i < loop ; i++ ) {
173-
Troop disney = new Troop();
174-
disney.setName( "Disney" );
175-
Soldier mickey = new Soldier();
176-
mickey.setName( "Mickey" );
177-
disney.addSoldier( mickey );
178-
em.persist( disney );
179-
}
180-
em.getTransaction().commit();
181-
em.close();
182-
183-
//Warm up loop
184-
em = getOrCreateEntityManager();
185-
em.getTransaction().begin();
186-
for ( int i = 0; i < loop ; i++ ) {
187-
//Soldier soldier = em.find( Soldier.class, new Integer(i) );
188-
Troop troop = em.find( Troop.class, new Integer( i ) );
189-
//( ( HibernateEntityManager ) em ).getSession().evict(soldier);
190-
}
191-
long l11 = System.currentTimeMillis();
192-
Query query = em.createQuery( "SELECT count(t) FROM Soldier t" );
193-
query.getSingleResult();
194-
long l2 = System.currentTimeMillis();
195-
System.out.println( "Query1 " + ( l2 - l11 ) );
196-
em.getTransaction().commit();
197-
em.close();
198-
199-
//do not evict
200-
for ( int j = 0; j < 10 ; j++ ) {
201-
em = getOrCreateEntityManager();
202-
em.getTransaction().begin();
203-
for ( int i = 0; i < loop ; i++ ) {
204-
Troop troop = em.find( Troop.class, new Integer( i ) );
205-
( (HibernateEntityManager) em ).getSession().evict( troop );
206-
}
207-
l11 = System.currentTimeMillis();
208-
query = em.createQuery( "SELECT count(t) FROM Soldier t" );
209-
query.getSingleResult();
210-
l2 = System.currentTimeMillis();
211-
System.out.println( "Query " + ( l2 - l11 ) );
212-
em.getTransaction().commit();
213-
em.close();
214-
215-
//evict
216-
em = getOrCreateEntityManager();
217-
em.getTransaction().begin();
218-
for ( int i = 0; i < loop ; i++ ) {
219-
//Soldier soldier = em.find( Soldier.class, new Integer(i) );
220-
Troop troop = em.find( Troop.class, new Integer( i ) );
221-
222-
//( ( HibernateEntityManager ) em ).getSession().evict(troop);
223-
}
224-
l11 = System.currentTimeMillis();
225-
query = em.createQuery( "SELECT count(t) FROM Soldier t" );
226-
query.getSingleResult();
227-
l2 = System.currentTimeMillis();
228-
System.out.println( "Query " + ( l2 - l11 ) );
229-
em.getTransaction().commit();
230-
}
231-
em.close();
232-
}
233-
234166
@Override
235167
public Class[] getAnnotatedClasses() {
236168
return new Class[]{

0 commit comments

Comments
 (0)