Skip to content

Commit

Permalink
HHH-17532 Fix NPE when load-collection is used for collection within …
Browse files Browse the repository at this point in the history
…embeddable
  • Loading branch information
beikov committed Dec 8, 2023
1 parent c74ab4b commit f4df70a
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -214,20 +214,23 @@ public static NativeSQLQueryReturn extractReturnDescription(
JaxbHbmNativeQueryCollectionLoadReturnType rtnSource,
HbmLocalMetadataBuildingContext context,
int queryReturnPosition) {
final int dot = rtnSource.getRole().lastIndexOf( '.' );
if ( dot == -1 ) {
throw new MappingException(
String.format(
Locale.ENGLISH,
"Collection attribute for sql query return [%s] not formatted correctly {OwnerClassName.propertyName}",
rtnSource.getAlias()
),
context.getOrigin()
);
PersistentClass entityBinding = null;
int dot = rtnSource.getRole().length();
while ( entityBinding == null ) {
dot = rtnSource.getRole().lastIndexOf( '.', dot - 1);
if ( dot == -1 ) {
throw new MappingException(
String.format(
Locale.ENGLISH,
"Collection attribute for sql query return [%s] not formatted correctly {OwnerClassName.propertyName}",
rtnSource.getAlias()
),
context.getOrigin()
);
}
entityBinding = context.findEntityBinding( null, rtnSource.getRole().substring( 0, dot ) );
}

String ownerClassName = context.findEntityBinding( null, rtnSource.getRole().substring( 0, dot ) )
.getClassName();
String ownerClassName = entityBinding.getClassName();
String ownerPropertyName = rtnSource.getRole().substring( dot + 1 );

return new NativeSQLQueryCollectionReturn(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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.loader.collectioninembedded;

import java.util.Iterator;
import java.util.Set;

import org.hibernate.ObjectNotFoundException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.resource.transaction.spi.TransactionStatus;

import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

@TestForIssue(jiraKey = "")
public class LoaderCollectionInEmbeddedTest extends BaseCoreFunctionalTestCase {
@Override
protected String[] getXmlFiles() {
return new String[] {
"org/hibernate/test/annotations/loader/collectioninembedded/Loader.hbm.xml"
};
}

@Override
protected Class[] getAnnotatedClasses() {
return new Class[]{
Player.class,
Team.class
};
}

@Test
public void testBasic() throws Exception {
// set up data...
Session s = openSession( );
Transaction tx = s.beginTransaction();
Team t = new Team();
Player p = new Player();
p.setName( "me" );
t.getDetails().getPlayers().add( p );
p.setTeam( t );
s.persist(p);
s.persist( t );
tx.commit();
s.close();

s = openSession();
tx = s.beginTransaction();
Team t2 = s.load( Team.class, t.getId() );
Set<Player> players = t2.getDetails().getPlayers();
Iterator<Player> iterator = players.iterator();
assertEquals( "me", iterator.next().getName() );
tx.commit();
s.close();

// clean up data
s = openSession();
tx = s.beginTransaction();
t = s.get( Team.class, t2.getId() );
p = s.get( Player.class, p.getId() );
s.delete( p );
s.delete( t );
tx.commit();
s.close();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.loader.collectioninembedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

@Entity
public class Player {

private Long id;
private Team team;
private String name;

@Id
@GeneratedValue
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

@ManyToOne(targetEntity = Team.class)
@Fetch(FetchMode.SELECT)
@JoinColumn(name = "team_id")
public Team getTeam() {
return team;
}

public void setTeam(Team team) {
this.team = team;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}
Original file line number Diff line number Diff line change
@@ -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.loader.collectioninembedded;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.Loader;

@Entity
public class Team {
private Long id;
private TeamDetails details = new TeamDetails();

@Id
@GeneratedValue
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public TeamDetails getDetails() {
return details;
}

public void setDetails(TeamDetails details) {
this.details = details;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.loader.collectioninembedded;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Embeddable;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.Loader;

@Embeddable
public class TeamDetails {

private Set<Player> players = new HashSet<Player>();

@OneToMany(targetEntity = Player.class, mappedBy = "team", fetch = FetchType.EAGER)
@Fetch(FetchMode.SELECT)
@Loader(namedQuery = "loadByTeam")
public Set<Player> getPlayers() {
return players;
}

public void setPlayers(Set<Player> players) {
this.players = players;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--
Used to demonstrate the declarative configuration
of both hbm files and annotated classes
See hibernate.cfg.xml and ConfigurationTest
-->

<hibernate-mapping package="org.hibernate.test.annotations.loader.collectioninembedded">

<sql-query name="loadByTeam">
<load-collection alias="p" role="org.hibernate.test.annotations.loader.collectioninembedded.Team.details.players"/>
select {p.*} from Player p where p.team_id = ?1
</sql-query>

</hibernate-mapping>

0 comments on commit f4df70a

Please sign in to comment.