HHH-9474 Fixing perfomance issue with ElementCollection

This commit is contained in:
Sergey Astakhov 2015-01-14 14:27:32 +03:00 committed by Sanne Grinovero
parent 536b814dc8
commit 1ddcc7075f
5 changed files with 269 additions and 3 deletions

View File

@ -683,9 +683,19 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
// the param would have to be bound twice. Until we eventually add "parameter bind points" concepts to the
// AST in ORM 5+, handling this type of condition is either extremely difficult or impossible. Forcing
// recreation isn't ideal, but not really any other option in ORM 4.
if ( persister.getElementType() instanceof CompositeType ) {
CompositeType componentType = (CompositeType) persister.getElementType();
return !componentType.hasNotNullProperty();
// Selecting a type used in where part of update statement
// (must match condidion in org.hibernate.persister.collection.BasicCollectionPersister.doUpdateRows).
// See HHH-9474
Type whereType;
if ( persister.hasIndex() ) {
whereType = persister.getIndexType();
}
else {
whereType = persister.getElementType();
}
if ( whereType instanceof CompositeType ) {
CompositeType componentIndexType = (CompositeType) whereType;
return !componentIndexType.hasNotNullProperty();
}
return false;
}

View File

@ -0,0 +1,51 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.annotations.collectionelement.recreate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
/**
* @author Sergey Astakhov
*/
@Entity
@GenericGenerator(name = "increment", strategy = "increment")
public class Poi {
@Id
@GeneratedValue
private Integer id;
private String name;
public Poi() {
}
public Poi(String _name) {
name = _name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String _name) {
name = _name;
}
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.annotations.collectionelement.recreate;
import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;
/**
* @author Sergey Astakhov
*/
@Embeddable
public class PoiArrival {
@Temporal(TemporalType.TIMESTAMP)
private Date expectedTime;
@Temporal(TemporalType.TIMESTAMP)
private Date arriveTime;
public Date getExpectedTime() {
return expectedTime;
}
public void setExpectedTime(Date _expectedTime) {
expectedTime = _expectedTime;
}
public Date getArriveTime() {
return arriveTime;
}
public void setArriveTime(Date _arriveTime) {
arriveTime = _arriveTime;
}
}

View File

@ -0,0 +1,77 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.annotations.collectionelement.recreate;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.*;
import org.hibernate.annotations.GenericGenerator;
/**
* @author Sergey Astakhov
*/
@Entity
@GenericGenerator(name = "increment", strategy = "increment")
public class RaceExecution {
@Id
@GeneratedValue
private Integer id;
@ElementCollection
@MapKeyClass(Poi.class)
@MapKeyJoinColumn(name = "poi", nullable = false)
@CollectionTable(name = "race_poi_arrival", joinColumns = @JoinColumn(name = "race_id"))
private Map<Poi, PoiArrival> poiArrival;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Map<Poi, PoiArrival> getPoiArrival() {
return poiArrival;
}
public void setPoiArrival(Map<Poi, PoiArrival> _poiArrival) {
poiArrival = _poiArrival;
}
public void arriveToPoi(Poi poi, Date time) {
if ( poiArrival == null ) {
poiArrival = new HashMap<Poi, PoiArrival>();
}
PoiArrival arrival = poiArrival.get( poi );
if ( arrival == null ) {
arrival = new PoiArrival();
poiArrival.put( poi, arrival );
}
arrival.setArriveTime( time );
}
public void expectedArrive(Poi poi, Date time) {
if ( poiArrival == null ) {
poiArrival = new HashMap<Poi, PoiArrival>();
}
PoiArrival arrival = poiArrival.get( poi );
if ( arrival == null ) {
arrival = new PoiArrival();
poiArrival.put( poi, arrival );
}
arrival.setExpectedTime( time );
}
}

View File

@ -0,0 +1,86 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.annotations.collectionelement.recreate;
import org.hibernate.BaseSessionEventListener;
import org.hibernate.Session;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import java.util.Date;
import static org.junit.Assert.assertEquals;
/**
* @author Sergey Astakhov
*/
public class RecreateCollectionTest extends BaseCoreFunctionalTestCase {
private static class StatementsCounterListener extends BaseSessionEventListener {
int statements;
@Override
public void jdbcExecuteStatementEnd() {
statements++;
}
}
@Test
@TestForIssue(jiraKey = "HHH-9474")
public void testUpdateCollectionOfElements() throws Exception {
Session s = openSession();
s.getTransaction().begin();
Poi poi1 = new Poi( "Poi 1" );
Poi poi2 = new Poi( "Poi 2" );
s.save( poi1 );
s.save( poi2 );
RaceExecution race = new RaceExecution();
s.save( race );
Date currentTime = new Date();
race.arriveToPoi( poi1, currentTime );
race.expectedArrive( poi2, new Date( currentTime.getTime() + 60 * 1000 ) );
s.flush();
assertEquals( 2, race.getPoiArrival().size() );
StatementsCounterListener statementsCounterListener = new StatementsCounterListener();
s.addEventListeners( statementsCounterListener );
race.arriveToPoi( poi2, new Date( currentTime.getTime() + 2 * 60 * 1000 ) );
s.flush();
assertEquals( 2, race.getPoiArrival().size() );
// There is should be one UPDATE statement. Without fix there is one DELETE and two INSERT-s.
assertEquals( 1, statementsCounterListener.statements );
s.getTransaction().rollback();
s.close();
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
Poi.class,
RaceExecution.class
};
}
}