Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[master] Oracle 23C platform fixes #1969

Merged
merged 3 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2023 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
Expand All @@ -16,6 +16,7 @@

import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.internal.core.helper.CoreConversionManager;
import org.eclipse.persistence.internal.sessions.AbstractSession;

public interface CorePlatform<CONVERSION_MANAGER extends CoreConversionManager> {

Expand All @@ -29,6 +30,18 @@ public interface CorePlatform<CONVERSION_MANAGER extends CoreConversionManager>
*/
<T> T convertObject(Object sourceObject, Class<T> javaClass);

/**
* Convert the object to the appropriate type by invoking the appropriate
* ConversionManager method.
* @param sourceObject the object that must be converted
* @param javaClass the class that the object must be converted to
* @param session current database session
* @exception ConversionException all exceptions will be thrown as this type.
* @return the newly converted object
*/
<T> T convertObject(Object sourceObject, Class<T> javaClass, AbstractSession session) throws ConversionException;


/**
* The platform hold its own instance of conversion manager to allow customization.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,20 @@ public <T> T convertObject(Object sourceObject, Class<T> javaClass) throws Conve
return getConversionManager().convertObject(sourceObject, javaClass);
}

/**
* Convert the object to the appropriate type by invoking the appropriate
* ConversionManager method.
* @param sourceObject the object that must be converted
* @param javaClass the class that the object must be converted to
* @param session current database session
* @exception ConversionException all exceptions will be thrown as this type.
* @return the newly converted object
*/
@Override
public <T> T convertObject(Object sourceObject, Class<T> javaClass, AbstractSession session) throws ConversionException {
return convertObject(sourceObject, javaClass);
}

/**
* Copy the state into the new platform.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.persistence.internal.descriptors.ClassNameConversionRequired;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.DirectCollectionMapping;
import org.eclipse.persistence.mappings.foundation.AbstractDirectMapping;
Expand Down Expand Up @@ -209,7 +210,12 @@ public void setObjectClassName(String objectClassName) {
@Override
public Object convertObjectValueToDataValue(Object attributeValue, Session session) {
try {
return session.getDatasourcePlatform().convertObject(attributeValue, getDataClass());
if (session.isConnected()) {
//Should handle conversions where DB connection is needed like String -> java.sql.Clob
return session.getDatasourcePlatform().convertObject(attributeValue, getDataClass(), (AbstractSession)session);
} else {
return session.getDatasourcePlatform().convertObject(attributeValue, getDataClass());
}
} catch (ConversionException e) {
throw ConversionException.couldNotBeConverted(mapping, mapping.getDescriptor(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,62 @@
// Oracle - initial API and implementation
package org.eclipse.persistence.platform.database;

import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.sessions.AbstractSession;

import java.sql.Clob;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Hashtable;

import static org.eclipse.persistence.internal.helper.StringHelper.EMPTY_STRING;

public class Oracle23Platform extends Oracle21Platform {

public Oracle23Platform() {
super();
}

@Override
protected Hashtable<Class<?>, FieldTypeDefinition> buildFieldTypes() {
Hashtable<Class<?>, FieldTypeDefinition> fieldTypes = super.buildFieldTypes();
fieldTypes.put(java.time.LocalDateTime.class, new FieldTypeDefinition("TIMESTAMP", 9));
fieldTypes.put(java.time.LocalTime.class, new FieldTypeDefinition("TIMESTAMP", 9));
return fieldTypes;
}

/**
* INTERNAL:
* Check whether current platform is Oracle 23c or later.
* @return Always returns {@code true} for instances of Oracle 23c platform.
* @since 4.0.2
*/
@Override
public boolean isOracle23() {
return true;
}

/**
* INTERNAL:
* Allow for conversion from the Oracle type to the Java type. Used in cases when DB connection is needed like BLOB, CLOB.
*/
@Override
public <T> T convertObject(Object sourceObject, Class<T> javaClass, AbstractSession session) throws ConversionException, DatabaseException {
//Handle special case when empty String ("") is passed from the entity into CLOB type column
if (ClassConstants.CLOB.equals(javaClass) && sourceObject instanceof String && EMPTY_STRING.equals(sourceObject)) {
Connection connection = session.getAccessor().getConnection();
Clob clob = null;
try {
clob = connection.createClob();
clob.setString(1, (String)sourceObject);
} catch (SQLException e) {
throw ConversionException.couldNotBeConvertedToClass(sourceObject, ClassConstants.CLOB, e);
}
return (T) clob;
}
return super.convertObject(sourceObject, javaClass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
// Oracle - initial API and implementation
package org.eclipse.persistence.platform.database.oracle;

import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.sessions.AbstractSession;

import java.sql.Clob;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Hashtable;

import static org.eclipse.persistence.internal.helper.StringHelper.EMPTY_STRING;

/**
* <p><b>Purpose:</b>
* Supports certain new Oracle 23c data types, and usage of certain Oracle JDBC specific APIs.
Expand All @@ -29,7 +42,6 @@ public Oracle23Platform() {
super();
}


/**
* INTERNAL:
* Check whether current platform is Oracle 23c or later.
Expand All @@ -40,4 +52,33 @@ public Oracle23Platform() {
public boolean isOracle23() {
return true;
}

@Override
protected Hashtable<Class<?>, FieldTypeDefinition> buildFieldTypes() {
Hashtable<Class<?>, FieldTypeDefinition> fieldTypes = super.buildFieldTypes();
fieldTypes.put(java.time.LocalDateTime.class, new FieldTypeDefinition("TIMESTAMP", 9));
fieldTypes.put(java.time.LocalTime.class, new FieldTypeDefinition("TIMESTAMP", 9));
return fieldTypes;
}

/**
* INTERNAL:
* Allow for conversion from the Oracle type to the Java type. Used in cases when DB connection is needed like BLOB, CLOB.
*/
@Override
public <T> T convertObject(Object sourceObject, Class<T> javaClass, AbstractSession session) throws ConversionException, DatabaseException {
//Handle special case when empty String ("") is passed from the entity into CLOB type column
if (ClassConstants.CLOB.equals(javaClass) && sourceObject instanceof String && EMPTY_STRING.equals(sourceObject)) {
Connection connection = session.getAccessor().getConnection();
Clob clob = null;
try {
clob = connection.createClob();
clob.setString(1, (String)sourceObject);
} catch (SQLException e) {
throw ConversionException.couldNotBeConvertedToClass(sourceObject, ClassConstants.CLOB, e);
}
return (T) clob;
}
return super.convertObject(sourceObject, javaClass);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -157,12 +157,13 @@ public void testOracleWithoutLOBLocatorWithEmptyClob() throws Exception {
// After Oracle 11, the lob locator is disabled by default (requiring a Session Customizer to reenable it)
// So the test should fail because Eclipselink will try store a null value instead of an empty_blob()/empty_clob()
// and violate the NOT NULL constraint.
// In Oracle23Platform case when empty String ("") is stored into CLOB column is handled by java.sql.Clob
Set<String> notAllowedPlatforms = new HashSet<String>();
notAllowedPlatforms.add("org.eclipse.persistence.platform.database.Oracle8Platform");
notAllowedPlatforms.add("org.eclipse.persistence.platform.database.Oracle9Platform");
notAllowedPlatforms.add("org.eclipse.persistence.platform.database.Oracle10Platform");


notAllowedPlatforms.add("org.eclipse.persistence.platform.database.Oracle23Platform");
notAllowedPlatforms.add("org.eclipse.persistence.platform.database.oracle.Oracle23Platform");
if (!checkIsOracle() || notAllowedPlatforms.contains(getPlatform(emfNoSessionCustomizer).getClass().getName())) {
// Skip if not testing against Oracle
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023 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
Expand Down Expand Up @@ -120,10 +120,14 @@ public void process(DatabaseMapping mapping, MappingAccessor accessor, MetadataC
// referenceClass type.
if (isValidClobType(referenceClass)) {
setFieldClassification(mapping, java.sql.Clob.class, isForMapKey);
setConverter(mapping, new TypeConversionConverter(mapping), isForMapKey);
TypeConversionConverter typeConversionConverter = new TypeConversionConverter(mapping);
typeConversionConverter.setDataClass(java.sql.Clob.class);
setConverter(mapping, typeConversionConverter, isForMapKey);
} else if (isValidBlobType(referenceClass)) {
setFieldClassification(mapping, java.sql.Blob.class, isForMapKey);
setConverter(mapping, new TypeConversionConverter(mapping), isForMapKey);
TypeConversionConverter typeConversionConverter = new TypeConversionConverter(mapping);
typeConversionConverter.setDataClass(java.sql.Blob.class);
setConverter(mapping, typeConversionConverter, isForMapKey);
} else if (referenceClass.extendsInterface(Serializable.class)) {
setFieldClassification(mapping, java.sql.Blob.class, isForMapKey);
setConverter(mapping, new SerializedObjectConverter(mapping), isForMapKey);
Expand Down
Loading