【问题标题】:How can I access the jdbc Connection in a JPA Converter?如何访问 JPA 转换器中的 jdbc 连接?
【发布时间】:2014-08-30 06:05:30
【问题描述】:

我正在使用 Oracle Spatial,并且我有一个带有 SDO_GEOMETRY 字段的表。 该表映射到 JPA 实体。我想将 SDO_GEOMETRY 字段映射到 java oracle.spatial.geometry.JGeometry 类型。

我想我应该使用 JPA Converter 并与 java.sql.Struct(或者可能是 oracle.sql.STRUCT)相互转换。

问题是JGeometry转换为Struct的方法,JGeometry.storeJS(Connection conn, JGeometry geom),想要jdbc连接作为参数。

springEntityManagerFactory配置了持久化单元名称,持久化单元包含数据源jndi名称,数据源定义在tomcat中,作为连接池。

知道如何在转换器中获取Connection 吗?

这是我想要达到的目标:

@Converter(autoApply = true)
public class GeometryConverter implements AttributeConverter<JGeometry, Struct> {

  @Override
  public Struct convertToDatabaseColumn(JGeometry geometry) {
    // How to get this connection ?
    return JGeometry.storeJS(connection, geometry);
  }

  @Override
  public JGeometry convertToEntityAttribute(Struct struct) {
    try {
      return JGeometry.loadJS(struct);
    } catch (SQLException e) {
      throw new RuntimeException("Failed to convert geometry", e);
    }
  }
}

我正在使用 Spring 4、spring-data-jpa 1.6、Hibernate 4、Tomcat 8、Oracle 12c。

更新了更多信息

弹簧配置:

@Configuration
@EnableJpaRepositories("com.package.repository")
@EnableTransactionManagement
@ComponentScan("com.package")
public class SpringConfig {

  @Bean(name = "entityManagerFactory", destroyMethod = "destroy")
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setPersistenceUnitName("persistence-unit");
    return emf;
  }

  @Bean(name = "transactionManager")
  public JpaTransactionManager getTransactionManager() {
    return new JpaTransactionManager();
  }
}

【问题讨论】:

标签: java jpa jdbc oracle-spatial


【解决方案1】:

如果你使用spring,并且需要同时使用JPA和JDBC,你应该:

  • 构造一个数据源 bean 并在那里建立连接池(或从 jndi(*) 获取)
  • 将该数据源注入一个用于构建EntityManagerFactory 的spring 助手(例如LocalContainerEntityManagerFactoryBean
  • 将该数据源注入到您想要直接执行 JDBC 的任何 bean 中

这样您就可以将 JPA 用于您的普通 DAO,并且仍然可以在特殊部分访问 JDBC - 而不会过于依赖 JPA 提供者的内部结构。

编辑:

(*) 如果您的数据源是由 jndi 名称定义的,那么一切都很好。将其公开为 bean (ref)

如果使用 Spring 的基于 XML 模式的配置,在 Spring 上下文中设置如下:

<xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/jee 
                    http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">
...
<jee:jndi-lookup id="dbDataSource"
   jndi-name="jdbc/DatabaseName"
   expected-type="javax.sql.DataSource" />

或者,使用简单的 bean 配置进行设置,如下所示:

<bean id="dbDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/DatabaseName"/>
</bean>

当您使用 JpaTransactionManager 时,不会有任何问题,因为正如 spring javadoc 中所指定的那样 此事务管理器还支持事务中的直接数据源访问(即使用相同数据源的普通 JDBC 代码)。这允许混合使用 JPA 的服务和使用普通 JDBC 的服务(不知道 JPA)!前提是您通过 DataSourceUtils.getConnection(javax.sql.DataSource) 获得您的 Connection

EDIT2:

好的,现在唯一的问题是如何从非 bean 对象访问单例 bean。解决它的一个简单方法是使用静态方法创建一个 holder 单例 bean。

@Bean
public class DataSourceHolder implements InitializingBean {
    private DataSource dataSource;

    private static DataSourceHolder instance;

    public static DataSource getDataSource() {
        return instance.dataSource;
    }

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        DataSourceHolder.instance = this;
    }
}

那么在任何对象中,不管是不是bean,你都可以使用

DataSource ds = DataSourceHolder.getDataSource();
Connection con = DataSourceUtils.getConnection(ds);

【讨论】:

  • 我是这样的:spring的EntityManagerFactory配置了持久化单元名称,持久化单元包含数据源jndi名称,数据源定义在tomcat中,作为连接池。我想我可以在这个链的某个地方找到数据源。问题是,JPA 转换器不是 spring bean。我认为实例化它的不是 spring,而是 JPA 提供者,意思是休眠,所以没有机会在其中注入任何东西。
  • 好吧,我认为这没有问题:因为你的数据源是一个带有 jndi 名称的 tomcat 池,所以将它公开为一个 bean 并在你需要的地方注入它(参见我的编辑)。
  • 所以我将 DataSource 作为一个 bean。好的。如何在 Converter 中找到它? Converter 不是 bean,所以 @Autowired 不起作用。
【解决方案2】:

那会很棘手。纯粹来自 jpa api。您将不得不深入研究特定的提供程序实现并获取DataSource 对象或PersistenceUnitInfo 对象。

您可以从这里获取Connection 对象。

现在取决于您工作的环境。如果您在 JavaEE 环境中,并且注入 EntityManager 或其 EntityManagerFactory,则无法保证返回实例是提供者自己实现的实例,因为这可能只是实现接口的代理,因此与提供者自己的实现。

在 JSE 环境中,由于您是从 Persistence.createEntityManagerFactory() 创建 EntityManagerFactory 的人,因此您可以调整提供程序以获取连接。

【讨论】:

  • 我使用的是spring,理论上我可以注入EntityManager。但是有两个问题:转换器不是 Spring bean,因此没有任何东西被注入其中,即使注入了,我也不确定我会得到的 EntityManager 是否具有相同的连接/事务。
猜你喜欢
  • 2021-01-14
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
  • 2021-06-28
  • 2016-02-12
  • 2021-03-22
  • 2016-07-06
  • 1970-01-01
相关资源
最近更新 更多