Skip to content

Commit

Permalink
Jakarta Persistence 3.2 new feature - java.lang.Record support in @Em…
Browse files Browse the repository at this point in the history
…bedded parts

Signed-off-by: Radek Felcman <[email protected]>
  • Loading branch information
rfelcman committed Jun 6, 2024
1 parent df24ebb commit faebf1d
Show file tree
Hide file tree
Showing 23 changed files with 1,112 additions and 163 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/

// Contributors:
// Oracle - initial API and implementation
package org.eclipse.persistence.testing.tests.junit.policies;

import java.time.Instant;
import java.util.Objects;

public record NestedDetailRecord(float floatAttribute, Instant instantAttribute) {

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NestedDetailRecord that = (NestedDetailRecord) o;
return Float.compare(floatAttribute, that.floatAttribute) == 0 && Objects.equals(instantAttribute, that.instantAttribute);
}

@Override
public int hashCode() {
return Objects.hash(floatAttribute, instantAttribute);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/

// Contributors:
// Oracle - initial API and implementation
package org.eclipse.persistence.testing.tests.junit.policies;

import java.util.Objects;

public record NestedMasterRecord(int intAttribute, String stringAttribute, NestedDetailRecord nestedDetailRecord) {

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NestedMasterRecord that = (NestedMasterRecord) o;
return intAttribute == that.intAttribute && Objects.equals(stringAttribute, that.stringAttribute) && Objects.equals(nestedDetailRecord, that.nestedDetailRecord);
}

@Override
public int hashCode() {
return Objects.hash(intAttribute, stringAttribute, nestedDetailRecord);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/

// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.tests.junit.policies;

import org.eclipse.persistence.descriptors.copying.RecordCopyPolicy;
import org.junit.Test;

import java.time.Instant;

import static org.junit.Assert.assertEquals;

public class RecordCopyPolicyTest {

private final int INT_ATTR = 1;
private final String STRING_ATTR = "abcde";
private final float FLOAT_ATTR = 1.1F;
private final Instant INSTANT_ATTR = Instant.parse("2024-05-27T10:15:30.00Z");

@Test
public void cloneNestedRecordTest() {
NestedDetailRecord nestedDetailRecord = new NestedDetailRecord(FLOAT_ATTR, INSTANT_ATTR);
NestedMasterRecord source = new NestedMasterRecord(INT_ATTR, STRING_ATTR, nestedDetailRecord);
RecordCopyPolicy recordCopyPolicy = new RecordCopyPolicy();
NestedMasterRecord clone = (NestedMasterRecord)recordCopyPolicy.buildClone(source, null);
assertEquals(source, clone);
}

@Test
public void cloneNestedRecordWithNullTest() {
NestedDetailRecord nestedDetailRecord = new NestedDetailRecord(FLOAT_ATTR, INSTANT_ATTR);
NestedMasterRecord source = new NestedMasterRecord(INT_ATTR, null, nestedDetailRecord);
RecordCopyPolicy recordCopyPolicy = new RecordCopyPolicy();
NestedMasterRecord clone = (NestedMasterRecord)recordCopyPolicy.buildClone(source, null);
assertEquals(source, clone);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/

// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.tests.junit.policies;

import org.eclipse.persistence.internal.descriptors.RecordInstantiationPolicy;
import org.junit.Test;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

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

public class RecordInstantiationPolicyTest {

private final float FLOAT_ATTR = 1.1F;
private final Instant INSTANT_ATTR = Instant.parse("2024-05-27T10:15:30.00Z");

@Test
public void newRecordTest() {
Class clazz = NestedDetailRecord.class;
//Values order is important. It must be same as order of Record attributes.
List values = new ArrayList<>();
values.add(FLOAT_ATTR);
values.add(INSTANT_ATTR);
RecordInstantiationPolicy recordInstantiationPolicy = new RecordInstantiationPolicy(clazz);
recordInstantiationPolicy.setValues(values);
NestedDetailRecord nestedDetailRecord = (NestedDetailRecord) recordInstantiationPolicy.buildNewInstance();
assertEquals(FLOAT_ATTR, nestedDetailRecord.floatAttribute(), 0);
assertEquals(INSTANT_ATTR, nestedDetailRecord.instantAttribute());
}

@Test
public void newRecordWithNullTest() {
Class clazz = NestedDetailRecord.class;
//Values order is important. It must be same as order of Record attributes.
List values = new ArrayList<>();
values.add(FLOAT_ATTR);
values.add(null);
RecordInstantiationPolicy recordInstantiationPolicy = new RecordInstantiationPolicy(clazz);
recordInstantiationPolicy.setValues(values);
NestedDetailRecord nestedDetailRecord = (NestedDetailRecord) recordInstantiationPolicy.buildNewInstance();
assertEquals(FLOAT_ATTR, nestedDetailRecord.floatAttribute(), 0);
assertNull(nestedDetailRecord.instantAttribute());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.eclipse.persistence.descriptors.copying.CopyPolicy;
import org.eclipse.persistence.descriptors.copying.InstantiationCopyPolicy;
import org.eclipse.persistence.descriptors.copying.PersistenceEntityCopyPolicy;
import org.eclipse.persistence.descriptors.copying.RecordCopyPolicy;
import org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy;
import org.eclipse.persistence.descriptors.invalidation.NoExpiryCacheInvalidationPolicy;
import org.eclipse.persistence.descriptors.partitioning.PartitioningPolicy;
Expand All @@ -73,6 +74,7 @@
import org.eclipse.persistence.internal.descriptors.PersistenceObject;
import org.eclipse.persistence.internal.descriptors.PersistenceObjectAttributeAccessor;
import org.eclipse.persistence.internal.descriptors.PersistenceObjectInstantiationPolicy;
import org.eclipse.persistence.internal.descriptors.RecordInstantiationPolicy;
import org.eclipse.persistence.internal.descriptors.SerializedObjectPolicyWrapper;
import org.eclipse.persistence.internal.descriptors.VirtualAttributeMethodInfo;
import org.eclipse.persistence.internal.dynamic.DynamicEntityImpl;
Expand Down Expand Up @@ -2123,7 +2125,11 @@ public List<Class<?>> getConstraintDependencies() {
public CopyPolicy getCopyPolicy() {
// Lazy initialize for XML deployment.
if (copyPolicy == null) {
setCopyPolicy(new InstantiationCopyPolicy());
if (javaClass != null && javaClass.isRecord()) {
setCopyPolicy(new RecordCopyPolicy());
} else {
setCopyPolicy(new InstantiationCopyPolicy());
}
}
return copyPolicy;
}
Expand Down Expand Up @@ -2308,7 +2314,11 @@ public InheritancePolicy getInheritancePolicyOrNull() {
public InstantiationPolicy getInstantiationPolicy() {
// Lazy initialize for XML deployment.
if (instantiationPolicy == null) {
setInstantiationPolicy(new InstantiationPolicy());
if (javaClass != null && javaClass.isRecord()) {
setInstantiationPolicy(new RecordInstantiationPolicy(javaClass));
} else {
setInstantiationPolicy(new InstantiationPolicy());
}
}
return instantiationPolicy;
}
Expand Down Expand Up @@ -3994,12 +4004,20 @@ public void preInitialize(AbstractSession session) throws DescriptorException {
}
if (!isMethodAccess) {
if (this.copyPolicy == null) {
setCopyPolicy(new PersistenceEntityCopyPolicy());
if (javaClass != null && javaClass.isRecord()) {
setCopyPolicy(new RecordCopyPolicy());
} else {
setCopyPolicy(new PersistenceEntityCopyPolicy());
}
}
if (!isAbstract()) {
try {
if (this.instantiationPolicy == null) {
setInstantiationPolicy(new PersistenceObjectInstantiationPolicy((PersistenceObject)getJavaClass().getConstructor().newInstance()));
if (javaClass != null && javaClass.isRecord()) {
setInstantiationPolicy(new RecordInstantiationPolicy(javaClass));
} else {
setInstantiationPolicy(new PersistenceObjectInstantiationPolicy((PersistenceObject)getJavaClass().getConstructor().newInstance()));
}
}
} catch (Exception ignore) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,8 @@ public void setDescriptor(ClassDescriptor descriptor) {
this.descriptor = descriptor;
}

@Override
public String toString() {
return getClass().getSimpleName() + "()";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,4 @@ public Object buildClone(Object domainObject, Session session) throws Descriptor
public boolean buildsNewInstance() {
return true;
}

@Override
public String toString() {
return getClass().getSimpleName() + "()";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,4 @@ public Object buildClone(Object object, Session session) throws DescriptorExcept
public boolean buildsNewInstance() {
return false;
}

@Override
public String toString() {
return getClass().getSimpleName() + "()";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/

// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.descriptors.copying;

import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.sessions.Session;

import java.lang.reflect.Constructor;
import java.lang.reflect.RecordComponent;
import java.util.ArrayList;

/**
* <p><b>Purpose</b>: This is the copy policy for {@code java.lang.Record}.
* <p>
* It creates a copy by creating a new instance. As {@code java.lang.Record} is immutable
* all cloned attributes must be collected and passed to the constructor.
*/
public class RecordCopyPolicy extends AbstractCopyPolicy {
public RecordCopyPolicy() {
super();
}

@Override
public Object buildClone(Object domainObject, Session session) throws DescriptorException {
return cloneRecord((Record) domainObject);
}

@Override
public boolean buildsNewInstance() {
return true;
}

//There is limited functionality some complex nested objects shouldn't be cloned.
private <R extends Record> R cloneRecord(R template) {
try {
ArrayList<Class<?>> types = new ArrayList<>();
ArrayList values = new ArrayList<>();
for (RecordComponent component : template.getClass().getRecordComponents()) {
types.add(component.getType());
Object value = component.getAccessor().invoke(template);
if (value instanceof Record) {
value = cloneRecord((Record) value);
}
values.add(value);
}
Constructor<? extends Record> canonical = template.getClass().getDeclaredConstructor(types.toArray(Class[]::new));
var result = (R) canonical.newInstance(values.toArray(Object[]::new));
return result;
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Record clone failed: " + e, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.eclipse.persistence.internal.core.sessions.CoreAbstractRecord;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;

import java.util.List;

public abstract class CoreObjectBuilder<
ABSTRACT_RECORD extends CoreAbstractRecord,
ABSTRACT_SESSION extends CoreAbstractSession,
Expand All @@ -36,6 +38,12 @@ protected CoreObjectBuilder() {
*/
public abstract Object buildNewInstance();

/**
* Return a new {@code java.lang.Record} instance.
* As this kind of class is immutable all values must be passed during creation as constructor parameters.
*/
public abstract Object buildNewRecordInstance(Class<Record> clazz, List<MAPPING> mappings, ABSTRACT_RECORD databaseRow, ABSTRACT_SESSION session);

/**
* Create a new row/record for the object builder.
* This allows subclasses to define different record types.
Expand Down
Loading

0 comments on commit faebf1d

Please sign in to comment.