Skip to content

Commit

Permalink
jakartaee/persistence#457 - add union/intersect/except to JPQL and cr…
Browse files Browse the repository at this point in the history
…iteria

Jakarta Persistence 3.2.0-B02

Signed-off-by: Tomáš Kraus <[email protected]>
  • Loading branch information
Tomas-Kraus committed Nov 10, 2023
1 parent f56cd6a commit 7f7fab6
Show file tree
Hide file tree
Showing 16 changed files with 334 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2022 IBM Corporation.
* Copyright (c) 2010, 2022 Dies Koper (Fujitsu).
*
Expand Down Expand Up @@ -205,18 +205,20 @@ protected static FieldDefinition createNumericPk(final String name) {
/**
* Helper method to create {@link FieldDefinition} instance for
* numeric foreign key with given name, size and foreign key name.
* @param name Column name.
* @param size Column numeric type size.
* @param name Column name.
* @param size Column numeric type size.
* @param fkName Foreign key name (e.g. {@code "MY_TABLE.ID"}.
* @param allowNull Allow {@code null} values for column.
* @return Initialized {@link FieldDefinition} instance.
* @noinspection SameParameterValue
*/
protected static FieldDefinition createNumericFk(
final String name, final int size, final String fkName) {
final String name, final int size, final String fkName, final boolean allowNull) {
final FieldDefinition field = new FieldDefinition();
field.setName(name);
field.setTypeName("NUMERIC");
field.setSize(size);
field.setShouldAllowNull(false);
field.setShouldAllowNull(allowNull);
field.setIsPrimaryKey(false);
field.setUnique(false);
field.setIsIdentity(false);
Expand All @@ -229,11 +231,12 @@ protected static FieldDefinition createNumericFk(
* numeric foreign key with given name, foreign key name and default size
* of {@code 15}.
* @param name Column name.
* @param fkName Foreign key name (e.g. {@code "MY_TABLE.ID"}.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createNumericFk(
final String name, final String fkName) {
return createNumericFk(name, 15, fkName);
return createNumericFk(name, 15, fkName, false);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ public void addAttribute(String attributeNameOrPath, AttributeGroup group) {
super.addAttribute(attributeNameOrPath, group);
}

/**
* Remove an attribute from the group.
* @param attributeNameOrPath a simple attribute, array or attributes forming a path
*/
public void removeAttribute(String attributeNameOrPath) {
super.removeAttribute(attributeNameOrPath);
}

/**
* Returns AttributeGroup corresponding to the passed (possibly nested)
* attribute.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ protected ObjectLevelReadQuery() {
*/
public void union(ReportQuery query) {
addUnionExpression(getExpressionBuilder().union(query));
query.getArguments().forEach(this::addArgument);
copyQueryArguments(query);
}

/**
Expand All @@ -274,7 +274,7 @@ public void union(ReportQuery query) {
*/
public void unionAll(ReportQuery query) {
addUnionExpression(getExpressionBuilder().unionAll(query));
query.getArguments().forEach(this::addArgument);
copyQueryArguments(query);
}

/**
Expand All @@ -283,7 +283,7 @@ public void unionAll(ReportQuery query) {
*/
public void intersect(ReportQuery query) {
addUnionExpression(getExpressionBuilder().intersect(query));
query.getArguments().forEach(this::addArgument);
copyQueryArguments(query);
}

/**
Expand All @@ -292,7 +292,7 @@ public void intersect(ReportQuery query) {
*/
public void intersectAll(ReportQuery query) {
addUnionExpression(getExpressionBuilder().intersectAll(query));
query.getArguments().forEach(this::addArgument);
copyQueryArguments(query);
}

/**
Expand All @@ -301,7 +301,7 @@ public void intersectAll(ReportQuery query) {
*/
public void except(ReportQuery query) {
addUnionExpression(getExpressionBuilder().except(query));
query.getArguments().forEach(this::addArgument);
copyQueryArguments(query);
}

/**
Expand All @@ -310,7 +310,35 @@ public void except(ReportQuery query) {
*/
public void exceptAll(ReportQuery query) {
addUnionExpression(getExpressionBuilder().exceptAll(query));
query.getArguments().forEach(this::addArgument);
copyQueryArguments(query);
}

// Union/intersect/except methods helper
// Copy arguments, argumentTypes, argumentTypeNames and nullableArguments
private void copyQueryArguments(ReportQuery query) {
List<String> arguments = query.arguments;
List<Class<?>> argumentTypes = query.argumentTypes;
// Arguments and argumentTypes Lists shall have the same size
int size = arguments != null ? arguments.size() : 0;
for (int i = 0; i < size; i++) {
String argument = arguments.get(i);
// Mark as non-nullable to avoid unnecessary DatabaseField clone
addArgument(argument, argumentTypes.get(i));
// Copy nullable information for current argument if exists
if (query.nullableArguments != null) {
List <DatabaseField> nullableArguments = query.nullableArguments;
DatabaseField nullable = null;
for (DatabaseField nullableArgument : nullableArguments) {
if (nullableArgument.getQualifiedName().equals(argument)) {
nullable = nullableArgument;
break;
}
}
if (nullable != null) {
this.getNullableArguments().add(nullable);
}
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ public void copyReportItems(Map alreadyDone) {
if ((expression != null) && (alreadyDone.get(expression.getBuilder()) != null)) {
expression = expression.copiedVersionFrom(alreadyDone);
}
this.items.set(i, new ReportItem(item.getName(), expression));
this.items.set(i, (ReportItem) item.clone());
}
if (this.groupByExpressions != null) {
this.groupByExpressions = new ArrayList<>(this.groupByExpressions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,21 @@ public class Persistence32TableCreator extends TogglingFastTableCreator {

public Persistence32TableCreator() {
setName("Persistence32Project");
addTableDefinition(buildTrainerTable());
addTableDefinition(buildTypeTable());
addTableDefinition(buildPokemonTable());
addTableDefinition(buildPokemonTypeTable());
addTableDefinition(buildSyntaxEntityTable());
}

public static TableDefinition buildTrainerTable() {
TableDefinition table = new TableDefinition();
table.setName("PERSISTENCE32_TRAINER");
table.addField(createNumericPk("ID"));
table.addField(createStringColumn("NAME", 64, false));
return table;
}

public static TableDefinition buildTypeTable() {
TableDefinition table = new TableDefinition();
table.setName("PERSISTENCE32_TYPE");
Expand All @@ -37,6 +46,7 @@ public static TableDefinition buildPokemonTable() {
TableDefinition table = new TableDefinition();
table.setName("PERSISTENCE32_POKEMON");
table.addField(createNumericPk("ID"));
table.addField(createNumericFk("TRAINER_ID", 15,"PERSISTENCE32_TRAINER.ID", true));
table.addField(createStringColumn("NAME", 64, false));
return table;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
package org.eclipse.persistence.testing.models.jpa.persistence32;

import java.util.Collection;
import java.util.Collections;
import java.util.Objects;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;
Expand All @@ -37,6 +39,10 @@ public class Pokemon {

private String name;

@ManyToOne
@JoinColumn(name = "TRAINER_ID")
private Trainer trainer;

@ManyToMany
@JoinTable(name = "PERSISTENCE32_POKEMON_TYPE",
joinColumns = @JoinColumn(
Expand All @@ -52,13 +58,24 @@ public class Pokemon {
public Pokemon() {
}

public Pokemon(String name, Collection<Type> types) {
public Pokemon(String name, Trainer trainer, Collection<Type> types) {
this.name = name;
this.trainer = trainer;
this.types = types;
}

public Pokemon(int id, String name) {
this(name, null, Collections.EMPTY_LIST);
this.id = id;
}

public Pokemon(int id, String name, Collection<Type> types) {
this(name, types);
this(name, null, types);
this.id = id;
}

public Pokemon(int id, Trainer trainer, String name, Collection<Type> types) {
this(name, trainer, types);
this.id = id;
}

Expand Down Expand Up @@ -86,19 +103,28 @@ public void setTypes(Collection<Type> types) {
this.types = types;
}

public Trainer getTrainer() {
return trainer;
}

public void setTrainer(Trainer trainer) {
this.trainer = trainer;
}

@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
return id == ((Pokemon) obj).id
&& Objects.equals(name, ((Pokemon) obj).name)
&& Objects.equals(trainer, ((Pokemon) obj).trainer)
&& Objects.deepEquals(types, ((Pokemon) obj).types);
}

@Override
public int hashCode() {
int result = Objects.hash(id, name);
int result = Objects.hash(id, name, trainer);
for (Type type : types) {
result = 31 * result + type.hashCode();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.eclipse.persistence.testing.models.jpa.persistence32;

import java.util.Objects;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;

@Entity
@Table(name="PERSISTENCE32_TRAINER")
@NamedQuery(name="Trainer.get", query="SELECT t FROM Trainer t WHERE t.id = :id")
@NamedNativeQuery(name="Trainer.deleteAll", query="DELETE FROM PERSISTENCE32_TRAINER")
public class Trainer {

// ID is assigned in tests to avoid collisions
@Id
private int id;

private String name;

public Trainer() {
}

public Trainer(int id, String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

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

public String getName() {
return name;
}

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

@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
return id == ((Trainer) obj).id
&& Objects.equals(name, ((Trainer) obj).name);
}

@Override
public int hashCode() {
int result = Objects.hash(id, name);
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<!-- This is the default persistence unit -->
<persistence-unit name="persistence32" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.Trainer</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.Pokemon</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.Type</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.SyntaxEntity</class>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,22 @@
import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
import org.eclipse.persistence.testing.framework.jpa.junit.JUnitTestCase;
import org.eclipse.persistence.testing.models.jpa.persistence32.Persistence32TableCreator;
import org.eclipse.persistence.testing.models.jpa.persistence32.Trainer;
import org.eclipse.persistence.testing.models.jpa.persistence32.Type;

/**
* Abstract jUnit test suite with Pokemon model.
*/
public abstract class AbstractPokemon extends JUnitTestCase {

// Pokemon trainers. Array index is ID value.
// Value of ID = 0 does not exist so it's array instance is set to null.
static final Trainer[] TRAINERS = new Trainer[] {
null, // Skip array index 0
new Trainer(1, "Ash"),
new Trainer(2, "Brock")
};

// Pokemon types. Array index is ID value.
// Value of ID = 0 does not exist so it's array instance is set to null.
static final Type[] TYPES = new Type[] {
Expand Down Expand Up @@ -116,6 +125,9 @@ public void testSetup() {
new Persistence32TableCreator().replaceTables(JUnitTestCase.getServerSession(getPersistenceUnitName()));
clearCache();
emf.runInTransaction(em -> {
for (int i = 1; i < TRAINERS.length; i++) {
em.persist(TRAINERS[i]);
}
for (int i = 1; i < TYPES.length; i++) {
em.persist(TYPES[i]);
}
Expand All @@ -131,6 +143,7 @@ public void testCleanup() {
em.createNamedQuery("Pokemon.deleteAllTypes").executeUpdate();
em.createNamedQuery("Pokemon.deleteAll").executeUpdate();
em.createNamedQuery("Type.deleteAll").executeUpdate();
em.createNamedQuery("Trainer.deleteAll").executeUpdate();
});
}

Expand Down
Loading

0 comments on commit 7f7fab6

Please sign in to comment.