如何在JPA Converter中访问jdbc连接?

时间:2014-07-09 09:23:14

标签: java jpa jdbc oracle-spatial

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

我想我应该使用JPA Converter并转换为java.sql.Struct(或者oracle.sql.STRUCT)。

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

spring EntityManagerFactory配置了持久性单元名称,持久性单元包含数据源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。

更新了更多信息

Spring配置:

@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();
  }
}

2 个答案:

答案 0 :(得分:2)

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

  • 构造一个数据源bean并在那里建立连接池(或从jndi(*)获取)
  • 将该数据源注入弹簧助手中以构建EntityManagerFactory(例如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 中指定此事务管理器还支持在事务中直接访问DataSource(即使用相同DataSource的纯JDBC代码)。这允许混合访问JPA的服务和使用普通JDBC的服务(不知道JPA)!如果你得到ConnectionDataSourceUtils.getConnection(javax.sql.DataSource)

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);

答案 1 :(得分:0)

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

从这里你可以抓住Connection对象。

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

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