From fbc265b72bcb1d2df4d916505b10a0f12b7e7c06 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 28 Feb 2024 17:35:46 +0100 Subject: [PATCH] Add DataSource configuration/exposure to LocalEntityManagerFactoryBean Closes gh-32344 --- ...ocalContainerEntityManagerFactoryBean.java | 11 ++-- .../jpa/LocalEntityManagerFactoryBean.java | 66 ++++++++++++++----- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java index ed9af96f4a..23c3a8b96f 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java @@ -276,8 +276,9 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage * @see jakarta.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource() * @see #setPersistenceUnitManager */ - public void setDataSource(DataSource dataSource) { - this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(dataSource)); + public void setDataSource(@Nullable DataSource dataSource) { + this.internalPersistenceUnitManager.setDataSourceLookup( + dataSource != null ? new SingleDataSourceLookup(dataSource) : null); this.internalPersistenceUnitManager.setDefaultDataSource(dataSource); } @@ -293,8 +294,9 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage * @see jakarta.persistence.spi.PersistenceUnitInfo#getJtaDataSource() * @see #setPersistenceUnitManager */ - public void setJtaDataSource(DataSource jtaDataSource) { - this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(jtaDataSource)); + public void setJtaDataSource(@Nullable DataSource jtaDataSource) { + this.internalPersistenceUnitManager.setDataSourceLookup( + jtaDataSource != null ? new SingleDataSourceLookup(jtaDataSource) : null); this.internalPersistenceUnitManager.setDefaultJtaDataSource(jtaDataSource); } @@ -439,6 +441,7 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage } @Override + @Nullable public DataSource getDataSource() { if (this.persistenceUnitInfo != null) { return (this.persistenceUnitInfo.getJtaDataSource() != null ? diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java index 68e7400578..88b3d1128f 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalEntityManagerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,15 @@ package org.springframework.orm.jpa; +import javax.sql.DataSource; + import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; import jakarta.persistence.PersistenceException; import jakarta.persistence.spi.PersistenceProvider; +import org.springframework.lang.Nullable; + /** * {@link org.springframework.beans.factory.FactoryBean} that creates a JPA * {@link jakarta.persistence.EntityManagerFactory} according to JPA's standard @@ -28,28 +32,18 @@ import jakarta.persistence.spi.PersistenceProvider; * shared JPA EntityManagerFactory in a Spring application context; the * EntityManagerFactory can then be passed to JPA-based DAOs via * dependency injection. Note that switching to a JNDI lookup or to a - * {@link LocalContainerEntityManagerFactoryBean} - * definition is just a matter of configuration! + * {@link LocalContainerEntityManagerFactoryBean} definition based on the + * JPA container contract is just a matter of configuration! * *

Configuration settings are usually read from a {@code META-INF/persistence.xml} * config file, residing in the class path, according to the JPA standalone bootstrap - * contract. Additionally, most JPA providers will require a special VM agent - * (specified on JVM startup) that allows them to instrument application classes. - * See the Java Persistence API specification and your provider documentation - * for setup details. - * - *

This EntityManagerFactory bootstrap is appropriate for standalone applications - * which solely use JPA for data access. If you want to set up your persistence - * provider for an external DataSource and/or for global transactions which span - * multiple resources, you will need to either deploy it into a full Jakarta EE - * application server and access the deployed EntityManagerFactory via JNDI, - * or use Spring's {@link LocalContainerEntityManagerFactoryBean} with appropriate - * configuration for local setup according to JPA's container contract. + * contract. See the Java Persistence API specification and your persistence provider + * documentation for setup details. Additionally, JPA properties can also be added + * on this FactoryBean via {@link #setJpaProperties}/{@link #setJpaPropertyMap}. * *

Note: This FactoryBean has limited configuration power in terms of - * what configuration it is able to pass to the JPA provider. If you need more - * flexible configuration, for example passing a Spring-managed JDBC DataSource - * to the JPA provider, consider using Spring's more powerful + * the configuration that it is able to pass to the JPA provider. If you need + * more flexible configuration options, consider using Spring's more powerful * {@link LocalContainerEntityManagerFactoryBean} instead. * * @author Juergen Hoeller @@ -67,6 +61,42 @@ import jakarta.persistence.spi.PersistenceProvider; @SuppressWarnings("serial") public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean { + private static final String DATASOURCE_PROPERTY = "jakarta.persistence.dataSource"; + + + /** + * Specify the JDBC DataSource that the JPA persistence provider is supposed + * to use for accessing the database. This is an alternative to keeping the + * JDBC configuration in {@code persistence.xml}, passing in a Spring-managed + * DataSource through the "jakarta.persistence.dataSource" property instead. + *

When configured here, the JDBC DataSource will also get autodetected by + * {@link JpaTransactionManager} for exposing JPA transactions to JDBC accessors. + * @since 6.2 + * @see #getJpaPropertyMap() + * @see JpaTransactionManager#setDataSource + */ + public void setDataSource(@Nullable DataSource dataSource) { + if (dataSource != null) { + getJpaPropertyMap().put(DATASOURCE_PROPERTY, dataSource); + } + else { + getJpaPropertyMap().remove(DATASOURCE_PROPERTY); + } + } + + /** + * Expose the JDBC DataSource from the "jakarta.persistence.dataSource" + * property, if any. + * @since 6.2 + * @see #getJpaPropertyMap() + */ + @Override + @Nullable + public DataSource getDataSource() { + return (DataSource) getJpaPropertyMap().get(DATASOURCE_PROPERTY); + } + + /** * Initialize the EntityManagerFactory for the given configuration. * @throws jakarta.persistence.PersistenceException in case of JPA initialization errors