You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring4+Hibernate5+JPA+c3p0:SchemaCreator无法识别DataSource问题

问题描述

正在将现有应用升级为Spring 4 + Hibernate 5 + JPA + c3p0架构,遇到以下问题:

  • SchemaCreator类无法从c3p0的DataSource bean中获取Hibernate连接信息
  • 原代码通过注解显式传递dialect,不确定是否有必要这么做
  • 当前报错:Caused by: java.sql.SQLException: No suitable driver
  • 疑惑:已在entityManagerFactory中配置了DataSource引用,为何连接信息未被识别?同时想了解使用JPA时是否仍需使用Connection类。

相关代码

pom.xml版本配置

<spring.version>4.3.30.RELEASE</spring.version>
<c3p0.version>0.9.5.5</c3p0.version>
<hibernate.version>5.6.4.Final</hibernate.version>
<hibernate-jpa-api.version>1.0.0.Final</hibernate-jpa-api.version>
<hsqldb.version>2.7.0</hsqldb.version>

....
<dependency>
  <groupId>com.mchange</groupId>
  <artifactId>c3p0</artifactId>
  <version>${c3p0.version}</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>${hibernate.version}</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>${hibernate.version}</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-jpamodelgen</artifactId>
  <version>${hibernate.version}</version>
</dependency>
<dependency>
  <groupId>org.hibernate.javax.persistence</groupId>
  <artifactId>hibernate-jpa-2.1-api</artifactId>
  <version>${hibernate-jpa-api.version}</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-ehcache</artifactId>
  <version>${hibernate.version}</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-tools</artifactId>
  <version>${hibernate.version}</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-c3p0</artifactId>
  <version>${hibernate.version}</version>
</dependency>
...

baseContext.xml配置

<bean id="propertyConfigurer" class="org.springframework.context.support.PortalPropertySourcesPlaceholderConfigurer">
  <property name="locations">
    <list>
      <value>classpath:datasource.properties</value>
      <value>classpath:configuration.properties</value>
      <value>file:${portal.home}/global.properties</value>
      <value>file:${portal.home}/simple-cms.properties</value>
    </list>
  </property>
</bean>

<bean id="propertyResolver" factory-bean="propertyConfigurer" factory-method="getPropertyResolver"/>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
      p:driverClass="${hibernate.connection.driver_class}"
      p:jdbcUrl="${hibernate.connection.url}"
      p:user="${hibernate.connection.username}"
      p:password="${hibernate.connection.password}">

  <property name="acquireIncrement" value="1"/>
  <property name="minPoolSize" value="1"/>
  <property name="initialPoolSize" value="1"/>
  <property name="maxPoolSize" value="5"/>
  <property name="maxIdleTime" value="900"/>
  <property name="testConnectionOnCheckout" value="true"/>
  <property name="contextClassLoaderSource" value="library"/>
  <property name="privilegeSpawnedThreads" value="true"/>
  <!-- End of c3p0 properties -->
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="jpaVendorAdapter">
    <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
  </property>
  <property name="jpaProperties">
    <props>
      <prop key="hibernate.dialect">${hibernate.dialect}</prop>
    </props>
  </property>
  <property name="persistenceUnitName" value="AttachmentsDb" />
</bean>

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

<bean id="schemaCreator" class="org.jasig.portlet.attachment.util.SchemaCreator" />
...

SchemaCreator类代码

public class SchemaCreator implements ApplicationContextAware {

    private static final String APPLICATION_CONTEXT_LOCATION = "classpath:/context/baseContext.xml";

    private static final String DATA_SOURCE_BEAN_NAME = "dataSource";

    private ApplicationContext applicationContext;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    <!-- Should I pass the other hibernate connection details like this?-->
    @Value("${hibernate.dialect}")
    private String hibernateDialect;
    
    public static void main(String[] args) {

        // Bootstrap an ApplicationContext...
        final GenericApplicationContext context = new GenericApplicationContext();
        final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);

        final Resource resource = context.getResource(APPLICATION_CONTEXT_LOCATION);
        reader.loadBeanDefinitions(resource);

        context.refresh();
        context.registerShutdownHook(); // close this context on JVM shutdown unless it has already been closed.

        final SchemaCreator schemaCreator = context.getBean("schemaCreator", SchemaCreator.class);
        System.exit(schemaCreator.create());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private int create() {
           
        final DataSource dataSource = applicationContext.getBean(DATA_SOURCE_BEAN_NAME, DataSource.class);

        try (final Connection conn = dataSource.getConnection()) { 
            Map<String, String> settings = new HashMap<>();
            settings.put("generateDdl", "false");
            settings.put("hibernate.dialect",hibernateDialect);

            ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(settings).build();
            MetadataSources metadata = new MetadataSources(serviceRegistry);
            metadata.addAnnotatedClass(Attachment.class);

            EnumSet<TargetType> enumSet = EnumSet.of(TargetType.DATABASE);
            SchemaExport schemaExport = new SchemaExport();
            schemaExport.execute(enumSet, SchemaExport.Action.BOTH, metadata.buildMetadata());  

            final List<Exception> exceptions = schemaExport.getExceptions();
            if (exceptions.size() != 0) {
                logger.error("Schema Create Failed;  see below for details");
                for (Exception e : exceptions) {
                    logger.error("Exception from Hibernate Tools SchemaExport", e);
                }
                return 1;
            }
        } catch (SQLException sqle) {
            logger.error("Failed to initialize & invoke the SchemaExport tool", sqle);
            return 1;
        }

        return 0;
    }

}

问题分析与解决方案

1. 解决"No suitable driver"错误

  • 补充数据库驱动依赖:你的pom.xml中定义了hsqldb.version但未添加hsqldb依赖,导致驱动类无法被加载。添加以下依赖:
    <dependency>
        <groupId>org.hsqldb</groupId>
        <artifactId>hsqldb</artifactId>
        <version>${hsqldb.version}</version>
    </dependency>
    
  • 校验jdbcUrl格式:HSQLDB的jdbcUrl需符合规范,比如内存库是jdbc:hsqldb:mem:yourDbName,文件库是jdbc:hsqldb:file:/path/to/yourDb,确认datasource.properties中的hibernate.connection.url是否正确。
  • 类加载验证:c3p0配置了contextClassLoaderSource="library",确保驱动类能被应用类加载器正确加载,若仍有问题可尝试移除该配置。

2. SchemaCreator连接信息未被识别的原因及修复

问题出在SchemaCreator的create()方法中:你虽然获取了DataSource并拿到Connection,但未将DataSource或Connection传递给Hibernate的ServiceRegistry。Hibernate的SchemaExport默认会尝试新建连接,而它没有获取到你配置的数据源信息,因此触发驱动找不到的错误。

修复后的create()方法:

private int create() {
    final DataSource dataSource = applicationContext.getBean(DATA_SOURCE_BEAN_NAME, DataSource.class);

    try {
        Map<String, Object> settings = new HashMap<>();
        settings.put(AvailableSettings.DIALECT, hibernateDialect);
        // 将已配置好的DataSource传给Hibernate,让它使用现有连接池
        settings.put(AvailableSettings.DATASOURCE, dataSource);

        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(settings).build();
        MetadataSources metadata = new MetadataSources(serviceRegistry);
        metadata.addAnnotatedClass(Attachment.class);

        EnumSet<TargetType> enumSet = EnumSet.of(TargetType.DATABASE);
        SchemaExport schemaExport = new SchemaExport();
        schemaExport.execute(enumSet, SchemaExport.Action.BOTH, metadata.buildMetadata());  

        final List<Exception> exceptions = schemaExport.getExceptions();
        if (!exceptions.isEmpty()) {
            logger.error("Schema Create Failed;  see below for details");
            exceptions.forEach(e -> logger.error("Exception from Hibernate Tools SchemaExport", e));
            return 1;
        }
    } catch (Exception e) {
        logger.error("Failed to initialize & invoke the SchemaExport tool", e);
        return 1;
    }

    return 0;
}

关于显式传递dialect:Hibernate 5支持自动检测dialect,但显式配置更稳妥,尤其是对于HSQLDB这类多版本的数据库,能避免自动检测出错,因此保留当前的注解传递方式是合理的。

3. JPA是否需要使用Connection类

  • 日常开发无需直接使用:常规JPA操作中,应通过EntityManager进行数据库交互,JPA会自动管理连接、事务、实体映射等,无需手动操作Connection。
  • 特殊场景可使用:像SchemaCreator这类需要执行DDL生成的工具场景,JPA标准没有提供原生支持,此时直接使用Connection或Hibernate原生API是合理的。

内容的提问来源于stack exchange,提问作者loulou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 14:10:59