HHH-6562 - Unknown collection role when accessing ElementCollection in Embeddable via Criteria API

This commit is contained in:
Andrea Boriero 2016-08-02 22:17:07 +02:00 committed by Gail Badner
parent 20c2037529
commit 774a16c2d9
4 changed files with 1025 additions and 31 deletions

View File

@ -11,9 +11,12 @@ import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Bindable; import javax.persistence.metamodel.Bindable;
import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.IdentifiableType; import javax.persistence.metamodel.IdentifiableType;
import javax.persistence.metamodel.MappedSuperclassType;
import javax.persistence.metamodel.PluralAttribute; import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type; import javax.persistence.metamodel.Type;
import org.hibernate.AssertionFailure;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl; import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.PathSource; 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) { private String resolveRole(PluralAttribute attribute) {
Class<?> roleOwnerType = attribute.getDeclaringType().getJavaType(); switch ( attribute.getDeclaringType().getPersistenceType() ) {
if ( attribute.getDeclaringType().getPersistenceType() == Type.PersistenceType.MAPPED_SUPERCLASS ) { case ENTITY: {
// the attribute is declared in a mappedsuperclass return attribute.getDeclaringType().getJavaType().getName() + '.' + attribute.getName();
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();
} }
// 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() { public PluralAttribute<?,X,?> getAttribute() {
@ -115,4 +152,26 @@ public class PluralAttributePath<X> extends AbstractPathImpl<X> implements Seria
+ attribute.getName() + "] cannot be dereferenced" + 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;
}
} }

View File

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

View File

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

View File

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