Merge remote-tracking branch 'upstream/master' into wip/6.0_merge_47
This commit is contained in:
commit
4550c70d15
|
@ -16,9 +16,8 @@ buildscript {
|
|||
classpath 'gradle.plugin.com.github.lburgazzoli:gradle-karaf-plugin:0.5.1'
|
||||
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.7'
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
|
||||
classpath 'de.thetaphi:forbiddenapis:2.7'
|
||||
classpath 'de.thetaphi:forbiddenapis:3.0.1'
|
||||
classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.1'
|
||||
classpath 'biz.aQute.bnd:biz.aQute.bnd.gradle:4.2.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +26,7 @@ plugins {
|
|||
id 'org.hibernate.build.xjc' version '2.0.1' apply false
|
||||
id 'org.hibernate.build.maven-repo-auth' version '3.0.2' apply false
|
||||
id 'org.jetbrains.gradle.plugin.idea-ext' version '0.5'
|
||||
id 'biz.aQute.bnd' version '5.1.1' apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
|
|
@ -20,3 +20,9 @@ dependencies {
|
|||
compile 'org.hibernate.build.gradle:gradle-animalSniffer-plugin:1.0.1.Final'
|
||||
compile 'org.hibernate.build.gradle:hibernate-matrix-testing:3.0.0.Final'
|
||||
}
|
||||
|
||||
tasks.withType( GroovyCompile ) {
|
||||
options.encoding = 'UTF-8'
|
||||
sourceCompatibility = 8
|
||||
targetCompatibility = 8
|
||||
}
|
||||
|
|
|
@ -217,6 +217,14 @@ include::{sourcedir}/GraphFetchingTest.java[tags=fetching-strategies-dynamic-fet
|
|||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
When executing a JPQL query, if an EAGER association is omitted, Hibernate will issue a secondary select for every association needed to be fetched eagerly,
|
||||
which can lead to N+1 query issues.
|
||||
|
||||
For this reason, it's better to use LAZY associations, and only fetch them eagerly on a per-query basis.
|
||||
====
|
||||
|
||||
An EntityGraph is the root of a "load plan" and must correspond to an EntityType.
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'de.thetaphi:forbiddenapis:2.7'
|
||||
classpath 'de.thetaphi:forbiddenapis:3.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
@ -175,14 +175,9 @@ save () {
|
|||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
|
|
@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
|
|||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
|
|
|
@ -1362,7 +1362,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
private Object getParentsByChild(Object childEntity) {
|
||||
if ( parentsByChild != null ) {
|
||||
parentsByChild.get( childEntity );
|
||||
return parentsByChild.get( childEntity );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ public final class TwoPhaseLoad {
|
|||
* Add the "hydrated state" (an array) of an uninitialized entity to the session. We don't try
|
||||
* to resolve any associations yet, because there might be other entities waiting to be
|
||||
* read from the JDBC result set we are currently processing
|
||||
* @param persister The persister for the hydrated entity
|
||||
*
|
||||
* @param persister The persister for the hydrated entity
|
||||
* @param id The entity identifier
|
||||
* @param values The entity values
|
||||
* @param rowId The rowId for the entity
|
||||
|
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* 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.jpa.test.graphs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityGraph;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
* @author Nathan Xu
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-14097")
|
||||
public class LoadAndFetchGraphTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
AEntity.class,
|
||||
BEntity.class,
|
||||
CEntity.class,
|
||||
DEntity.class,
|
||||
EEntity.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( Environment.GENERATE_STATISTICS, "true" );
|
||||
super.addConfigOptions( options );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInJPA(
|
||||
this::entityManagerFactory, entityManager -> {
|
||||
AEntity a1 = new AEntity();
|
||||
a1.setId( 1 );
|
||||
a1.setLabel( "A1" );
|
||||
|
||||
AEntity a2 = new AEntity();
|
||||
a2.setId( 2 );
|
||||
a2.setLabel( "A2" );
|
||||
|
||||
entityManager.persist( a1 );
|
||||
entityManager.persist( a2 );
|
||||
|
||||
BEntity b1 = new BEntity();
|
||||
b1.setId( 1 );
|
||||
b1.setLabel( "B1" );
|
||||
|
||||
BEntity b2 = new BEntity();
|
||||
b2.setId( 2 );
|
||||
b2.setLabel( "B2" );
|
||||
|
||||
entityManager.persist( b1 );
|
||||
entityManager.persist( b2 );
|
||||
|
||||
EEntity e1 = new EEntity();
|
||||
e1.setId( 1 );
|
||||
e1.setLabel( "E1" );
|
||||
|
||||
EEntity e2 = new EEntity();
|
||||
e2.setId( 2 );
|
||||
e2.setLabel( "E2" );
|
||||
|
||||
EEntity e3 = new EEntity();
|
||||
e3.setId( 3 );
|
||||
e3.setLabel( "E3" );
|
||||
|
||||
EEntity e4 = new EEntity();
|
||||
e4.setId( 4 );
|
||||
e4.setLabel( "E4" );
|
||||
|
||||
entityManager.persist( e1 );
|
||||
entityManager.persist( e2 );
|
||||
entityManager.persist( e3 );
|
||||
entityManager.persist( e4 );
|
||||
|
||||
DEntity d1 = new DEntity();
|
||||
d1.setId( 1 );
|
||||
d1.setLabel( "D1" );
|
||||
d1.setE( e1 );
|
||||
|
||||
DEntity d2 = new DEntity();
|
||||
d2.setId( 2 );
|
||||
d2.setLabel( "D2" );
|
||||
d2.setE( e2 );
|
||||
|
||||
CEntity c1 = new CEntity();
|
||||
c1.setId( 1 );
|
||||
c1.setLabel( "C1" );
|
||||
c1.setA( a1 );
|
||||
c1.setB( b1 );
|
||||
c1.addD( d1 );
|
||||
c1.addD( d2 );
|
||||
|
||||
entityManager.persist( c1 );
|
||||
|
||||
DEntity d3 = new DEntity();
|
||||
d3.setId( 3 );
|
||||
d3.setLabel( "D3" );
|
||||
d3.setE( e3 );
|
||||
|
||||
DEntity d4 = new DEntity();
|
||||
d4.setId( 4 );
|
||||
d4.setLabel( "D4" );
|
||||
d4.setE( e4 );
|
||||
|
||||
CEntity c2 = new CEntity();
|
||||
c2.setId( 2 );
|
||||
c2.setLabel( "C2" );
|
||||
c2.setA( a2 );
|
||||
c2.setB( b2 );
|
||||
c2.addD( d3 );
|
||||
c2.addD( d4 );
|
||||
|
||||
entityManager.persist( c2 );
|
||||
|
||||
CEntity c3 = new CEntity();
|
||||
c3.setId( 3 );
|
||||
c3.setLabel( "C3" );
|
||||
|
||||
entityManager.persist( c3 );
|
||||
|
||||
c1.setC( c2 );
|
||||
c2.setC( c3 );
|
||||
|
||||
int id = 5;
|
||||
for ( int i = 0; i < 10; i++ ) {
|
||||
DEntity dn = new DEntity();
|
||||
dn.setId( id++ );
|
||||
dn.setLabel( "label" );
|
||||
dn.setE( e3 );
|
||||
entityManager.persist( dn );
|
||||
}
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryById() {
|
||||
Statistics statistics = entityManagerFactory().unwrap( SessionFactory.class ).getStatistics();
|
||||
statistics.clear();
|
||||
doInJPA(
|
||||
this::entityManagerFactory, entityManager -> {
|
||||
TypedQuery<CEntity> query = entityManager.createQuery(
|
||||
"select c from CEntity as c where c.id = :cid ",
|
||||
CEntity.class
|
||||
);
|
||||
query.setParameter( "cid", 1 );
|
||||
CEntity cEntity = query.getSingleResult();
|
||||
|
||||
assertFalse( Hibernate.isInitialized( cEntity.getA() ) );
|
||||
assertFalse( Hibernate.isInitialized( cEntity.getB() ) );
|
||||
assertFalse( Hibernate.isInitialized( cEntity.getC() ) );
|
||||
assertFalse( Hibernate.isInitialized( cEntity.getdList() ) );
|
||||
|
||||
assertEquals( 1L, statistics.getPrepareStatementCount() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryByIdWithLoadGraph() {
|
||||
Statistics statistics = entityManagerFactory().unwrap( SessionFactory.class ).getStatistics();
|
||||
statistics.clear();
|
||||
doInJPA(
|
||||
this::entityManagerFactory, entityManager -> {
|
||||
EntityGraph<CEntity> entityGraph = entityManager.createEntityGraph( CEntity.class );
|
||||
entityGraph.addAttributeNodes( "a", "b" );
|
||||
entityGraph.addSubgraph( "dList" ).addAttributeNodes( "e" );
|
||||
|
||||
TypedQuery<CEntity> query = entityManager.createQuery(
|
||||
"select c from CEntity as c where c.id = :cid ",
|
||||
CEntity.class
|
||||
);
|
||||
query.setHint( GraphSemantic.LOAD.getJpaHintName(), entityGraph );
|
||||
query.setParameter( "cid", 1 );
|
||||
|
||||
CEntity cEntity = query.getSingleResult();
|
||||
|
||||
assertTrue( Hibernate.isInitialized( cEntity.getA() ) );
|
||||
assertTrue( Hibernate.isInitialized( cEntity.getB() ) );
|
||||
assertFalse( Hibernate.isInitialized( cEntity.getC() ) );
|
||||
assertTrue( Hibernate.isInitialized( cEntity.getdList() ) );
|
||||
cEntity.getdList().forEach( dEntity -> {
|
||||
assertTrue( Hibernate.isInitialized( dEntity.getE() ) );
|
||||
} );
|
||||
|
||||
assertEquals( 1L, statistics.getPrepareStatementCount() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryByIdWithFetchGraph() {
|
||||
Statistics statistics = entityManagerFactory().unwrap( SessionFactory.class ).getStatistics();
|
||||
statistics.clear();
|
||||
doInJPA(
|
||||
this::entityManagerFactory, entityManager -> {
|
||||
EntityGraph<CEntity> entityGraph = entityManager.createEntityGraph( CEntity.class );
|
||||
entityGraph.addAttributeNodes( "a", "b" );
|
||||
entityGraph.addSubgraph( "dList" ).addAttributeNodes( "e" );
|
||||
|
||||
TypedQuery<CEntity> query = entityManager.createQuery(
|
||||
"select c from CEntity as c where c.id = :cid ",
|
||||
CEntity.class
|
||||
);
|
||||
query.setHint( GraphSemantic.FETCH.getJpaHintName(), entityGraph );
|
||||
query.setParameter( "cid", 1 );
|
||||
|
||||
CEntity cEntity = query.getSingleResult();
|
||||
assertTrue( Hibernate.isInitialized( cEntity.getA() ) );
|
||||
assertTrue( Hibernate.isInitialized( cEntity.getB() ) );
|
||||
assertFalse( Hibernate.isInitialized( cEntity.getC() ) );
|
||||
assertTrue( Hibernate.isInitialized( cEntity.getdList() ) );
|
||||
cEntity.getdList().forEach( dEntity -> {
|
||||
assertTrue( Hibernate.isInitialized( dEntity.getE() ) );
|
||||
} );
|
||||
|
||||
assertEquals( 1L, statistics.getPrepareStatementCount() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryByIdWithFetchGraph2() {
|
||||
Statistics statistics = entityManagerFactory().unwrap( SessionFactory.class ).getStatistics();
|
||||
statistics.clear();
|
||||
doInJPA(
|
||||
this::entityManagerFactory, entityManager -> {
|
||||
EntityGraph<CEntity> entityGraph = entityManager.createEntityGraph( CEntity.class );
|
||||
entityGraph.addSubgraph( "c" ).addAttributeNodes( "a" );
|
||||
|
||||
TypedQuery<CEntity> query = entityManager.createQuery(
|
||||
"select c from CEntity as c where c.id = :cid ",
|
||||
CEntity.class
|
||||
);
|
||||
query.setHint( GraphSemantic.FETCH.getJpaHintName(), entityGraph );
|
||||
query.setParameter( "cid", 1 );
|
||||
|
||||
CEntity cEntity = query.getSingleResult();
|
||||
assertTrue( Hibernate.isInitialized( cEntity.getC() ) );
|
||||
assertTrue( Hibernate.isInitialized( cEntity.getC().getA() ) );
|
||||
assertFalse( Hibernate.isInitialized( cEntity.getC().getC() ) );
|
||||
|
||||
assertEquals( 1L, statistics.getPrepareStatementCount() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "AEntity")
|
||||
@Table(name = "A")
|
||||
public static class AEntity {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String label;
|
||||
|
||||
@OneToMany(
|
||||
fetch = FetchType.LAZY,
|
||||
mappedBy = "a",
|
||||
cascade = CascadeType.ALL,
|
||||
orphanRemoval = true
|
||||
)
|
||||
private List<CEntity> cList = new ArrayList<>();
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "BEntity")
|
||||
@Table(name = "B")
|
||||
public static class BEntity {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String label;
|
||||
|
||||
@OneToMany(
|
||||
fetch = FetchType.LAZY,
|
||||
mappedBy = "b",
|
||||
cascade = CascadeType.ALL,
|
||||
orphanRemoval = true
|
||||
)
|
||||
private List<CEntity> cList = new ArrayList<>();
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "CEntity")
|
||||
@Table(name = "C")
|
||||
public static class CEntity {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String label;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "A_ID")
|
||||
private AEntity a;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "B_ID")
|
||||
private BEntity b;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private CEntity c;
|
||||
|
||||
@OneToMany(
|
||||
fetch = FetchType.LAZY,
|
||||
mappedBy = "c",
|
||||
cascade = CascadeType.ALL,
|
||||
orphanRemoval = true
|
||||
)
|
||||
private List<DEntity> dList = new ArrayList<>();
|
||||
|
||||
public void addD(DEntity d) {
|
||||
dList.add( d );
|
||||
d.setC( this );
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public AEntity getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(AEntity a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public BEntity getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB(BEntity b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public CEntity getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
public void setC(CEntity c) {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public List<DEntity> getdList() {
|
||||
return dList;
|
||||
}
|
||||
|
||||
public void setdList(List<DEntity> dList) {
|
||||
this.dList = dList;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "DEntity")
|
||||
@Table(name = "D")
|
||||
public static class DEntity {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String label;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "C_ID")
|
||||
private CEntity c;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "E_ID")
|
||||
private EEntity e;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public CEntity getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
public void setC(CEntity c) {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public EEntity getE() {
|
||||
return e;
|
||||
}
|
||||
|
||||
public void setE(EEntity e) {
|
||||
this.e = e;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "EEntity")
|
||||
@Table(name = "E")
|
||||
public static class EEntity {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String label;
|
||||
|
||||
@OneToMany(
|
||||
fetch = FetchType.LAZY,
|
||||
mappedBy = "e",
|
||||
cascade = CascadeType.ALL,
|
||||
orphanRemoval = true
|
||||
)
|
||||
private List<DEntity> dList = new ArrayList<>();
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* 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.batchfetch;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.*;
|
||||
|
||||
public class BatchFetchRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Test
|
||||
|
||||
public void testRefreshWithBatch() {
|
||||
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
|
||||
// Retrieve one of the parents into the session.
|
||||
Parent parent = session.find(Parent.class, 1);
|
||||
Assert.assertNotNull(parent);
|
||||
|
||||
// Retrieve children but keep their parents lazy!
|
||||
// This allows batch fetching to do its thing when we refresh below.
|
||||
session.createQuery( "FROM Child" ).getResultList();
|
||||
|
||||
session.refresh( parent, LockModeType.PESSIMISTIC_WRITE );
|
||||
|
||||
// Just something to force delazification of children on parent entity
|
||||
// The parent is obviously attached to the session (we just refreshed it!)
|
||||
parent.getChildren().size();
|
||||
|
||||
// Another interesting thing to note - em.getLockMode returns an incorrect value after the above refresh
|
||||
Assert.assertEquals( LockModeType.PESSIMISTIC_WRITE, session.getLockMode( parent ) );
|
||||
});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupData() {
|
||||
final int numParents = 5;
|
||||
final int childrenPerParent = 2;
|
||||
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
int k = 1;
|
||||
for ( int i = 1; i <= numParents; i++ ) {
|
||||
Parent parent = new Parent();
|
||||
parent.parentId = i;
|
||||
parent.name = "Parent_" + i;
|
||||
|
||||
session.persist( parent );
|
||||
|
||||
// Create some children for each parent...
|
||||
for ( int j = 0; j < childrenPerParent; j++,k++ ) {
|
||||
Child child = new Child();
|
||||
child.childId = k;
|
||||
child.name = "Child_" + i + "_" + j;
|
||||
child.age = 15;
|
||||
child.parent = parent;
|
||||
parent.getChildren().add( child );
|
||||
session.persist( child );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Parent.class,
|
||||
Child.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSettings(Map settings) {
|
||||
super.addSettings( settings );
|
||||
settings.put( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "8" );
|
||||
}
|
||||
|
||||
@Entity(name = "Parent")
|
||||
public static class Parent {
|
||||
|
||||
@Id
|
||||
@Column(name = "parent_id")
|
||||
private int parentId;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "parent")
|
||||
private Set<Child> children = new LinkedHashSet<>();
|
||||
|
||||
public int getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Child> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(Set<Child> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Child")
|
||||
public static class Child {
|
||||
|
||||
@Id
|
||||
@Column(name = "child_id")
|
||||
private int childId;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "parent_id")
|
||||
private Parent parent;
|
||||
|
||||
@Column(name = "age")
|
||||
private int age;
|
||||
|
||||
public int getChildId() {
|
||||
return childId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Parent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Parent parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.lazy.proxy.batch;
|
||||
|
||||
import java.util.UUID;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.hibernate.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.NoDirtyCheckEnhancementContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckEnhancementContext.class })
|
||||
public abstract class AbstractBatchingTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
protected String childName = UUID.randomUUID().toString();
|
||||
protected Long parentId;
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
ssrb.applySetting( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "100" );
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
sources.addAnnotatedClass( PaddedBatchingTest.ParentEntity.class );
|
||||
sources.addAnnotatedClass( PaddedBatchingTest.ChildEntity.class );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadParent() {
|
||||
StatisticsImplementor statistics = sessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
inTransaction(
|
||||
session -> {
|
||||
ParentEntity parentEntity = session.find( ParentEntity.class, parentId );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 1L ) );
|
||||
|
||||
ChildEntity childEntity = parentEntity.getChildEntity();
|
||||
|
||||
assertFalse( Hibernate.isPropertyInitialized( childEntity, "name" ) );
|
||||
assertEquals( childName, childEntity.getName() );
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 2L ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
PaddedBatchingTest.ParentEntity parent = new PaddedBatchingTest.ParentEntity();
|
||||
inTransaction(
|
||||
session -> {
|
||||
PaddedBatchingTest.ChildEntity childEntity = new PaddedBatchingTest.ChildEntity();
|
||||
|
||||
childEntity.setName( childName );
|
||||
|
||||
parent.setChildEntity( childEntity );
|
||||
session.persist( parent );
|
||||
}
|
||||
);
|
||||
parentId = parent.getId();
|
||||
}
|
||||
|
||||
@Entity(name = "ParentEntity")
|
||||
public static class ParentEntity {
|
||||
private Long id;
|
||||
|
||||
private ChildEntity childEntity;
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@JoinColumn(name = "FK_CHILD_ENTITY_ID", nullable = false)
|
||||
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
public ChildEntity getChildEntity() {
|
||||
return childEntity;
|
||||
}
|
||||
|
||||
public void setChildEntity(ChildEntity childEntity) {
|
||||
this.childEntity = childEntity;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "ChildEntity")
|
||||
public static class ChildEntity {
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.lazy.proxy.batch;
|
||||
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.loader.BatchFetchStyle;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-14108")
|
||||
public class DynamicBatchingTest extends AbstractBatchingTest {
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.BATCH_FETCH_STYLE, BatchFetchStyle.DYNAMIC.toString() );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.lazy.proxy.batch;
|
||||
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.loader.BatchFetchStyle;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-14108")
|
||||
public class PaddedBatchingTest extends AbstractBatchingTest {
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.BATCH_FETCH_STYLE, BatchFetchStyle.PADDED.toString() );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.inheritance;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@RequiresDialectFeature(DialectChecks.SupportsRowValueConstructorSyntaxInInListCheck.class)
|
||||
@TestForIssue(jiraKey = "HHH-13214")
|
||||
public class InheritanceDeleteBatchTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
TestEntity.class,
|
||||
TestEntityType1.class,
|
||||
TestEntityType2.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.setProperty(
|
||||
AvailableSettings.HQL_BULK_ID_STRATEGY,
|
||||
InlineIdsInClauseBulkIdStrategy.class.getName()
|
||||
);
|
||||
configuration.setProperty( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.persist( new TestEntity( 1 ) );
|
||||
session.persist( new TestEntityType1( 2 ) );
|
||||
session.persist( new TestEntityType2( 3 ) );
|
||||
session.persist( new TestEntityType2( 4 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete() {
|
||||
StatisticsImplementor statistics = sessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
for ( int i = 1; i <= 4; i++ ) {
|
||||
Query deleteQuery = session.createQuery( "delete TestEntity e where e.id = :id" );
|
||||
deleteQuery.setParameter( "id", i );
|
||||
deleteQuery.executeUpdate();
|
||||
assertThat( statistics.getPrepareStatementCount(), is( 4L ) );
|
||||
statistics.clear();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntity")
|
||||
@Inheritance(strategy = InheritanceType.JOINED)
|
||||
@Table(name = "test_entity")
|
||||
public static class TestEntity {
|
||||
@Id
|
||||
int id;
|
||||
|
||||
private String field;
|
||||
|
||||
public TestEntity() {
|
||||
}
|
||||
|
||||
public TestEntity(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntityType1")
|
||||
@Table(name = "test_entity_type1")
|
||||
public static class TestEntityType1 extends TestEntity {
|
||||
|
||||
public TestEntityType1(int id) {
|
||||
super( id );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntityType2")
|
||||
@Table(name = "test_entity_type2")
|
||||
public static class TestEntityType2 extends TestEntity {
|
||||
public TestEntityType2(int id) {
|
||||
super( id );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,3 +21,9 @@ dependencies {
|
|||
compile gradleApi()
|
||||
compile localGroovy()
|
||||
}
|
||||
|
||||
tasks.withType( GroovyCompile ) {
|
||||
options.encoding = 'UTF-8'
|
||||
sourceCompatibility = project.baselineJavaVersion
|
||||
targetCompatibility = project.baselineJavaVersion
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue