我有这个要求在运行时将实体类添加到持久性单元,而不是在persistence.xml中指定所有实体类。有人能帮我一样吗?
我知道Hibernate有自己的相同机制:
AnnotationConfiguration.addAnnotatedClass(Class)
等 - 您还可以通过编程方式添加hibernate config(*.hbm.xml
)文件。
要求是不重新启动应用服务器,我应该能够动态地将实体类/它们的配置(映射)文件添加到持久性单元。
但是,在运行时以编程方式将实体类/配置文件添加到持久性单元的解决方案不应该特定于JPA实现。
答案 0 :(得分:31)
JPA尚未提供此功能。您可以查看以下三个选项:
Programmatically loading Entity classes with JPA 2.0?
关于SO的这个问题与你的相似。答案报告说,春季可行。
JPA 2.0: Adding entity classes to PersistenceUnit *from different jar* automatically
这里给出了很好的指示。
最后但并非最不重要的,一个简单的解决方法:
1。使用新的持久性单元即时生成persistence.xml
(简单的XML文件创建)。
2。动态地将持久性文件添加到类路径(URLCLassLoader
)
3。要求PersistenceProvider
加载新的持久性单元(createEntityManagerFactory
)
修改强>
如果JPA提供程序是Hibernate,那么从Hibernate 4.0开始,就可以直接将实体传递给这个JPA提供程序而不在persistence.xml
文件中声明。 Hibernate将动态处理实体。
修改强>
以下是JPA 2.1 + Hibernate 4.3.7.Final的示例配置,但没有声明任何实体:
<强> META-INF / persistence.xml中强>
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="my-persistence-unit"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!-- Database Properties -->
<property name="javax.persistence.jdbc.url"
value="jdbc:postgresql://localhost:5432/my-database" />
<property name="javax.persistence.jdbc.user" value="login" />
<property name="javax.persistence.jdbc.password" value="password" />
<!-- Hibernate Properties -->
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.default_schema" value="public" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
<!-- Connection Pool -->
<property name="hibernate.c3p0.min_size" value="5" />
<property name="hibernate.c3p0.max_size" value="20" />
<property name="hibernate.c3p0.timeout" value="500" />
<property name="hibernate.c3p0.max_statements" value="50" />
<property name="hibernate.c3p0.idle_test_period" value="2000" />
</properties>
</persistence-unit>
</persistence>
<强>参考强>
答案 1 :(得分:7)
我迟到了,但我认为这会让一些人头疼。我实现了纯粹的JPA(不需要弹簧等)的类路径扫描,它与例如如果需要,也要坚持不懈。
这是您需要做的事情。
首先,更改persistence.xml并添加您自己的实现,例如:
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="my.persistence.unit" transaction-type="RESOURCE_LOCAL">
<provider>my.custom.package.HibernateDynamicPersistenceProvider</provider>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<property name="hibernate.max_fetch_depth" value="30" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
为了使提供商得到认可,您必须使其可被发现。 JPA发现使用服务加载机制,因此我们添加:
/src/main/resources/META-INF/services/javax.persistence.spi.PersistenceProvider
此文件只有一行:
my.custom.package.HibernateDynamicPersistenceProvider
最后添加您自己的提供程序并将其基于HibernateProvider(我基于它,因为我想使用hibernate):
public class HibernateDynamicPersistenceProvider extends HibernatePersistenceProvider implements PersistenceProvider {
private static final Logger log = Logger.getLogger(HibernateDynamicPersistenceProvider.class);
public static final String CUSTOM_CLASSES = "CUSTOM_CLASSES";
@Override
protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder(
PersistenceUnitDescriptor persistenceUnitDescriptor, Map integration, ClassLoader providedClassLoader) {
if(persistenceUnitDescriptor instanceof ParsedPersistenceXmlDescriptor) {
ParsedPersistenceXmlDescriptor tmp = (ParsedPersistenceXmlDescriptor) persistenceUnitDescriptor;
Object object = integration.get("CUSTOM_CLASSES");
}
return super.getEntityManagerFactoryBuilder(persistenceUnitDescriptor, integration, providedClassLoader);
}
protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties, ClassLoader providedClassLoader) {
log.debug( String.format("Attempting to obtain correct EntityManagerFactoryBuilder for persistenceUnitName : %s", persistenceUnitName ));
final Map integration = wrap( properties );
final List<ParsedPersistenceXmlDescriptor> units;
try {
units = PersistenceXmlParser.locatePersistenceUnits( integration );
}
catch (Exception e) {
log.debug( "Unable to locate persistence units", e );
throw new PersistenceException( "Unable to locate persistence units", e );
}
log.debug( String.format("Located and parsed %s persistence units; checking each", units.size() ));
if ( persistenceUnitName == null && units.size() > 1 ) {
// no persistence-unit name to look for was given and we found multiple persistence-units
throw new PersistenceException( "No name provided and multiple persistence units found" );
}
for ( ParsedPersistenceXmlDescriptor persistenceUnit : units ) {
log.debug( String.format(
"Checking persistence-unit [name=%s, explicit-provider=%s] against incoming persistence unit name [%s]",
persistenceUnit.getName(),
persistenceUnit.getProviderClassName(),
persistenceUnitName
));
final boolean matches = persistenceUnitName == null || persistenceUnit.getName().equals( persistenceUnitName );
if ( !matches ) {
log.debug( "Excluding from consideration due to name mis-match" );
continue;
}
// See if we (Hibernate) are the persistence provider
String extractRequestedProviderName = ProviderChecker.extractRequestedProviderName(persistenceUnit, integration);
if ( ! ProviderChecker.isProvider( persistenceUnit, properties ) && !(this.getClass().getName().equals(extractRequestedProviderName))) {
log.debug( "Excluding from consideration due to provider mis-match" );
continue;
}
return getEntityManagerFactoryBuilder( persistenceUnit, integration, providedClassLoader );
}
log.debug( "Found no matching persistence units" );
return null;
}
}
我必须覆盖2种方法,首先:
protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder(
PersistenceUnitDescriptor persistenceUnitDescriptor, Map integration, ClassLoader providedClassLoader)
这是截取方法。我添加了一个自定义属性&#34; CUSTOM_CLASSES&#34;应该真正被称为&#34; CUSTOM_PACKAGES&#34;这将列出所有需要扫描的包。 在这一点上,我有点懒,我会跳过实际的类路径扫描,但你可以自己做 - 它非常直接。然后你可以打电话
tmp.addClasses("class1", "class2");
哪些类是您发现的类。
我们重写的第二种方法是:
protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties, ClassLoader providedClassLoader)
这是因为我们扩展的提供程序是硬编码的,只允许hibernate类创建EMF。由于我们有一个自定义类拦截结构,我们的名字不会加起来。所以我补充道:
String extractRequestedProviderName = ProviderChecker.extractRequestedProviderName(persistenceUnit, integration);
if ( ! ProviderChecker.isProvider( persistenceUnit, properties ) && !(this.getClass().getName().equals(extractRequestedProviderName))) {
log.debug( "Excluding from consideration due to provider mis-match" );
continue;
}
这扩展了正常的休眠检查以使我的自定义提供程序有效。
Wola,我们已经完成了,你现在已经使用JPA启用了hibernate启用类路径扫描。