@ -20,15 +20,14 @@ import java.sql.JDBCType;
import java.sql.SQLException ;
import java.sql.SQLException ;
import java.sql.SQLType ;
import java.sql.SQLType ;
import java.util.Iterator ;
import java.util.Iterator ;
import java.util.List ;
import java.util.Map ;
import java.util.Map ;
import java.util.Optional ;
import java.util.Optional ;
import java.util.function.Function ;
import java.util.function.Function ;
import org.apache.commons.logging.Log ;
import org.apache.commons.logging.LogFactory ;
import org.springframework.context.ApplicationContextAware ;
import org.springframework.context.ApplicationContextAware ;
import org.springframework.core.convert.converter.Converter ;
import org.springframework.core.convert.converter.Converter ;
import org.springframework.dao.NonTransient DataAccessException ;
import org.springframework.dao.DataAccessException ;
import org.springframework.data.convert.CustomConversions ;
import org.springframework.data.convert.CustomConversions ;
import org.springframework.data.jdbc.core.mapping.AggregateReference ;
import org.springframework.data.jdbc.core.mapping.AggregateReference ;
import org.springframework.data.jdbc.core.mapping.JdbcValue ;
import org.springframework.data.jdbc.core.mapping.JdbcValue ;
@ -46,6 +45,10 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentEnti
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty ;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty ;
import org.springframework.data.relational.domain.RowDocument ;
import org.springframework.data.relational.domain.RowDocument ;
import org.springframework.data.util.TypeInformation ;
import org.springframework.data.util.TypeInformation ;
import org.springframework.jdbc.UncategorizedSQLException ;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator ;
import org.springframework.jdbc.support.SQLExceptionSubclassTranslator ;
import org.springframework.jdbc.support.SQLExceptionTranslator ;
import org.springframework.lang.Nullable ;
import org.springframework.lang.Nullable ;
import org.springframework.util.Assert ;
import org.springframework.util.Assert ;
@ -67,9 +70,9 @@ import org.springframework.util.Assert;
* /
* /
public class MappingJdbcConverter extends MappingRelationalConverter implements JdbcConverter , ApplicationContextAware {
public class MappingJdbcConverter extends MappingRelationalConverter implements JdbcConverter , ApplicationContextAware {
private static final Log LOG = LogFactory . getLog ( MappingJdbcConverter . class ) ;
private static final Converter < Iterable < ? > , Map < ? , ? > > ITERABLE_OF_ENTRY_TO_MAP_CONVERTER = new IterableOfEntryToMapConverter ( ) ;
private static final Converter < Iterable < ? > , Map < ? , ? > > ITERABLE_OF_ENTRY_TO_MAP_CONVERTER = new IterableOfEntryToMapConverter ( ) ;
private SQLExceptionTranslator exceptionTranslator = new SQLExceptionSubclassTranslator ( ) ;
private final JdbcTypeFactory typeFactory ;
private final JdbcTypeFactory typeFactory ;
private final RelationResolver relationResolver ;
private final RelationResolver relationResolver ;
@ -111,6 +114,15 @@ public class MappingJdbcConverter extends MappingRelationalConverter implements
this . relationResolver = relationResolver ;
this . relationResolver = relationResolver ;
}
}
/ * *
* Set the exception translator for this instance . Defaults to a { @link SQLErrorCodeSQLExceptionTranslator } .
*
* @see SQLExceptionSubclassTranslator
* /
public void setExceptionTranslator ( SQLExceptionTranslator exceptionTranslator ) {
this . exceptionTranslator = exceptionTranslator ;
}
@Nullable
@Nullable
private Class < ? > getEntityColumnType ( TypeInformation < ? > type ) {
private Class < ? > getEntityColumnType ( TypeInformation < ? > type ) {
@ -189,59 +201,44 @@ public class MappingJdbcConverter extends MappingRelationalConverter implements
return null ;
return null ;
}
}
TypeInformation < ? > originalTargetType = targetType ;
value = potentiallyUnwrapArray ( value ) ;
value = readJdbcArray ( value ) ;
targetType = determineNestedTargetType ( targetType ) ;
return possiblyReadToAggregateReference ( getPotentiallyConvertedSimpleRead ( value , targetType ) , originalTargetType ) ;
if ( AggregateReference . class . isAssignableFrom ( targetType . getType ( ) ) ) {
List < TypeInformation < ? > > types = targetType . getTypeArguments ( ) ;
TypeInformation < ? > idType = types . get ( 1 ) ;
if ( value instanceof AggregateReference < ? , ? > ref ) {
return AggregateReference . to ( readValue ( ref . getId ( ) , idType ) ) ;
} else {
return AggregateReference . to ( readValue ( value , idType ) ) ;
}
}
return getPotentiallyConvertedSimpleRead ( value , targetType ) ;
}
}
/ * *
/ * *
* Unwrap a Jdbc array , if such a value is provided
* Unwrap a Jdbc array , if such a value is provided
* /
* /
private Object readJdbcArray ( Object value ) {
private Object potentiallyUnwrap Array( Object value ) {
if ( value instanceof Array array ) {
if ( value instanceof Array array ) {
try {
try {
return array . getArray ( ) ;
return array . getArray ( ) ;
} catch ( SQLException e ) {
} catch ( SQLException e ) {
throw new FailedToAccessJdbcArrayException ( e ) ;
throw translateException ( "Array.getArray()" , null , e ) ;
}
}
}
}
return value ;
return value ;
}
}
/ * *
* Determine the id type of an { @link AggregateReference } that the rest of the conversion infrastructure needs to use
* as a conversion target .
* /
private TypeInformation < ? > determineNestedTargetType ( TypeInformation < ? > ultimateTargetType ) {
if ( AggregateReference . class . isAssignableFrom ( ultimateTargetType . getType ( ) ) ) {
// the id type of a AggregateReference
return ultimateTargetType . getTypeArguments ( ) . get ( 1 ) ;
}
return ultimateTargetType ;
}
/ * *
* Convert value to an { @link AggregateReference } if that is specified by the parameter targetType .
* /
private Object possiblyReadToAggregateReference ( Object value , TypeInformation < ? > targetType ) {
if ( AggregateReference . class . isAssignableFrom ( targetType . getType ( ) ) ) {
return AggregateReference . to ( value ) ;
}
return value ;
}
@Nullable
@Nullable
@Override
@Override
protected Object getPotentiallyConvertedSimpleWrite ( Object value , TypeInformation < ? > type ) {
protected Object getPotentiallyConvertedSimpleWrite ( Object value , TypeInformation < ? > type ) {
if ( value instanceof AggregateReference < ? , ? > agg regateRe ference ) {
if ( value instanceof AggregateReference < ? , ? > ref ) {
return writeValue ( agg regateRe ference . getId ( ) , type ) ;
return writeValue ( ref . getId ( ) , type ) ;
}
}
return super . getPotentiallyConvertedSimpleWrite ( value , type ) ;
return super . getPotentiallyConvertedSimpleWrite ( value , type ) ;
@ -277,11 +274,11 @@ public class MappingJdbcConverter extends MappingRelationalConverter implements
public JdbcValue writeJdbcValue ( @Nullable Object value , TypeInformation < ? > columnType , SQLType sqlType ) {
public JdbcValue writeJdbcValue ( @Nullable Object value , TypeInformation < ? > columnType , SQLType sqlType ) {
TypeInformation < ? > targetType = canWriteAsJdbcValue ( value ) ? TypeInformation . of ( JdbcValue . class ) : columnType ;
TypeInformation < ? > targetType = canWriteAsJdbcValue ( value ) ? TypeInformation . of ( JdbcValue . class ) : columnType ;
if ( value instanceof AggregateReference < ? , ? > agg regateRe ference ) {
if ( value instanceof AggregateReference < ? , ? > ref ) {
return writeJdbcValue ( agg regateRe ference . getId ( ) , columnType , sqlType ) ;
return writeJdbcValue ( ref . getId ( ) , columnType , sqlType ) ;
}
}
Object convertedValue = writeValue ( value , targetType ) ;
Object convertedValue = writeValue ( value , targetType ) ;
if ( convertedValue instanceof JdbcValue result ) {
if ( convertedValue instanceof JdbcValue result ) {
@ -306,7 +303,7 @@ public class MappingJdbcConverter extends MappingRelationalConverter implements
return JdbcValue . of ( convertedValue , JDBCType . BINARY ) ;
return JdbcValue . of ( convertedValue , JDBCType . BINARY ) ;
}
}
return JdbcValue . of ( convertedValue , sqlType ) ;
return JdbcValue . of ( convertedValue , sqlType ) ;
}
}
@ -340,10 +337,18 @@ public class MappingJdbcConverter extends MappingRelationalConverter implements
return super . newValueProvider ( documentAccessor , evaluator , context ) ;
return super . newValueProvider ( documentAccessor , evaluator , context ) ;
}
}
private static class FailedToAccessJdbcArrayException extends NonTransientDataAccessException {
/ * *
public FailedToAccessJdbcArrayException ( SQLException e ) {
* Translate the given { @link SQLException } into a generic { @link DataAccessException } .
super ( "Failed to read array" , e ) ;
*
}
* @param task readable text describing the task being attempted
* @param sql the SQL query or update that caused the problem ( can be { @code null } )
* @param ex the offending { @code SQLException }
* @return a DataAccessException wrapping the { @code SQLException } ( never { @code null } )
* /
private DataAccessException translateException ( String task , @org.jspecify.annotations.Nullable String sql ,
SQLException ex ) {
DataAccessException dae = exceptionTranslator . translate ( task , sql , ex ) ;
return ( dae ! = null ? dae : new UncategorizedSQLException ( task , sql , ex ) ) ;
}
}
/ * *
/ * *