【问题标题】:LocalContainerEntityManagerFactoryBean with Karaf, Spring, and JPA带有 Karaf、Spring 和 JPA 的 LocalContainerEntityManagerFactoryBean
【发布时间】:2015-06-29 08:04:28
【问题描述】:

在许多情况下,在 XML 文件中声明数据源和持久性单元并不理想。为此,我尝试使用 LocalContainerEntityManagerFactoryBean 配置 JPA。但首先,一些证明这种方法在非 OSGI 环境中有效的参考资料:

  • www.baeldung.com/2011/12/22/the-persistence-layer-with-spring-data-jpa/
  • docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html

为此,我整理的 Karaf OSGI 包的源代码如下。

    public class AbstractXyzTableServiceImpl
    {
        private static EntityManagerFactory _entityManagerFactory;

        MysqlDataSource mysqlDataSource = null;


        // private static final SessionFactory configurationSessionFactory = buildConfigurationSessionFactory();

        @Bean
        public DataSource dataSource()
        {
            final MysqlDataSource dataSource = new MysqlDataSource();

            dataSource.setServerName("localhost");
            dataSource.setDatabaseName("PSH");
            dataSource.setUser("root");
            dataSource.setPassword("password");

            return dataSource;
        }


        @Bean
        public Properties hibernateProperties()
        {
            final Properties properties = new Properties();

            properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
            properties.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
            // properties.put("hibernate.connection.driver_class", "mysql-pool-xa");
            // properties.put("hibernate.hbm2ddl.auto", "create-drop");

            return properties;
        }


        public EntityManagerFactory getEntityManagerFactory()
        {
            // _entityManagerFactory = Persistence.createEntityManagerFactory("Config");

            if (_entityManagerFactory == null)
                _entityManagerFactory = getEntityManagerFactory(dataSource(), hibernateProperties());
            return _entityManagerFactory;
        }


        @Bean
        public EntityManagerFactory getEntityManagerFactory(DataSource dataSource, Properties hibernateProperties)
        {
            final LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
            //emfBean.setPersistenceUnitName("Config");
            emfBean.setDataSource(dataSource);
            emfBean.setPackagesToScan(new String[] { "info.test.configuration.data" });
            emfBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
            emfBean.setJpaProperties(hibernateProperties);
            emfBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
            //emfBean.setPersistenceXmlLocation("META-INF/persistence.xml");
            emfBean.afterPropertiesSet();

            return emfBean.getObject();
        }

    }

不幸的是,Spring/LocalContainerEntityManagerFactoryBean 似乎坚持使用相应的 persistence.xml 文件,所有定义的属性都可能被覆盖。这很好,除了在 Karaf 4.0.0 中部署时我无法获取此代码来查找 persistence.xml 文件。 Karaf 提供的堆栈跟踪如下:

    javax.persistence.PersistenceException: Unable to resolve persistence unit root URL
        at org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager.determineDefaultPersistenceUnitRootUrl(DefaultPersistenceUnitManager.java:591)[158:org.apache.servicemix.bundles.spring-orm:4.1.6.RELEASE_1]
        at org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager.preparePersistenceUnitInfos(DefaultPersistenceUnitManager.java:443)[158:org.apache.servicemix.bundles.spring-orm:4.1.6.RELEASE_1]
        at org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager.afterPropertiesSet(DefaultPersistenceUnitManager.java:424)[158:org.apache.servicemix.bundles.spring-orm:4.1.6.RELEASE_1]
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:310)[158:org.apache.servicemix.bundles.spring-orm:4.1.6.RELEASE_1]
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318)[158:org.apache.servicemix.bundles.spring-orm:4.1.6.RELEASE_1]
        at info.leonard.configuration.service.impl.AbstractConfigurationServiceImpl.getEntityManagerFactory(AbstractConfigurationServiceImpl.java:83)[195:leonard-configuration-businessLogic:0.0.1.SNAPSHOT]
        at info.leonard.configuration.service.impl.AbstractConfigurationServiceImpl.getEntityManagerFactory(AbstractConfigurationServiceImpl.java:67)[195:leonard-configuration-businessLogic:0.0.1.SNAPSHOT]
        at info.leonard.configuration.service.impl.ConfigurationDomainServiceImpl.createDomain(ConfigurationDomainServiceImpl.java:80)[195:leonard-configuration-businessLogic:0.0.1.SNAPSHOT]
        at info.leonard.configuration.command.domain.CreateDomainCommand.execute(CreateDomainCommand.java:34)[195:leonard-configuration-businessLogic:0.0.1.SNAPSHOT]
        at org.apache.karaf.shell.commands.basic.AbstractCommand.execute(AbstractCommand.java:34)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.karaf.shell.compat.CommandTracker$1.execute(CommandTracker.java:109)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:67)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:87)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:480)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:406)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:182)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:119)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:94)[43:org.apache.karaf.shell.core:4.0.0]
        at org.apache.karaf.shell.impl.console.ConsoleSessionImpl.run(ConsoleSessionImpl.java:267)[43:org.apache.karaf.shell.core:4.0.0]
        at java.lang.Thread.run(Thread.java:745)[:1.7.0_60-ea]
    Caused by: java.io.FileNotFoundException: class path resource [] cannot be resolved to URL because it does not exist
        at org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:187)[155:org.apache.servicemix.bundles.spring-core:4.1.6.RELEASE_1]
        at org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager.determineDefaultPersistenceUnitRootUrl(DefaultPersistenceUnitManager.java:588)[158:org.apache.servicemix.bundles.spring-orm:4.1.6.RELEASE_1]
        ... 20 more

在 Karaf 4.0.0 中,已加载以下弹簧功能:

@root()> feature:list | grep spring
spring                          | 4.1.6.RELEASE_1                  |          | Started     | spring-4.0.0             | Spring 4.1.x support
spring-aspects                  | 4.1.6.RELEASE_1                  |          | Uninstalled | spring-4.0.0             | Spring 4.1.x AOP support
spring-instrument               | 4.1.6.RELEASE_1                  |          | Uninstalled | spring-4.0.0             | Spring 4.1.x Instrument support
spring-jdbc                     | 4.1.6.RELEASE_1                  |          | Started     | spring-4.0.0             | Spring 4.1.x JDBC support
spring-jms                      | 4.1.6.RELEASE_1                  |          | Uninstalled | spring-4.0.0             | Spring 4.1.x JMS support
spring-test                     | 4.1.6.RELEASE_1                  |          | Uninstalled | spring-4.0.0             | Spring 4.1.x Test support
spring-orm                      | 4.1.6.RELEASE_1                  | x        | Started     | spring-4.0.0             | Spring 4.1.x ORM support
spring-oxm                      | 4.1.6.RELEASE_1                  |          | Uninstalled | spring-4.0.0             | Spring 4.1.x OXM support
spring-tx                       | 4.1.6.RELEASE_1                  |          | Started     | spring-4.0.0             | Spring 4.1.x Transaction (TX) support
spring-web                      | 4.1.6.RELEASE_1                  |          | Uninstalled | spring-4.0.0             | Spring 4.1.x Web support
spring-web-portlet              | 4.1.6.RELEASE_1                  |          | Uninstalled | spring-4.0.0             | Spring 4.1.x Web Portlet support
spring-websocket                | 4.1.6.RELEASE_1                  |          | Uninstalled | spring-4.0.0             | Spring 4.1.x WebSocket support
spring-security                 | 3.1.4.RELEASE                    |          | Uninstalled | spring-4.0.0             | Spring Security 3.1.x support

另外,我的pom.xml如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>info.leonard.configuration</groupId>
    <artifactId>leonard-configuration-businessLogic</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>bundle</packaging>
    <parent>
        <groupId>info.leonard.configuration</groupId>
        <artifactId>leonard-configuration</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../leonard-configuration</relativePath>
    </parent>

    <properties>
        <log4j-version>1.2.16</log4j-version>
        <slf4j-version>1.6.1</slf4j-version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                        <Bundle-Version>${project.version}</Bundle-Version>
                        <Import-Package>
                            org.apache.felix.service.command;version="[0.6,1)",
                            org.apache.felix.gogo.commands;version="[0.6,1)",
                            org.apache.karaf.shell.console;version="[2.2,4)",
                            com.mysql.jdbc,
                            org.hibernate,
                            org.hibernate.cfg,
                            org.hibernate.service,
                            org.hibernate.jpa,
                            org.hibernate.proxy,
                            org.hibernate.annotations,
                            org.springframework.context.annotation,
                            org.springframework.orm.jpa,
                            org.springframework.orm.jpa.vendor,
                            *,
                            javassist.util.proxy
                            info.leonard.foundation.common.*,
                            info.leonard.foundation.database.*,
                            info.leonard.foundation.environment.*,
                            info.leonard.foundation.exception.*,
                            info.leonard.foundation.finance.*,
                            info.leonard.foundation.finance.exception.*,
                            info.leonard.foundation.identity.*,
                            info.leonard.foundation.localization.*
                        </Import-Package>
                        <Export-Package>
                            info.leonard.configuration.data,
                            info.leonard.configuration.data.dao,
                            info.leonard.configuration.data.exception
                        </Export-Package>
                        <DynamicImport-Package>com.mysql.jdbc</DynamicImport-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.apache.karaf.shell</groupId>
            <artifactId>org.apache.karaf.shell.console</artifactId>
            <version>4.0.0.M2</version>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>4.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.enterprise</artifactId>
            <version>4.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <version>1.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.3.6.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-osgi</artifactId>
            <version>4.3.6.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.6.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.common</groupId>
            <artifactId>hibernate-commons-annotations</artifactId>
            <version>4.0.4.Final</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j-version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j-version}</version>
        </dependency>

        <dependency>
            <groupId>info.leonard.foundation</groupId>
            <artifactId>leonard-foundation-businessLogic</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.servicemix.bundles</groupId>
            <artifactId>org.apache.servicemix.bundles.commons-dbcp</artifactId>
            <version>1.4_3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.0-beta1</version>
        </dependency>
    </dependencies>
</project>

这似乎是某种类路径问题:

  • jira.spring.io/browse/SPR-8832

我的问题是:

  • 有其他人在 Karaf/OSGI 中采用无 XML 方法来初始化 JPA,并且可以分享他们的方法
  • 如果这是阻止 LocalContainerEntityManagerFactoryBean 在 resources/META-INF 目录中找到 persistence.xml 的类路径问题,是否有推荐的解决方案?

提前致谢, 兰迪

【问题讨论】:

  • 不要使用persistence.xml。您已将其配置为明确需要一个,不要,它应该在没有一个的情况下工作。此外,您的设置似乎相当复杂,为什么将其分配给静态字段并使用 get 方法公开它?
  • 感谢您的反馈。我已经注释掉了 setPersistenceUnitName() 和 setPersistenceXmlLocation() 的行,这是我最初的尝试。但我仍然遇到同样的问题。您是否建议进行任何其他更改?
  • 我会先清理您的配置,因为这起初看起来很复杂。你自己管理bean回调(你不应该)你的getter我真的不明白为什么,你不应该将它分配给静态实例变量。只是想知道完整的堆栈跟踪我怀疑会有更多。我也不明白为什么到底是一个配置类扩展了一个名为AbstractDao的类???
  • 再次感谢您的反馈,一些 cmets。该实体被称为配置,但它可以被称为任何东西。它只是一个数据库表,所以让我们将其重命名为 XyzTable,这样我们就不会被挂断。至于 AbstractDao 超类,我将完全删除超类调用,它没有被使用(我只是从工作的 Karaf 代码中复制代码来测试 LocalContainerEntityManagerFactoryBean)。至于回调,我在任何地方都没有看到,所以也许需要澄清一下?堆栈跟踪是 Karaf 提供的所有内容。
  • 但归根结底,问题在于第一次调用 getEntityManagerFactory(),它试图实例化 LocalContainerEntityManagerFactoryBean 的实例。不管其他上下文如何,似乎在 Karaf 之外找到的代码在 Karaf 中不起作用......因为代码坚持寻找一个 persistence.xml 文件。不确定这是否是一个 Karaf 问题:jira.spring.io/browse/SPR-8832。或者,如果有其他事情在起作用。

标签: spring hibernate jpa karaf


【解决方案1】:

对于初学者,我会尝试将您的配置更改为以下内容。

public class AbstractConfigurationServiceImpl extends AbstractDao {

    @Bean
    public DataSource dataSource()
    {
        final MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setServerName("localhost");
        dataSource.setDatabaseName("PSH");
        dataSource.setUser("root");
        dataSource.setPassword("password");
        return dataSource;
    }


    @Bean
    public FactoryBean<EntityManagerFactory> entityManagerFactory()
    {
        final LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
        emfBean.setDataSource(dataSource());
        emfBean.setPackagesToScan(new String[] { "info.test.configuration.data" });
        emfBean.setJpaVendorAdapter(hibernateJpaVendorAdapter());
        return emfBean;
    }

    @Bean
    public JpaVendorAdapter hibernateJpaVendorAdapter(){
        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
        jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect"
        return jpaVendorAdapter;
    }

}

您实际上并不需要这些属性,也不需要设置JpaDialect,因为您已经在设置JpaVendorAdapter。您还应该让 Spring 管理生命周期,而不是您自己。

我想知道的一件事是,它到底为什么要扩展 AbstractDao,在我看来,您似乎是在将 DAO 方法硬塞进配置方法中。基本上混合了关注点和责任。这也基于您的堆栈跟踪。

在您的服务中,您应该请求EntityManager,而不是获得EntityManagerFactory。如果您真的想注入它但不使用配置类作为基类。

@Service
@Transactional
public class ConfigurationDomainServiceImpl implements ConfigurationDomainService {

    @PersistenceContext
    private EntityManager em;

    public void createDomain(...) {
       Domain d = // do whatever is needed to create the domain.
       em.persist(domain);

    }

}

【讨论】:

  • 非常感谢,稍后会尝试一下。谢谢。
  • 不幸的是,这并没有很好地工作。 emfBean.getObject() 将返回 null,除非首先调用 emfBean.afterPropertiesSet()。这样做之后,我仍然得到相同的堆栈跟踪。查看 DefaultPersistenceUnitManager 中的代码。发生异常的determineDefaultPersistenceUnitRootUrl() 似乎很清楚Spring 坚持加载persistence.xml 文件...API 文档声称LocalContainerEntityManagerFactoryBean 不需要加载此类文件。也许 Karaf/OSGI 正在引入一些意想不到的转折?我将通过 Spring 代码来解决这个问题,但欢迎提供其他想法。
  • 好吧,问题在于它不是应有的@Configuration 类,并且如上所述,您应该注入依赖项而不是emfBean.getObject。你没有让 spring 控制它应该控制的 bean。正如我所说,将关注点分开。
  • 我同意...我将进一步了解 Spring 并发布我的发现。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-25
  • 2014-09-30
  • 1970-01-01
  • 1970-01-01
  • 2013-07-07
  • 2021-10-16
  • 2021-11-09
相关资源
最近更新 更多