java 动态设置 JPA 持久性属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11695291/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Setting JPA persistence properties dynamically
提问by Blessed Geek
Let's say I have the following persistence.xml with the connection url, user & password all hard-coded.
假设我有以下persistence.xml,其中的连接url、用户和密码都是硬编码的。
The following is for Hibernate 3.2. For Hibernate 3.5 ++, we have to change "hibernate.connection" to "javax.persistence". But let me ask this question regardless of the literals "hibernate.connection" or "javax.persistence".
以下是针对 Hibernate 3.2 的。对于 Hibernate 3.5 ++,我们必须将“hibernate.connection”更改为“javax.persistence”。但是,不管文字“hibernate.connection”还是“javax.persistence”,让我问这个问题。
<persistence-unit name="obamacare" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.archive.autodetection" value="class, hbm"/>
<property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver"/>
<property name="hibernate.connection.url" value="blah blah blah"/>
<property name="hibernate.connection.username" value="careuser"/>
<property name="hibernate.connection.password" value="carepass"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
However, we need to set the url, user & password dynamically. There is a proposed authentication server which serves the url, user & password. So that we do not need to individually configure the myriads of webapps that use some form of jdbc, hibernate or JPA. Besides the security issue of not wanting to store/manage passwords on visible text files.
但是,我们需要动态设置 url、用户和密码。有一个提议的身份验证服务器,它提供 url、用户和密码。这样我们就不需要单独配置无数使用某种形式的 jdbc、hibernate 或 JPA 的 web 应用程序。除了不想在可见文本文件上存储/管理密码的安全问题。
As far as JPA is concerned, how can I set these JPA properties dynamically? I am seeking two sets of answers:
就 JPA 而言,如何动态设置这些 JPA 属性?我正在寻求两组答案:
For a solution that is JPA vendor independent (toplink, eclipselink, hibernate, etc) - Is there any JPA functionality that would let me set these three properties dynamically?
If I am allowed to depend totally on Hibernate, besides the possible JPA only avenue, is there a way to achieving this without involving Spring framework (which seems like a huge monstrosity with tentacles all over the place)?
对于独立于 JPA 供应商的解决方案(toplink、eclipselink、hibernate 等) - 是否有任何 JPA 功能可以让我动态设置这三个属性?
如果允许我完全依赖 Hibernate,除了可能的 JPA 唯一途径之外,有没有办法在不涉及 Spring 框架的情况下实现这一目标(这似乎是一个到处都是触手的巨大怪物)?
I would be elated if you also wish to throw in two cents/quids/rupees on JNDI and how I could use it to replace the functionality of persistence.xml properties. However, that is not the priority of the question.
如果您还希望在 JNDI 上投入 2 美分/英镑/卢比,以及我如何使用它来替换 persistence.xml 属性的功能,我会很高兴。然而,这不是问题的优先级。
采纳答案by Steve Ebersole
It depends how you bootstrap your EntityManagerFactory. The 2 spec defined methods each allow you to pass in a java.util.Map of values. These values are supposed to take precedence over values defined in persistence-unit.
这取决于您如何引导 EntityManagerFactory。2 个规范定义的方法都允许您传入一个 java.util.Map 值。这些值应该优先于在persistence-unit 中定义的值。
In the "SE approach" no problem since the bootstrap process is typically controlled by your app: javax.persistence.Persistence#createEntityManagerFactory(String puName, Map config
. Now you might have problems here if something else (ahem, Spring) is "managing" the EMF for you...
在“SE 方法”中没有问题,因为引导过程通常由您的应用程序控制:javax.persistence.Persistence#createEntityManagerFactory(String puName, Map config
. 现在,如果其他东西(咳咳,Spring)正在为您“管理”EMF,您可能会在这里遇到问题......
In the "EE approach" I am not aware of a good global approach. This Map of values still exists in the bootstrapping, but the problem is that the EE container is the one calling this method.
在“EE 方法”中,我不知道一个好的全局方法。这个值映射仍然存在于引导程序中,但问题是 EE 容器是调用此方法的容器。
One Hibernate-specific approach that would work in either case would be to use config variable replacement. So in your persistence-unit you'd define user name or password using ${some.key}
and Hibernate would replace those for you. Whether this would work through really depends how you want to set these values ultimately; Hibernate still needs access to a config value named some.key
for this to work...
一种适用于任一情况的 Hibernate 特定方法是使用配置变量替换。因此,在您的持久性单元中,您可以使用定义用户名或密码${some.key}
,Hibernate 会为您替换它们。这是否会奏效取决于您最终希望如何设置这些值;Hibernate 仍然需要访问为此命名的配置值some.key
才能工作......
Yet another "global approach"... The "EE approach" to bootstrap the EMF is for the container to instantiate the javax.persistence.spi.PersistenceProvider and call its javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory. createContainerEntityManagerFactory has an interesting signature here. Essentially it is passed a javax.persistence.spi.PersistenceUnitInfo which is an object representation of the parsed persistence unit plus some other stuff. An option would be to use this approach to bootstrap and pass in an instance of javax.persistence.spi.PersistenceUnitInfo that you construct your self. javax.persistence.spi.PersistenceProvider is an interface. To instantiate it, you'd need to know the provider you want to use and the FQN to their impl. But thats usually not a problem since these are pretty well known.
另一个“全局方法”……引导 EMF 的“EE 方法”是让容器实例化 javax.persistence.spi.PersistenceProvider 并调用它的 javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory。createContainerEntityManagerFactory 在这里有一个有趣的签名。本质上,它传递了一个 javax.persistence.spi.PersistenceUnitInfo,它是解析的持久性单元的对象表示以及其他一些东西。一种选择是使用这种方法来引导并传入您构造自己的 javax.persistence.spi.PersistenceUnitInfo 的实例。javax.persistence.spi.PersistenceProvider 是一个接口。要实例化它,您需要知道要使用的提供程序以及它们的实现的 FQN。
You are specifically asking about JDBC connection creation/pooling. You have additional options there specifically. You could have your "credential service" create DataSources and your JPA provider simply use that DataSource. All JPA providers support locating DataSources via JNDI lookup. In "EE bootstrapping" providers can also be passed a DataSource to use via PersistenceUnitInfo#getJtaDataSource and/or PersistenceUnitInfo#getNonJtaDataSource. Hibernate alternately accepts a DataSource instance in place of the typical DataSource JNDI name setting. If you don't want to use DataSource (for some odd reason), a Hibernate-specific alternative is to implement Hibernate's ConnectionProvider contract yourself, this is the contract (interface) Hibernate uses to obtain and release JDBC connections when it needs to. Implementing ConnectionProvider you could configure the underlying Connections in any way you wanted to.
您特别询问 JDBC 连接创建/池。您在那里有额外的选择。您可以让您的“凭证服务”创建数据源,而您的 JPA 提供程序只需使用该数据源。所有 JPA 提供程序都支持通过 JNDI 查找来定位数据源。在“EE bootstrapping”中,提供者也可以通过 PersistenceUnitInfo#getJtaDataSource 和/或 PersistenceUnitInfo#getNonJtaDataSource 传递一个数据源来使用。Hibernate 交替地接受一个 DataSource 实例来代替典型的 DataSource JNDI 名称设置。如果您不想使用 DataSource(出于某种奇怪的原因),则 Hibernate 特定的替代方案是自己实现 Hibernate 的 ConnectionProvider 合同,这是 Hibernate 在需要时用于获取和释放 JDBC 连接的合同(接口)。
Lots of options :)
很多选择:)
回答by chris.tian
For your second question I can provide a Hibernate-only solution.
对于您的第二个问题,我可以提供仅适用于 Hibernate 的解决方案。
package dev.stackoverflow;
import java.util.Properties;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class DynamicHibernateSessionFactory {
public Session setProperties(final String provider,
final Boolean excludeUnlisted,
final Properties properties) {
properties.setProperty("provider", provider);
properties.setProperty("exclude-unlisted-classes", excludeUnlisted.toString());
Configuration configuration = new Configuration();
configuration.setProperties(properties);
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();
return sessionFactory.openSession();
}
}