Skip to content
Open
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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>4.0.1-SNAPSHOT</version>
<version>4.0.x-GH-4092-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data JPA Parent</name>
Expand Down
4 changes: 2 additions & 2 deletions spring-data-envers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>4.0.1-SNAPSHOT</version>
<version>4.0.x-GH-4092-SNAPSHOT</version>

<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>4.0.1-SNAPSHOT</version>
<version>4.0.x-GH-4092-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-jpa-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>4.0.1-SNAPSHOT</version>
<version>4.0.x-GH-4092-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jpa/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>4.0.1-SNAPSHOT</version>
<version>4.0.x-GH-4092-SNAPSHOT</version>

<name>Spring Data JPA</name>
<description>Spring Data module for JPA repositories.</description>
Expand All @@ -16,7 +16,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>4.0.1-SNAPSHOT</version>
<version>4.0.x-GH-4092-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import jakarta.persistence.spi.PersistenceUnitInfo;

import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import org.springframework.data.repository.config.AotRepositoryContext;
Expand Down Expand Up @@ -60,11 +61,26 @@ private AotEntityManagerFactoryCreator(Supplier<EntityManagerFactory> factory, O
* @param repositoryContext repository context providing classes.
*/
public static AotEntityManagerFactoryCreator from(AotRepositoryContext repositoryContext) {
return from(repositoryContext, Map.of());
}

/**
* Create a {@code PersistenceUnitContext} from the given {@link AotRepositoryContext} using Jakarta
* Persistence-annotated classes.
* <p>
* The underlying {@link jakarta.persistence.metamodel.Metamodel} requires Hibernate to build metamodel information.
*
* @param repositoryContext repository context providing classes.
* @param jpaProperties JPA properties to apply.
* @since 4.0.1
*/
public static AotEntityManagerFactoryCreator from(AotRepositoryContext repositoryContext,
Map<String, Object> jpaProperties) {

List<String> typeNames = repositoryContext.getResolvedTypes().stream()
.filter(AotEntityManagerFactoryCreator::isJakartaAnnotated).map(Class::getName).toList();

return from(PersistenceManagedTypes.of(typeNames, List.of()), typeNames);
return from(PersistenceManagedTypes.of(typeNames, List.of()), typeNames, jpaProperties);
}

/**
Expand All @@ -75,7 +91,21 @@ public static AotEntityManagerFactoryCreator from(AotRepositoryContext repositor
* @param persistenceUnitInfo persistence unit info to use.
*/
public static AotEntityManagerFactoryCreator from(PersistenceUnitInfo persistenceUnitInfo) {
return from(() -> new AotMetamodel(persistenceUnitInfo), persistenceUnitInfo);
return from(persistenceUnitInfo, Map.of());
}

/**
* Create a {@code PersistenceUnitContext} from the given {@link PersistenceUnitInfo}.
* <p>
* The underlying {@link jakarta.persistence.metamodel.Metamodel} requires Hibernate to build metamodel information.
*
* @param persistenceUnitInfo persistence unit info to use.
* @param jpaProperties JPA properties to apply.
* @since 4.0.1
*/
public static AotEntityManagerFactoryCreator from(PersistenceUnitInfo persistenceUnitInfo,
Map<String, Object> jpaProperties) {
return from(() -> new AotMetamodel(persistenceUnitInfo, jpaProperties), persistenceUnitInfo);
}

/**
Expand All @@ -86,11 +116,26 @@ public static AotEntityManagerFactoryCreator from(PersistenceUnitInfo persistenc
* @param managedTypes managed types to use.
*/
public static AotEntityManagerFactoryCreator from(PersistenceManagedTypes managedTypes) {
return from(managedTypes, managedTypes);
return from(managedTypes, managedTypes, Map.of());
}

/**
* Create a {@code PersistenceUnitContext} from the given {@link PersistenceManagedTypes}.
* <p>
* The underlying {@link jakarta.persistence.metamodel.Metamodel} requires Hibernate to build metamodel information.
*
* @param managedTypes managed types to use.
* @param jpaProperties JPA properties to apply.
* @since 4.0.1
*/
public static AotEntityManagerFactoryCreator from(PersistenceManagedTypes managedTypes,
Map<String, Object> jpaProperties) {
return from(managedTypes, managedTypes, jpaProperties);
}

private static AotEntityManagerFactoryCreator from(PersistenceManagedTypes managedTypes, Object cacheKey) {
return from(() -> new AotMetamodel(managedTypes), cacheKey);
private static AotEntityManagerFactoryCreator from(PersistenceManagedTypes managedTypes, Object cacheKey,
Map<String, Object> jpaProperties) {
return from(() -> new AotMetamodel(managedTypes, jpaProperties), cacheKey);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,16 @@
import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl;
import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor;
import org.hibernate.query.common.TemporalUnit;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.util.Lazy;
import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes;
import org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo;
import org.springframework.util.CollectionUtils;

/**
* AOT metamodel implementation that uses Hibernate to build the metamodel.
Expand All @@ -58,14 +64,26 @@
*/
class AotMetamodel implements Metamodel {

private static final Logger log = LoggerFactory.getLogger(AotMetamodel.class);

/**
* Collection of know properties causing problems during AOT if set differntly
*/
private static final Map<String, Object> FAILSAFE_AOT_PROPERTIES = Map.of( //
JdbcSettings.ALLOW_METADATA_ON_BOOT, false, //
JdbcSettings.CONNECTION_PROVIDER, NoOpConnectionProvider.INSTANCE, //
QuerySettings.QUERY_STARTUP_CHECKING, false, //
PersistenceSettings.JPA_CALLBACKS_ENABLED, false //
);
private final Lazy<EntityManagerFactory> entityManagerFactory;
private final Lazy<EntityManager> entityManager = Lazy.of(() -> getEntityManagerFactory().createEntityManager());

public AotMetamodel(PersistenceManagedTypes managedTypes) {
this(managedTypes.getManagedClassNames(), managedTypes.getPersistenceUnitRootUrl());
public AotMetamodel(PersistenceManagedTypes managedTypes, Map<String, Object> jpaProperties) {
this(managedTypes.getManagedClassNames(), managedTypes.getPersistenceUnitRootUrl(), jpaProperties);
}

public AotMetamodel(Collection<String> managedTypes, @Nullable URL persistenceUnitRootUrl) {
public AotMetamodel(Collection<String> managedTypes, @Nullable URL persistenceUnitRootUrl,
Map<String, Object> jpaProperties) {

SpringPersistenceUnitInfo persistenceUnitInfo = new SpringPersistenceUnitInfo(
managedTypes.getClass().getClassLoader());
Expand All @@ -78,22 +96,46 @@ public AotMetamodel(Collection<String> managedTypes, @Nullable URL persistenceUn

persistenceUnitInfo.setPersistenceProviderClassName(HibernatePersistenceProvider.class.getName());
return new PersistenceUnitInfoDescriptor(persistenceUnitInfo.asStandardPersistenceUnitInfo());
});
}, jpaProperties);
}

public AotMetamodel(PersistenceUnitInfo unitInfo, Map<String, Object> jpaProperties) {
this.entityManagerFactory = init(() -> new PersistenceUnitInfoDescriptor(unitInfo), jpaProperties);
}

static Lazy<EntityManagerFactory> init(Supplier<PersistenceUnitInfoDescriptor> unitInfo,
Map<String, Object> jpaProperties) {
return Lazy.of(() -> new EntityManagerFactoryBuilderImpl(unitInfo.get(), initProperties(jpaProperties)).build());
}

public AotMetamodel(PersistenceUnitInfo unitInfo) {
this.entityManagerFactory = init(() -> new PersistenceUnitInfoDescriptor(unitInfo));
static Map<String, Object> initProperties(Map<String, Object> jpaProperties) {

Map<String, Object> properties = CollectionUtils
.newLinkedHashMap(jpaProperties.size() + FAILSAFE_AOT_PROPERTIES.size() + 1);

// we allow explicit Dialect Overrides, but put in a default one to avoid potential db access
properties.put(JdbcSettings.DIALECT, SpringDataJpaAotDialect.INSTANCE);

// apply user defined properties
properties.putAll(jpaProperties);

// override properties known to cause trouble
applyPropertyOverrides(properties);

return properties;
}

static Lazy<EntityManagerFactory> init(Supplier<PersistenceUnitInfoDescriptor> unitInfo) {
private static void applyPropertyOverrides(Map<String, Object> properties) {

for (Map.Entry<String, Object> entry : FAILSAFE_AOT_PROPERTIES.entrySet()) {

return Lazy.of(() -> new EntityManagerFactoryBuilderImpl(unitInfo.get(),
Map.of(JdbcSettings.DIALECT, SpringDataJpaAotDialect.INSTANCE, //
JdbcSettings.ALLOW_METADATA_ON_BOOT, false, //
JdbcSettings.CONNECTION_PROVIDER, new UserSuppliedConnectionProviderImpl(), //
QuerySettings.QUERY_STARTUP_CHECKING, false, //
PersistenceSettings.JPA_CALLBACKS_ENABLED, false))
.build());
if (log.isDebugEnabled() && properties.containsKey(entry.getKey())) {
log.debug("Overriding property [%s] with value [%s] for AOT Repository processing.".formatted(entry.getKey(),
entry.getValue()));
}

properties.put(entry.getKey(), entry.getValue());
}
}

private Metamodel getMetamodel() {
Expand Down Expand Up @@ -141,6 +183,7 @@ public EntityManagerFactory getEntityManagerFactory() {
* A {@link Dialect} to satisfy the bootstrap requirements of {@link JdbcSettings#DIALECT} during the AOT Phase. Printed
* to log files (info level) when the {@link org.hibernate.engine.jdbc.env.spi.JdbcEnvironment} is created.
*/
@NullUnmarked
@SuppressWarnings("deprecation")
static class SpringDataJpaAotDialect extends Dialect {

Expand All @@ -164,6 +207,12 @@ public SequenceSupport getSequenceSupport() {
return ANSISequenceSupport.INSTANCE;
}

@Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
// javadoc implies null would trigger default which is not the case
return new StandardSqlAstTranslatorFactory();
}

@Override
@SuppressWarnings("deprecation")
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
Expand All @@ -175,4 +224,14 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT

}

static class NoOpConnectionProvider extends UserSuppliedConnectionProviderImpl {

static final NoOpConnectionProvider INSTANCE = new NoOpConnectionProvider();

@Override
public String toString() {
return "NoOpConnectionProvider";
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ protected String rewriteQuery(DeclaredQuery query, Sort sort, Class<?> returnedT
return source;
}

@SuppressWarnings("NullAway")
protected long getCount(Query query) {

List<?> totals = query.getResultList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ public CodeBlock build() {
TypeName typeToRead = isProjecting ? methodReturn.getActualTypeName() : TypeName.get(context.getDomainType());
builder.add("\n");

if (modifying.isPresent() && !aotQuery.isDerived()) {
if (modifying.isPresent() && aotQuery !=null && !aotQuery.isDerived()) {

if (modifying.getBoolean("flushAutomatically")) {
builder.addStatement("this.$L.flush()", context.fieldNameOf(EntityManager.class));
Expand Down
Loading
Loading