Skip to content

Commit

Permalink
HHH-9474 Fixing perfomance issue with ElementCollection
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeyastakhov authored and Sanne committed Jan 13, 2016
1 parent 536b814 commit 1ddcc70
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 3 deletions.
Expand Up @@ -683,9 +683,19 @@ public boolean needsRecreate(CollectionPersister persister) {
// 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).

This comment has been minimized.

Copy link
@gsmet

gsmet Jan 13, 2016

Member

s/condidion/condition/

// 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;
}
Expand Down
@@ -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;
}

}
@@ -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;
}

}
@@ -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 );
}

}
@@ -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
};
}

}

0 comments on commit 1ddcc70

Please sign in to comment.