HHH-6562 - Unknown collection role when accessing ElementCollection in Embeddable via Criteria API
This commit is contained in:
parent
20c2037529
commit
774a16c2d9
|
@ -11,9 +11,12 @@ import javax.persistence.metamodel.Attribute;
|
|||
import javax.persistence.metamodel.Bindable;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import javax.persistence.metamodel.IdentifiableType;
|
||||
import javax.persistence.metamodel.MappedSuperclassType;
|
||||
import javax.persistence.metamodel.PluralAttribute;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.persistence.metamodel.Type;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
|
||||
import org.hibernate.query.criteria.internal.PathSource;
|
||||
|
@ -44,39 +47,73 @@ public class PluralAttributePath<X> extends AbstractPathImpl<X> implements Seria
|
|||
}
|
||||
|
||||
private String resolveRole(PluralAttribute attribute) {
|
||||
Class<?> roleOwnerType = attribute.getDeclaringType().getJavaType();
|
||||
if ( attribute.getDeclaringType().getPersistenceType() == Type.PersistenceType.MAPPED_SUPERCLASS ) {
|
||||
// the attribute is declared in a mappedsuperclass
|
||||
if ( getPathSource().getModel().getBindableType() == Bindable.BindableType.ENTITY_TYPE ) {
|
||||
// the role will be assigned to the "nearest" EnityType subclass of the MappedSuperclassType
|
||||
EntityType entityTypeNearestDeclaringType = (EntityType) getPathSource().getModel();
|
||||
IdentifiableType superType = entityTypeNearestDeclaringType.getSupertype();
|
||||
IdentifiableType previousType = entityTypeNearestDeclaringType;
|
||||
while ( superType != attribute.getDeclaringType() ) {
|
||||
if ( superType == null ) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Cannot determine nearest EntityType extending mapped superclass [%s]; [%s] extends [%s], but supertype of [%s] is null",
|
||||
attribute.getDeclaringType().getJavaType().getName(),
|
||||
( (EntityType) getPathSource().getModel() ).getJavaType().getName(),
|
||||
previousType.getJavaType().getName(),
|
||||
previousType.getJavaType().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
if ( superType.getPersistenceType() == Type.PersistenceType.ENTITY ) {
|
||||
entityTypeNearestDeclaringType = (EntityType) superType;
|
||||
}
|
||||
previousType = superType;
|
||||
superType = superType.getSupertype();
|
||||
}
|
||||
roleOwnerType = entityTypeNearestDeclaringType.getJavaType();
|
||||
switch ( attribute.getDeclaringType().getPersistenceType() ) {
|
||||
case ENTITY: {
|
||||
return attribute.getDeclaringType().getJavaType().getName() + '.' + attribute.getName();
|
||||
}
|
||||
// else throw an exception?
|
||||
case MAPPED_SUPERCLASS: {
|
||||
// the attribute is declared in a mappedsuperclass
|
||||
if ( getPathSource().getModel().getBindableType() == Bindable.BindableType.ENTITY_TYPE ) {
|
||||
// the role will be assigned to the "nearest" EnityType subclass of the MappedSuperclassType
|
||||
final EntityType entityTypeNearestDeclaringType = locateNearestSubclassEntity(
|
||||
(MappedSuperclassType) attribute.getDeclaringType(),
|
||||
(EntityType) getPathSource().getModel()
|
||||
);
|
||||
return entityTypeNearestDeclaringType.getJavaType().getName() + '.' + attribute.getName();
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure(
|
||||
String.format(
|
||||
"Unexpected BindableType; expected [%s]; instead got [%s]",
|
||||
Bindable.BindableType.ENTITY_TYPE,
|
||||
getPathSource().getModel().getBindableType()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
case EMBEDDABLE: {
|
||||
// initialize role to '.' + <plural_attribute_name>
|
||||
StringBuilder role = new StringBuilder().append( '.' ).append( attribute.getName() );
|
||||
PathSource parentPath = getPathSource();
|
||||
SingularAttribute singularAttribute;
|
||||
do {
|
||||
final SingularAttributePath singularAttributePath = (SingularAttributePath) parentPath;
|
||||
singularAttribute = singularAttributePath.getAttribute();
|
||||
// insert '.' + <parent_embeddable_attribute_name> at start of role
|
||||
role.insert( 0, '.' );
|
||||
role.insert( 1, singularAttributePath.getAttribute().getName() );
|
||||
parentPath = singularAttributePath.getPathSource();
|
||||
} while ( ( SingularAttributePath.class.isInstance( parentPath ) ) );
|
||||
final EntityType entityType;
|
||||
if ( singularAttribute.getDeclaringType().getPersistenceType() == Type.PersistenceType.ENTITY ) {
|
||||
entityType = (EntityType) singularAttribute.getDeclaringType();
|
||||
}
|
||||
else if ( singularAttribute.getDeclaringType().getPersistenceType() == Type.PersistenceType.MAPPED_SUPERCLASS ){
|
||||
// find the "nearest" EnityType subclass of the MappedSuperclassType
|
||||
entityType = locateNearestSubclassEntity(
|
||||
(MappedSuperclassType) singularAttribute.getDeclaringType(),
|
||||
(EntityType) parentPath.getModel()
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure(
|
||||
String.format(
|
||||
"Unexpected PersistenceType: [%s]",
|
||||
singularAttribute.getDeclaringType().getPersistenceType()
|
||||
)
|
||||
);
|
||||
}
|
||||
// insert <entity_name> at start of role
|
||||
return role.insert( 0, entityType.getJavaType().getName() ).toString();
|
||||
}
|
||||
default:
|
||||
throw new AssertionFailure(
|
||||
String.format(
|
||||
"Unexpected PersistenceType: [%s]",
|
||||
attribute.getDeclaringType().getPersistenceType()
|
||||
)
|
||||
);
|
||||
}
|
||||
// TODO: still need to deal with a plural attribute declared in an embeddable (HHH-6562)
|
||||
return roleOwnerType.getName() +
|
||||
'.' + attribute.getName();
|
||||
}
|
||||
|
||||
public PluralAttribute<?,X,?> getAttribute() {
|
||||
|
@ -115,4 +152,26 @@ public class PluralAttributePath<X> extends AbstractPathImpl<X> implements Seria
|
|||
+ attribute.getName() + "] cannot be dereferenced"
|
||||
);
|
||||
}
|
||||
|
||||
private EntityType locateNearestSubclassEntity(MappedSuperclassType mappedSuperclassType, EntityType entityTypeTop) {
|
||||
EntityType entityTypeNearestDeclaringType = entityTypeTop;
|
||||
IdentifiableType superType = entityTypeNearestDeclaringType.getSupertype();
|
||||
while ( superType != mappedSuperclassType ) {
|
||||
if ( superType == null ) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Cannot determine nearest EntityType extending mapped superclass [%s] starting from [%s]; a supertype of [%s] is null",
|
||||
mappedSuperclassType.getJavaType().getName(),
|
||||
entityTypeTop.getJavaType().getName(),
|
||||
entityTypeTop.getJavaType().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
if ( superType.getPersistenceType() == Type.PersistenceType.ENTITY ) {
|
||||
entityTypeNearestDeclaringType = (EntityType) superType;
|
||||
}
|
||||
superType = superType.getSupertype();
|
||||
}
|
||||
return entityTypeNearestDeclaringType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* 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.criteria.components;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Root;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.transaction.TransactionUtil;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-6562")
|
||||
public class ComponentInWhereClauseTest extends BaseEntityManagerFunctionalTestCase {
|
||||
private Projects projects;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {Employee.class, Project.class, Person.class};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
projects = new Projects();
|
||||
projects.addPreviousProject( new Project( "First" ) );
|
||||
projects.addPreviousProject( new Project( "Second" ) );
|
||||
projects.setCurrentProject( new Project( "Third" ) );
|
||||
|
||||
ContactDetail contactDetail = new ContactDetail();
|
||||
contactDetail.setEmail( "abc@mail.org" );
|
||||
contactDetail.addPhone( new Phone( "+4411111111" ) );
|
||||
|
||||
final Employee employee = new Employee();
|
||||
employee.setProjects( projects );
|
||||
employee.setContactDetail( contactDetail );
|
||||
entityManager.persist( employee );
|
||||
|
||||
final Person person = new Person();
|
||||
person.setInformation( new Information() );
|
||||
ContactDetail infoContactDetail = new ContactDetail();
|
||||
infoContactDetail.setEmail( "xyz@mail.org" );
|
||||
infoContactDetail.addPhone( new Phone( "999-999-9999" ) );
|
||||
person.getInformation().setInfoContactDetail( infoContactDetail );
|
||||
entityManager.persist( person );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheOneToManyPropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
|
||||
Root<Employee> root = query.from( Employee.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "projects" ).get( "previousProjects" ) )
|
||||
, 2 ) );
|
||||
|
||||
final List<Employee> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheElementCollectionPropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
|
||||
Root<Employee> root = query.from( Employee.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "contactDetail" ).get( "phones" ) )
|
||||
, 1 )
|
||||
);
|
||||
|
||||
final List<Employee> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheElementCollectionPropertyOfASubComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Person> query = builder.createQuery( Person.class );
|
||||
Root<Person> root = query.from( Person.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "information" ).get( "infoContactDetail" ).get( "phones" ) )
|
||||
, 1 )
|
||||
);
|
||||
|
||||
final List<Person> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualExpressionForThePropertyOfTheElementCollectionPropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
|
||||
Root<Employee> root = query.from( Employee.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
root.join( "contactDetail" ).join( "phones" ).get( "number" )
|
||||
, "+4411111111" )
|
||||
);
|
||||
|
||||
final List<Employee> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualityForThePropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
|
||||
Root<Employee> root = query.from( Employee.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
root.join( "contactDetail" ).get( "email" )
|
||||
, "abc@mail.org"
|
||||
)
|
||||
);
|
||||
|
||||
final List<Employee> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInExpressionForAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
|
||||
Root<Employee> root = query.from( Employee.class );
|
||||
|
||||
query.where( root.get( "projects" ).in( projects, new Projects() ) );
|
||||
|
||||
final List<Employee> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInExpressionForTheManyToOnePropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
|
||||
Root<Employee> root = query.from( Employee.class );
|
||||
|
||||
query.where( root.get( "projects" )
|
||||
.get( "currentProject" )
|
||||
.in( projects.getCurrentProject() ) );
|
||||
|
||||
final List<Employee> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static abstract class AbstractEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
protected Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Employee")
|
||||
@Table(name = "EMPLOYEE")
|
||||
public static class Employee extends AbstractEntity {
|
||||
|
||||
@Embedded
|
||||
private Projects projects;
|
||||
|
||||
@Embedded
|
||||
private ContactDetail contactDetail;
|
||||
|
||||
public void setProjects(Projects projects) {
|
||||
this.projects = projects;
|
||||
}
|
||||
|
||||
public void setContactDetail(ContactDetail contactDetail) {
|
||||
this.contactDetail = contactDetail;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class ContactDetail {
|
||||
private String email;
|
||||
|
||||
@ElementCollection
|
||||
private List<Phone> phones = new ArrayList<>();
|
||||
|
||||
public void addPhone(Phone phone) {
|
||||
this.phones.add( phone );
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Projects {
|
||||
|
||||
@OneToMany(cascade = CascadeType.PERSIST)
|
||||
private Set<Project> previousProjects = new HashSet<>();
|
||||
|
||||
@ManyToOne(cascade = CascadeType.PERSIST)
|
||||
private Project currentProject;
|
||||
|
||||
public void setCurrentProject(Project project) {
|
||||
this.currentProject = project;
|
||||
}
|
||||
|
||||
public void addPreviousProject(Project project) {
|
||||
this.previousProjects.add( project );
|
||||
}
|
||||
|
||||
public Set<Project> getPreviousProjects() {
|
||||
return previousProjects;
|
||||
}
|
||||
|
||||
public Project getCurrentProject() {
|
||||
return currentProject;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Project")
|
||||
@Table(name = "PROJECT")
|
||||
public static class Project extends AbstractEntity {
|
||||
|
||||
public Project() {
|
||||
}
|
||||
|
||||
public Project(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Phone {
|
||||
private String number;
|
||||
|
||||
public Phone() {
|
||||
}
|
||||
|
||||
public Phone(String number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return this.number;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@Table(name="PERSON")
|
||||
public static class Person extends AbstractEntity {
|
||||
@Embedded
|
||||
private Information information;
|
||||
|
||||
public Information getInformation() {
|
||||
return information;
|
||||
}
|
||||
|
||||
public void setInformation(Information information) {
|
||||
this.information = information;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Information {
|
||||
@Embedded
|
||||
private ContactDetail infoContactDetail;
|
||||
|
||||
public ContactDetail getInfoContactDetail() {
|
||||
return infoContactDetail;
|
||||
}
|
||||
|
||||
public void setInfoContactDetail(ContactDetail infoContactDetail) {
|
||||
this.infoContactDetail = infoContactDetail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* 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.criteria.components;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.transaction.TransactionUtil;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-6562")
|
||||
public class EntitySuperclassComponentWithCollectionTest extends BaseEntityManagerFunctionalTestCase {
|
||||
private Projects projects;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {Employee.class, Manager.class, Project.class, Person.class, Leader.class};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
projects = new Projects();
|
||||
projects.addPreviousProject( new Project( "First" ) );
|
||||
projects.addPreviousProject( new Project( "Second" ) );
|
||||
projects.setCurrentProject( new Project( "Third" ) );
|
||||
|
||||
ContactDetail contactDetail = new ContactDetail();
|
||||
contactDetail.setEmail( "abc@mail.org" );
|
||||
contactDetail.addPhone( new Phone( "+4411111111" ) );
|
||||
|
||||
final Manager manager = new Manager();
|
||||
manager.setProjects( projects );
|
||||
manager.setContactDetail( contactDetail );
|
||||
entityManager.persist( manager );
|
||||
|
||||
final Leader leader = new Leader();
|
||||
leader.setInformation( new Information() );
|
||||
ContactDetail infoContactDetail = new ContactDetail();
|
||||
infoContactDetail.setEmail( "xyz@mail.org" );
|
||||
infoContactDetail.addPhone( new Phone( "999-999-9999" ) );
|
||||
leader.getInformation().setInfoContactDetail( infoContactDetail );
|
||||
entityManager.persist( leader );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheOneToManyPropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Manager> query = builder.createQuery( Manager.class );
|
||||
Root<Manager> root = query.from( Manager.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "projects" ).get( "previousProjects" ) )
|
||||
, 2 ) );
|
||||
|
||||
final List<Manager> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheElementCollectionPropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Manager> query = builder.createQuery( Manager.class );
|
||||
Root<Manager> root = query.from( Manager.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "contactDetail" ).get( "phones" ) )
|
||||
, 1 )
|
||||
);
|
||||
|
||||
final List<Manager> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheElementCollectionPropertyOfASubComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Leader> query = builder.createQuery( Leader.class );
|
||||
Root<Leader> root = query.from( Leader.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "information" ).get( "infoContactDetail" ).get( "phones" ) )
|
||||
, 1 )
|
||||
);
|
||||
|
||||
final List<Leader> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static abstract class AbstractEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
protected Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Employee")
|
||||
@Table(name = "EMPLOYEE")
|
||||
public static class Employee extends AbstractEntity {
|
||||
|
||||
@Embedded
|
||||
private Projects projects;
|
||||
|
||||
@Embedded
|
||||
private ContactDetail contactDetail;
|
||||
|
||||
public void setProjects(Projects projects) {
|
||||
this.projects = projects;
|
||||
}
|
||||
|
||||
public void setContactDetail(ContactDetail contactDetail) {
|
||||
this.contactDetail = contactDetail;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Manager")
|
||||
@Table(name = "MANAGER")
|
||||
public static class Manager extends Employee {
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class ContactDetail {
|
||||
private String email;
|
||||
|
||||
@ElementCollection
|
||||
private List<Phone> phones = new ArrayList<>();
|
||||
|
||||
public void addPhone(Phone phone) {
|
||||
this.phones.add( phone );
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Projects {
|
||||
|
||||
@OneToMany(cascade = CascadeType.PERSIST)
|
||||
private Set<Project> previousProjects = new HashSet<>();
|
||||
|
||||
@ManyToOne(cascade = CascadeType.PERSIST)
|
||||
private Project currentProject;
|
||||
|
||||
public void setCurrentProject(Project project) {
|
||||
this.currentProject = project;
|
||||
}
|
||||
|
||||
public void addPreviousProject(Project project) {
|
||||
this.previousProjects.add( project );
|
||||
}
|
||||
|
||||
public Set<Project> getPreviousProjects() {
|
||||
return previousProjects;
|
||||
}
|
||||
|
||||
public Project getCurrentProject() {
|
||||
return currentProject;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Project")
|
||||
@Table(name = "PROJECT")
|
||||
public static class Project extends AbstractEntity {
|
||||
|
||||
public Project() {
|
||||
}
|
||||
|
||||
public Project(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Phone {
|
||||
private String number;
|
||||
|
||||
public Phone() {
|
||||
}
|
||||
|
||||
public Phone(String number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return this.number;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@Table(name="PERSON")
|
||||
public static class Person extends AbstractEntity {
|
||||
@Embedded
|
||||
private Information information;
|
||||
|
||||
public Information getInformation() {
|
||||
return information;
|
||||
}
|
||||
|
||||
public void setInformation(Information information) {
|
||||
this.information = information;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name="Leader")
|
||||
@Table(name="LEADER")
|
||||
public static class Leader extends Person {
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Information {
|
||||
@Embedded
|
||||
private ContactDetail infoContactDetail;
|
||||
|
||||
public ContactDetail getInfoContactDetail() {
|
||||
return infoContactDetail;
|
||||
}
|
||||
|
||||
public void setInfoContactDetail(ContactDetail infoContactDetail) {
|
||||
this.infoContactDetail = infoContactDetail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* 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.criteria.components;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.transaction.TransactionUtil;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-6562")
|
||||
public class MappedSuperclassComponentWithCollectionTest extends BaseEntityManagerFunctionalTestCase {
|
||||
private Projects projects;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {Employee.class, Manager.class, Project.class, Person.class, Leader.class};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
projects = new Projects();
|
||||
projects.addPreviousProject( new Project( "First" ) );
|
||||
projects.addPreviousProject( new Project( "Second" ) );
|
||||
projects.setCurrentProject( new Project( "Third" ) );
|
||||
|
||||
ContactDetail contactDetail = new ContactDetail();
|
||||
contactDetail.setEmail( "abc@mail.org" );
|
||||
contactDetail.addPhone( new Phone( "+4411111111" ) );
|
||||
|
||||
final Manager manager = new Manager();
|
||||
manager.setProjects( projects );
|
||||
manager.setContactDetail( contactDetail );
|
||||
entityManager.persist( manager );
|
||||
|
||||
final Leader leader = new Leader();
|
||||
leader.setInformation( new Information() );
|
||||
ContactDetail infoContactDetail = new ContactDetail();
|
||||
infoContactDetail.setEmail( "xyz@mail.org" );
|
||||
infoContactDetail.addPhone( new Phone( "999-999-9999" ) );
|
||||
leader.getInformation().setInfoContactDetail( infoContactDetail );
|
||||
entityManager.persist( leader );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheOneToManyPropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Manager> query = builder.createQuery( Manager.class );
|
||||
Root<Manager> root = query.from( Manager.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "projects" ).get( "previousProjects" ) )
|
||||
, 2 ) );
|
||||
|
||||
final List<Manager> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheElementCollectionPropertyOfAComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Manager> query = builder.createQuery( Manager.class );
|
||||
Root<Manager> root = query.from( Manager.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "contactDetail" ).get( "phones" ) )
|
||||
, 1 )
|
||||
);
|
||||
|
||||
final List<Manager> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeExpressionForTheElementCollectionPropertyOfASubComponent() {
|
||||
TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Leader> query = builder.createQuery( Leader.class );
|
||||
Root<Leader> root = query.from( Leader.class );
|
||||
|
||||
query.where(
|
||||
builder.equal(
|
||||
builder.size( root.get( "information" ).get( "infoContactDetail" ).get( "phones" ) )
|
||||
, 1 )
|
||||
);
|
||||
|
||||
final List<Leader> results = entityManager.createQuery( query ).getResultList();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static abstract class AbstractEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
protected Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class Employee extends AbstractEntity {
|
||||
|
||||
@Embedded
|
||||
private Projects projects;
|
||||
|
||||
@Embedded
|
||||
private ContactDetail contactDetail;
|
||||
|
||||
public void setProjects(Projects projects) {
|
||||
this.projects = projects;
|
||||
}
|
||||
|
||||
public void setContactDetail(ContactDetail contactDetail) {
|
||||
this.contactDetail = contactDetail;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Manager")
|
||||
@Table(name = "MANAGER")
|
||||
public static class Manager extends Employee {
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class ContactDetail {
|
||||
private String email;
|
||||
|
||||
@ElementCollection
|
||||
private List<Phone> phones = new ArrayList<>();
|
||||
|
||||
public void addPhone(Phone phone) {
|
||||
this.phones.add( phone );
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Projects {
|
||||
|
||||
@OneToMany(cascade = CascadeType.PERSIST)
|
||||
private Set<Project> previousProjects = new HashSet<>();
|
||||
|
||||
@ManyToOne(cascade = CascadeType.PERSIST)
|
||||
private Project currentProject;
|
||||
|
||||
public void setCurrentProject(Project project) {
|
||||
this.currentProject = project;
|
||||
}
|
||||
|
||||
public void addPreviousProject(Project project) {
|
||||
this.previousProjects.add( project );
|
||||
}
|
||||
|
||||
public Set<Project> getPreviousProjects() {
|
||||
return previousProjects;
|
||||
}
|
||||
|
||||
public Project getCurrentProject() {
|
||||
return currentProject;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Project")
|
||||
@Table(name = "PROJECT")
|
||||
public static class Project extends AbstractEntity {
|
||||
|
||||
public Project() {
|
||||
}
|
||||
|
||||
public Project(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Phone {
|
||||
private String number;
|
||||
|
||||
public Phone() {
|
||||
}
|
||||
|
||||
public Phone(String number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return this.number;
|
||||
}
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class Person extends AbstractEntity {
|
||||
@Embedded
|
||||
private Information information;
|
||||
|
||||
public Information getInformation() {
|
||||
return information;
|
||||
}
|
||||
|
||||
public void setInformation(Information information) {
|
||||
this.information = information;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
public static class Dummy1 extends Person {
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
public static class Dummy2 extends Dummy1 {
|
||||
}
|
||||
|
||||
@Entity(name="Leader")
|
||||
@Table(name="LEADER")
|
||||
public static class Leader extends Person {
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Information {
|
||||
@Embedded
|
||||
private ContactDetail infoContactDetail;
|
||||
|
||||
public ContactDetail getInfoContactDetail() {
|
||||
return infoContactDetail;
|
||||
}
|
||||
|
||||
public void setInfoContactDetail(ContactDetail infoContactDetail) {
|
||||
this.infoContactDetail = infoContactDetail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue